Merge remote-tracking branch 'origin/develop' into shadow-control-2.0
This commit is contained in:
commit
6fc929a0a0
26 changed files with 913 additions and 150 deletions
|
|
@ -27,7 +27,9 @@ export default {
|
|||
component: 'Alert'
|
||||
},
|
||||
component: 'Border',
|
||||
textColor: '--parent'
|
||||
directives: {
|
||||
textColor: '--parent'
|
||||
}
|
||||
},
|
||||
{
|
||||
variant: 'error',
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ export default {
|
|||
directives: {
|
||||
'--defaultButtonHoverGlow': 'shadow | 0 0 4 --text',
|
||||
'--defaultButtonShadow': 'shadow | 0 0 2 #000000',
|
||||
'--defaultButtonBevel': 'shadow | $borderSide(#FFFFFF, top, 0.2) | $borderSide(#000000, bottom, 0.2)',
|
||||
'--pressedButtonBevel': 'shadow | $borderSide(#FFFFFF, bottom, 0.2)| $borderSide(#000000, top, 0.2)'
|
||||
'--defaultButtonBevel': 'shadow | $borderSide(#FFFFFF, top, 0.2), $borderSide(#000000, bottom, 0.2)',
|
||||
'--pressedButtonBevel': 'shadow | $borderSide(#FFFFFF, bottom, 0.2), $borderSide(#000000, top, 0.2)'
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default {
|
|||
{
|
||||
component: 'Root',
|
||||
directives: {
|
||||
'--defaultInputBevel': 'shadow | $borderSide(#FFFFFF, bottom, 0.2)| $borderSide(#000000, top, 0.2)'
|
||||
'--defaultInputBevel': 'shadow | $borderSide(#FFFFFF, bottom, 0.2), $borderSide(#000000, top, 0.2)'
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -76,6 +76,13 @@
|
|||
>
|
||||
{{ $t('polls.vote') }}
|
||||
</button>
|
||||
<span
|
||||
v-if="poll.pleroma?.non_anonymous"
|
||||
:title="$t('polls.non_anonymous_title')"
|
||||
>
|
||||
{{ $t('polls.non_anonymous') }}
|
||||
·
|
||||
</span>
|
||||
<div class="total">
|
||||
<template v-if="typeof poll.voters_count === 'number'">
|
||||
{{ $tc("polls.people_voted_count", poll.voters_count, { count: poll.voters_count }) }}
|
||||
|
|
|
|||
|
|
@ -217,6 +217,29 @@
|
|||
{{ $t('settings.no_rich_text_description') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="useAbsoluteTimeFormat"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.absolute_time_format') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<ul
|
||||
class="setting-list suboptions"
|
||||
v-if="mergedConfig.useAbsoluteTimeFormat"
|
||||
>
|
||||
<li>
|
||||
<UnitSetting
|
||||
path="absoluteTimeFormatMinAge"
|
||||
unit-set="time"
|
||||
:units="['s', 'm', 'h', 'd']"
|
||||
:min="0"
|
||||
>
|
||||
{{ $t('settings.absolute_time_format_min_age') }}
|
||||
</UnitSetting>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>{{ $t('settings.attachments') }}</h3>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
:datetime="time"
|
||||
:title="localeDateString"
|
||||
>
|
||||
{{ relativeTimeString }}
|
||||
{{ relativeOrAbsoluteTimeString }}
|
||||
</time>
|
||||
</template>
|
||||
|
||||
|
|
@ -16,16 +16,28 @@ export default {
|
|||
props: ['time', 'autoUpdate', 'longFormat', 'nowThreshold', 'templateKey'],
|
||||
data () {
|
||||
return {
|
||||
relativeTimeMs: 0,
|
||||
relativeTime: { key: 'time.now', num: 0 },
|
||||
interval: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
localeDateString () {
|
||||
const browserLocale = localeService.internalToBrowserLocale(this.$i18n.locale)
|
||||
shouldUseAbsoluteTimeFormat () {
|
||||
if (!this.$store.getters.mergedConfig.useAbsoluteTimeFormat) {
|
||||
return false
|
||||
}
|
||||
return DateUtils.durationStrToMs(this.$store.getters.mergedConfig.absoluteTimeFormatMinAge) <= this.relativeTimeMs
|
||||
},
|
||||
browserLocale () {
|
||||
return localeService.internalToBrowserLocale(this.$i18n.locale)
|
||||
},
|
||||
timeAsDate () {
|
||||
return typeof this.time === 'string'
|
||||
? new Date(Date.parse(this.time)).toLocaleString(browserLocale)
|
||||
: this.time.toLocaleString(browserLocale)
|
||||
? new Date(Date.parse(this.time))
|
||||
: this.time
|
||||
},
|
||||
localeDateString () {
|
||||
return this.timeAsDate.toLocaleString(this.browserLocale)
|
||||
},
|
||||
relativeTimeString () {
|
||||
const timeString = this.$i18n.tc(this.relativeTime.key, this.relativeTime.num, [this.relativeTime.num])
|
||||
|
|
@ -35,6 +47,40 @@ export default {
|
|||
}
|
||||
|
||||
return timeString
|
||||
},
|
||||
absoluteTimeString () {
|
||||
if (this.longFormat) {
|
||||
return this.localeDateString
|
||||
}
|
||||
const now = new Date()
|
||||
const formatter = (() => {
|
||||
if (DateUtils.isSameDay(this.timeAsDate, now)) {
|
||||
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||
minute: 'numeric',
|
||||
hour: 'numeric'
|
||||
})
|
||||
} else if (DateUtils.isSameMonth(this.timeAsDate, now)) {
|
||||
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||
hour: 'numeric',
|
||||
day: 'numeric'
|
||||
})
|
||||
} else if (DateUtils.isSameYear(this.timeAsDate, now)) {
|
||||
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
})
|
||||
} else {
|
||||
return new Intl.DateTimeFormat(this.browserLocale, {
|
||||
year: 'numeric',
|
||||
month: 'short'
|
||||
})
|
||||
}
|
||||
})()
|
||||
|
||||
return formatter.format(this.timeAsDate)
|
||||
},
|
||||
relativeOrAbsoluteTimeString () {
|
||||
return this.shouldUseAbsoluteTimeFormat ? this.absoluteTimeString : this.relativeTimeString
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
@ -54,6 +100,7 @@ export default {
|
|||
methods: {
|
||||
refreshRelativeTimeObject () {
|
||||
const nowThreshold = typeof this.nowThreshold === 'number' ? this.nowThreshold : 1
|
||||
this.relativeTimeMs = DateUtils.relativeTimeMs(this.time)
|
||||
this.relativeTime = this.longFormat
|
||||
? DateUtils.relativeTime(this.time, nowThreshold)
|
||||
: DateUtils.relativeTimeShort(this.time, nowThreshold)
|
||||
|
|
|
|||
|
|
@ -229,7 +229,9 @@
|
|||
"expiry": "Poll age",
|
||||
"expires_in": "Poll ends in {0}",
|
||||
"expired": "Poll ended {0} ago",
|
||||
"not_enough_options": "Too few unique options in poll"
|
||||
"not_enough_options": "Too few unique options in poll",
|
||||
"non_anonymous": "Public poll",
|
||||
"non_anonymous_title": "Other instances may display the options you voted for"
|
||||
},
|
||||
"emoji": {
|
||||
"stickers": "Stickers",
|
||||
|
|
@ -506,6 +508,8 @@
|
|||
"autocomplete_select_first": "Automatically select the first candidate when autocomplete results are available",
|
||||
"emoji_reactions_on_timeline": "Show emoji reactions on timeline",
|
||||
"emoji_reactions_scale": "Reactions scale factor",
|
||||
"absolute_time_format": "Use absolute time format",
|
||||
"absolute_time_format_min_age": "Only use for time older than this amount of time",
|
||||
"export_theme": "Save preset",
|
||||
"filtering": "Filtering",
|
||||
"wordfilter": "Wordfilter",
|
||||
|
|
|
|||
|
|
@ -190,7 +190,8 @@
|
|||
"mobile_notifications_close": "關掉通知",
|
||||
"announcements": "公告",
|
||||
"search": "Tshuē",
|
||||
"mobile_notifications_mark_as_seen": "Lóng 標做有讀"
|
||||
"mobile_notifications_mark_as_seen": "Lóng 標做有讀",
|
||||
"quotes": "引用"
|
||||
},
|
||||
"notifications": {
|
||||
"broken_favorite": "狀態毋知影,leh tshiau-tshuē……",
|
||||
|
|
@ -212,7 +213,8 @@
|
|||
"unread_follow_requests": "{num}ê新ê跟tuè請求",
|
||||
"configuration_tip": "用{theSettings},lí通自訂siánn物佇tsia顯示。{dismiss}",
|
||||
"configuration_tip_settings": "設定",
|
||||
"configuration_tip_dismiss": "Mài koh顯示"
|
||||
"configuration_tip_dismiss": "Mài koh顯示",
|
||||
"subscribed_status": "有發送ê"
|
||||
},
|
||||
"polls": {
|
||||
"add_poll": "開投票",
|
||||
|
|
@ -252,7 +254,8 @@
|
|||
},
|
||||
"load_all_hint": "載入頭前 {saneAmount} ê 繪文字,規个攏載入效能可能 ē khah 食力。",
|
||||
"load_all": "Kā {emojiAmount} ê 繪文字攏載入",
|
||||
"regional_indicator": "地區指引 {letter}"
|
||||
"regional_indicator": "地區指引 {letter}",
|
||||
"hide_custom_emoji": "Khàm掉自訂ê繪文字"
|
||||
},
|
||||
"errors": {
|
||||
"storage_unavailable": "Pleroma buē-tàng the̍h 著瀏覽器儲存 ê。Lí ê 登入狀態抑是局部設定 buē 儲存,mā 凡勢 tú 著意料外 ê 問題。拍開 cookie 看māi。"
|
||||
|
|
@ -263,7 +266,8 @@
|
|||
"emoji_reactions": "繪文字 ê 反應",
|
||||
"reports": "檢舉",
|
||||
"moves": "用者 ê 移民",
|
||||
"load_older": "載入 koh khah 早 ê 互動"
|
||||
"load_older": "載入 koh khah 早 ê 互動",
|
||||
"statuses": "訂ê"
|
||||
},
|
||||
"post_status": {
|
||||
"edit_status": "編輯狀態",
|
||||
|
|
@ -935,7 +939,34 @@
|
|||
"notification_extra_chats": "顯示bô讀ê開講",
|
||||
"notification_extra_announcements": "顯示bô讀ê公告",
|
||||
"notification_extra_follow_requests": "顯示新ê跟tuè請求",
|
||||
"notification_extra_tip": "顯示自訂其他通知ê撇步"
|
||||
"notification_extra_tip": "顯示自訂其他通知ê撇步",
|
||||
"confirm_new_setting": "Lí敢確認新ê設定?",
|
||||
"text_size_tip": "用 {0} 做絕對值,{1} ē根據瀏覽器ê標準文字sài-suh放大縮小。",
|
||||
"theme_debug": "佇處理透明ê時,顯示背景主題ia̋n-jín 所假使ê(DEBUG)",
|
||||
"units": {
|
||||
"time": {
|
||||
"s": "秒鐘",
|
||||
"m": "分鐘",
|
||||
"h": "點鐘",
|
||||
"d": "工"
|
||||
}
|
||||
},
|
||||
"actor_type": "Tsit ê口座是:",
|
||||
"actor_type_Person": "一般ê用者",
|
||||
"actor_type_description": "標記lí ê口座做群組,ē hōo自動轉送提起伊ê狀態。",
|
||||
"actor_type_Group": "群組",
|
||||
"actor_type_Service": "機器lâng",
|
||||
"appearance": "外觀",
|
||||
"confirm_new_question": "Tse看起來kám好?設定ē佇10秒鐘後改轉去。",
|
||||
"revert": "改轉去",
|
||||
"confirm": "確認",
|
||||
"text_size": "文字kap界面ê sài-suh",
|
||||
"text_size_tip2": "毋是 {0} ê值可能ē破壞一寡物件kap主題",
|
||||
"emoji_size": "繪文字ê sài-suh",
|
||||
"navbar_size": "頂 liâu-á êsài-suh",
|
||||
"panel_header_size": "面pang標題ê sài-suh",
|
||||
"visual_tweaks": "細細ê外觀調整",
|
||||
"scale_and_layout": "界面ê sài-suh kap排列"
|
||||
},
|
||||
"status": {
|
||||
"favorites": "收藏",
|
||||
|
|
@ -1001,7 +1032,7 @@
|
|||
"show_only_conversation_under_this": "Kan-ta顯示tsit ê狀態ê回應",
|
||||
"status_history": "狀態ê歷史",
|
||||
"reaction_count_label": "{num}ê lâng用表情反應",
|
||||
"hide_quote": "Khàm條引用ê狀態",
|
||||
"hide_quote": "Khàm掉引用ê狀態",
|
||||
"display_quote": "顯示引用ê狀態",
|
||||
"invisible_quote": "引用ê狀態bē當用:{link}",
|
||||
"more_actions": "佇tsit ê狀態ê其他動作"
|
||||
|
|
|
|||
|
|
@ -180,7 +180,9 @@ export const defaultState = {
|
|||
autocompleteSelect: undefined, // instance default
|
||||
closingDrawerMarksAsSeen: undefined, // instance default
|
||||
unseenAtTop: undefined, // instance default
|
||||
ignoreInactionableSeen: undefined // instance default
|
||||
ignoreInactionableSeen: undefined, // instance default
|
||||
useAbsoluteTimeFormat: undefined, // instance defualt
|
||||
absoluteTimeFormatMinAge: undefined // instance default
|
||||
}
|
||||
|
||||
// caching the instance default properties
|
||||
|
|
|
|||
|
|
@ -119,6 +119,8 @@ const defaultState = {
|
|||
closingDrawerMarksAsSeen: true,
|
||||
unseenAtTop: false,
|
||||
ignoreInactionableSeen: false,
|
||||
useAbsoluteTimeFormat: false,
|
||||
absoluteTimeFormatMinAge: '0d',
|
||||
|
||||
// Nasty stuff
|
||||
customEmoji: [],
|
||||
|
|
|
|||
|
|
@ -6,10 +6,13 @@ export const WEEK = 7 * DAY
|
|||
export const MONTH = 30 * DAY
|
||||
export const YEAR = 365.25 * DAY
|
||||
|
||||
export const relativeTime = (date, nowThreshold = 1) => {
|
||||
export const relativeTimeMs = (date) => {
|
||||
if (typeof date === 'string') date = Date.parse(date)
|
||||
return Math.abs(Date.now() - date)
|
||||
}
|
||||
export const relativeTime = (date, nowThreshold = 1) => {
|
||||
const round = Date.now() > date ? Math.floor : Math.ceil
|
||||
const d = Math.abs(Date.now() - date)
|
||||
const d = relativeTimeMs(date)
|
||||
const r = { num: round(d / YEAR), key: 'time.unit.years' }
|
||||
if (d < nowThreshold * SECOND) {
|
||||
r.num = 0
|
||||
|
|
@ -57,3 +60,39 @@ export const secondsToUnit = (unit, amount) => {
|
|||
case 'days': return (1000 * amount) / DAY
|
||||
}
|
||||
}
|
||||
|
||||
export const isSameYear = (a, b) => {
|
||||
return a.getFullYear() === b.getFullYear()
|
||||
}
|
||||
|
||||
export const isSameMonth = (a, b) => {
|
||||
return a.getFullYear() === b.getFullYear() &&
|
||||
a.getMonth() === b.getMonth()
|
||||
}
|
||||
|
||||
export const isSameDay = (a, b) => {
|
||||
return a.getFullYear() === b.getFullYear() &&
|
||||
a.getMonth() === b.getMonth() &&
|
||||
a.getDate() === b.getDate()
|
||||
}
|
||||
|
||||
export const durationStrToMs = (str) => {
|
||||
if (typeof str !== 'string') {
|
||||
return 0
|
||||
}
|
||||
|
||||
const unit = str.replace(/[0-9,.]+/, '')
|
||||
const value = str.replace(/[^0-9,.]+/, '')
|
||||
switch (unit) {
|
||||
case 'd':
|
||||
return value * DAY
|
||||
case 'h':
|
||||
return value * HOUR
|
||||
case 'm':
|
||||
return value * MINUTE
|
||||
case 's':
|
||||
return value * SECOND
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const internalToBackendLocaleMulti = codes => {
|
|||
|
||||
const getLanguageName = (code) => {
|
||||
const specialLanguageNames = {
|
||||
pdc: 'Pennsylvania Dutch',
|
||||
pdc: 'Pennsilfaanisch-Deitsch',
|
||||
ja_easy: 'やさしいにほんご',
|
||||
'nan-TW': '臺語(閩南語)',
|
||||
zh: '简体中文',
|
||||
|
|
|
|||
157
src/services/theme_data/iss_deserializer.js
Normal file
157
src/services/theme_data/iss_deserializer.js
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
import { flattenDeep } from 'lodash'
|
||||
|
||||
const parseShadow = string => {
|
||||
const modes = ['_full', 'inset', 'x', 'y', 'blur', 'spread', 'color', 'alpha']
|
||||
const regexPrep = [
|
||||
// inset keyword (optional)
|
||||
'^(?:(inset)\\s+)?',
|
||||
// x
|
||||
'(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)',
|
||||
// y
|
||||
'(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)',
|
||||
// blur (optional)
|
||||
'(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)?',
|
||||
// spread (optional)
|
||||
'(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)?',
|
||||
// either hex, variable or function
|
||||
'(#[0-9a-f]{6}|--[a-z\\-_]+|\\$[a-z\\-()_]+)',
|
||||
// opacity (optional)
|
||||
'(?:\\s+\\/\\s+([0-9]+(?:\\.[0-9]+)?)\\s*)?$'
|
||||
].join('')
|
||||
const regex = new RegExp(regexPrep, 'gis') // global, (stable) indices, single-string
|
||||
const result = regex.exec(string)
|
||||
if (result == null) {
|
||||
return string
|
||||
} else {
|
||||
const numeric = new Set(['x', 'y', 'blur', 'spread', 'alpha'])
|
||||
const { x, y, blur, spread, alpha, inset, color } = Object.fromEntries(modes.map((mode, i) => {
|
||||
if (numeric.has(mode)) {
|
||||
return [mode, Number(result[i])]
|
||||
} else if (mode === 'inset') {
|
||||
return [mode, !!result[i]]
|
||||
} else {
|
||||
return [mode, result[i]]
|
||||
}
|
||||
}).filter(([k, v]) => v !== false).slice(1))
|
||||
|
||||
return { x, y, blur, spread, color, alpha, inset }
|
||||
}
|
||||
}
|
||||
// this works nearly the same as HTML tree converter
|
||||
const parseIss = (input) => {
|
||||
const buffer = [{ selector: null, content: [] }]
|
||||
let textBuffer = ''
|
||||
|
||||
const getCurrentBuffer = () => {
|
||||
let current = buffer[buffer.length - 1]
|
||||
if (current == null) {
|
||||
current = { selector: null, content: [] }
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
// Processes current line buffer, adds it to output buffer and clears line buffer
|
||||
const flushText = (kind) => {
|
||||
if (textBuffer === '') return
|
||||
if (kind === 'content') {
|
||||
getCurrentBuffer().content.push(textBuffer.trim())
|
||||
} else {
|
||||
getCurrentBuffer().selector = textBuffer.trim()
|
||||
}
|
||||
textBuffer = ''
|
||||
}
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
const char = input[i]
|
||||
|
||||
if (char === ';') {
|
||||
flushText('content')
|
||||
} else if (char === '{') {
|
||||
flushText('header')
|
||||
} else if (char === '}') {
|
||||
flushText('content')
|
||||
buffer.push({ selector: null, content: [] })
|
||||
textBuffer = ''
|
||||
} else {
|
||||
textBuffer += char
|
||||
}
|
||||
}
|
||||
|
||||
return buffer
|
||||
}
|
||||
export const deserialize = (input) => {
|
||||
const ast = parseIss(input)
|
||||
const finalResult = ast.filter(i => i.selector != null).map(item => {
|
||||
const { selector, content } = item
|
||||
let stateCount = 0
|
||||
const selectors = selector.split(/,/g)
|
||||
const result = selectors.map(selector => {
|
||||
const output = { component: '' }
|
||||
let currentDepth = null
|
||||
|
||||
selector.split(/ /g).reverse().forEach((fragment, index, arr) => {
|
||||
const fragmentObject = { component: '' }
|
||||
|
||||
let mode = 'component'
|
||||
for (let i = 0; i < fragment.length; i++) {
|
||||
const char = fragment[i]
|
||||
switch (char) {
|
||||
case '.': {
|
||||
mode = 'variant'
|
||||
fragmentObject.variant = ''
|
||||
break
|
||||
}
|
||||
case ':': {
|
||||
mode = 'state'
|
||||
fragmentObject.state = fragmentObject.state || []
|
||||
stateCount++
|
||||
break
|
||||
}
|
||||
default: {
|
||||
if (mode === 'state') {
|
||||
const currentState = fragmentObject.state[stateCount - 1]
|
||||
if (currentState == null) {
|
||||
fragmentObject.state.push('')
|
||||
}
|
||||
fragmentObject.state[stateCount - 1] += char
|
||||
} else {
|
||||
fragmentObject[mode] += char
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentDepth !== null) {
|
||||
currentDepth.parent = { ...fragmentObject }
|
||||
currentDepth = currentDepth.parent
|
||||
} else {
|
||||
Object.keys(fragmentObject).forEach(key => {
|
||||
output[key] = fragmentObject[key]
|
||||
})
|
||||
if (index !== (arr.length - 1)) {
|
||||
output.parent = { component: '' }
|
||||
}
|
||||
currentDepth = output
|
||||
}
|
||||
})
|
||||
|
||||
output.directives = Object.fromEntries(content.map(d => {
|
||||
const [property, value] = d.split(':')
|
||||
let realValue = value.trim()
|
||||
if (property === 'shadow') {
|
||||
if (realValue === 'none') {
|
||||
realValue = []
|
||||
} else {
|
||||
realValue = value.split(',').map(v => parseShadow(v.trim()))
|
||||
}
|
||||
} if (!Number.isNaN(Number(value))) {
|
||||
realValue = Number(value)
|
||||
}
|
||||
return [property, realValue]
|
||||
}))
|
||||
|
||||
return output
|
||||
})
|
||||
return result
|
||||
})
|
||||
return flattenDeep(finalResult)
|
||||
}
|
||||
48
src/services/theme_data/iss_serializer.js
Normal file
48
src/services/theme_data/iss_serializer.js
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import { unroll } from './iss_utils.js'
|
||||
|
||||
const serializeShadow = s => {
|
||||
if (typeof s === 'object') {
|
||||
return `${s.inset ? 'inset ' : ''}${s.x} ${s.y} ${s.blur} ${s.spread} ${s.color} / ${s.alpha}`
|
||||
} else {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
export const serialize = (ruleset) => {
|
||||
return ruleset.map((rule) => {
|
||||
if (Object.keys(rule.directives || {}).length === 0) return false
|
||||
|
||||
const header = unroll(rule).reverse().map(rule => {
|
||||
const { component } = rule
|
||||
const newVariant = (rule.variant == null || rule.variant === 'normal') ? '' : ('.' + rule.variant)
|
||||
const newState = (rule.state || []).filter(st => st !== 'normal')
|
||||
|
||||
return `${component}${newVariant}${newState.map(st => ':' + st).join('')}`
|
||||
}).join(' ')
|
||||
|
||||
const content = Object.entries(rule.directives).map(([directive, value]) => {
|
||||
if (directive.startsWith('--')) {
|
||||
const [valType, newValue] = value.split('|') // only first one! intentional!
|
||||
switch (valType) {
|
||||
case 'shadow':
|
||||
return ` ${directive}: ${valType.trim()} | ${newValue.map(serializeShadow).map(s => s.trim()).join(', ')}`
|
||||
default:
|
||||
return ` ${directive}: ${valType.trim()} | ${newValue.trim()}`
|
||||
}
|
||||
} else {
|
||||
switch (directive) {
|
||||
case 'shadow':
|
||||
if (value.length > 0) {
|
||||
return ` ${directive}: ${value.map(serializeShadow).join(', ')}`
|
||||
} else {
|
||||
return ` ${directive}: none`
|
||||
}
|
||||
default:
|
||||
return ` ${directive}: ${value}`
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return `${header} {\n${content.join(';\n')}\n}`
|
||||
}).filter(x => x).join('\n\n')
|
||||
}
|
||||
|
|
@ -522,9 +522,21 @@ export const init = ({
|
|||
console.debug('Eager processing took ' + (t2 - t1) + ' ms')
|
||||
}
|
||||
|
||||
// optimization to traverse big-ass array only once instead of twice
|
||||
const eager = []
|
||||
const lazy = []
|
||||
|
||||
result.forEach(x => {
|
||||
if (typeof x === 'function') {
|
||||
lazy.push(x)
|
||||
} else {
|
||||
eager.push(x)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
lazy: result.filter(x => typeof x === 'function'),
|
||||
eager: result.filter(x => typeof x !== 'function'),
|
||||
lazy,
|
||||
eager,
|
||||
staticVars,
|
||||
engineChecksum
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue