mirror of
https://git.pleroma.social/sjw/pleroma-fe.git
synced 2024-12-23 01:12:53 +01:00
optimizations, WIP theme selector
This commit is contained in:
parent
9d76fcc425
commit
40c9163d21
@ -20,6 +20,16 @@ export default {
|
|||||||
'Tab',
|
'Tab',
|
||||||
'ListItem'
|
'ListItem'
|
||||||
],
|
],
|
||||||
|
validInnerComponentsLite: [
|
||||||
|
'Text',
|
||||||
|
'Link',
|
||||||
|
'Icon',
|
||||||
|
'Border',
|
||||||
|
'Button',
|
||||||
|
'Input',
|
||||||
|
'PanelHeader',
|
||||||
|
'Alert'
|
||||||
|
],
|
||||||
defaultRules: [
|
defaultRules: [
|
||||||
{
|
{
|
||||||
directives: {
|
directives: {
|
||||||
|
@ -12,6 +12,11 @@ export default {
|
|||||||
'Alert',
|
'Alert',
|
||||||
'Button' // mobile post button
|
'Button' // mobile post button
|
||||||
],
|
],
|
||||||
|
validInnerComponentsLite: [
|
||||||
|
'Underlay',
|
||||||
|
'Scrollbar',
|
||||||
|
'ScrollbarElement'
|
||||||
|
],
|
||||||
defaultRules: [
|
defaultRules: [
|
||||||
{
|
{
|
||||||
directives: {
|
directives: {
|
||||||
|
@ -6,6 +6,18 @@ import UnitSetting, { defaultHorizontalUnits } from '../helpers/unit_setting.vue
|
|||||||
|
|
||||||
import FontControl from 'src/components/font_control/font_control.vue'
|
import FontControl from 'src/components/font_control/font_control.vue'
|
||||||
|
|
||||||
|
import { normalizeThemeData } from 'src/modules/interface'
|
||||||
|
|
||||||
|
import {
|
||||||
|
getThemes
|
||||||
|
} from 'src/services/style_setter/style_setter.js'
|
||||||
|
import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js'
|
||||||
|
import { init } from 'src/services/theme_data/theme_data_3.service.js'
|
||||||
|
import {
|
||||||
|
getCssRules,
|
||||||
|
getScopedVersion
|
||||||
|
} from 'src/services/theme_data/css_utils.js'
|
||||||
|
|
||||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||||
import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue'
|
import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue'
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
@ -13,6 +25,8 @@ import {
|
|||||||
faGlobe
|
faGlobe
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
import Preview from './theme_tab/preview.vue'
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
faGlobe
|
faGlobe
|
||||||
)
|
)
|
||||||
@ -20,6 +34,7 @@ library.add(
|
|||||||
const AppearanceTab = {
|
const AppearanceTab = {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
availableStyles: [],
|
||||||
thirdColumnModeOptions: ['none', 'notifications', 'postform'].map(mode => ({
|
thirdColumnModeOptions: ['none', 'notifications', 'postform'].map(mode => ({
|
||||||
key: mode,
|
key: mode,
|
||||||
value: mode,
|
value: mode,
|
||||||
@ -44,7 +59,32 @@ const AppearanceTab = {
|
|||||||
FloatSetting,
|
FloatSetting,
|
||||||
UnitSetting,
|
UnitSetting,
|
||||||
ProfileSettingIndicator,
|
ProfileSettingIndicator,
|
||||||
FontControl
|
FontControl,
|
||||||
|
Preview
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
const self = this
|
||||||
|
|
||||||
|
getThemes()
|
||||||
|
.then((promises) => {
|
||||||
|
return Promise.all(
|
||||||
|
Object.entries(promises)
|
||||||
|
.map(([k, v]) => v.then(res => [k, res]))
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.then(themes => themes.reduce((acc, [k, v]) => {
|
||||||
|
if (v) {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
[k]: v
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
}, {}))
|
||||||
|
.then((themesComplete) => {
|
||||||
|
self.availableStyles = themesComplete
|
||||||
|
})
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
horizontalUnits () {
|
horizontalUnits () {
|
||||||
@ -77,6 +117,25 @@ const AppearanceTab = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
...SharedComputedObject()
|
...SharedComputedObject()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
previewTheme (input) {
|
||||||
|
const style = normalizeThemeData(input)
|
||||||
|
const x = 2
|
||||||
|
if (x === 1) return
|
||||||
|
const theme2 = convertTheme2To3(style)
|
||||||
|
const theme3 = init({
|
||||||
|
inputRuleset: theme2,
|
||||||
|
ultimateBackgroundColor: '#000000',
|
||||||
|
liteMode: true,
|
||||||
|
onlyNormalState: true
|
||||||
|
})
|
||||||
|
|
||||||
|
return getScopedVersion(
|
||||||
|
getCssRules(theme3.eager),
|
||||||
|
'#theme-preview-' + (input.name || input[0]).replace(/ /g, '_')
|
||||||
|
).join('\n')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :label="$t('settings.general')">
|
<div :label="$t('settings.general')">
|
||||||
|
<div class="setting-item">
|
||||||
|
<h2>{{ $t('settings.theme') }}</h2>
|
||||||
|
<ul class="theme-list">
|
||||||
|
<li
|
||||||
|
v-for="style in availableStyles"
|
||||||
|
:key="style.name || style[0]"
|
||||||
|
class="theme-preview"
|
||||||
|
>
|
||||||
|
<h6>{{ style[0] || style.name }}</h6>
|
||||||
|
<!-- eslint-disable vue/no-v-text-v-html-on-component -->
|
||||||
|
<component :is="'style'" v-html="previewTheme(style)"/>
|
||||||
|
<!-- eslint-enable vue/no-v-text-v-html-on-component -->
|
||||||
|
<preview :id="'theme-preview-' + (style[0] || style.name).replace(/ /g,'_')"/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<h2>{{ $t('settings.scale_and_layout') }}</h2>
|
<h2>{{ $t('settings.scale_and_layout') }}</h2>
|
||||||
<ul class="setting-list">
|
<ul class="setting-list">
|
||||||
@ -231,4 +247,18 @@
|
|||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
margin-top: 0.5em;
|
margin-top: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.theme-list {
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-preview {
|
||||||
|
width: 10rem;
|
||||||
|
|
||||||
|
.preview-container {
|
||||||
|
zoom: 0.33;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -139,6 +139,108 @@ export default {}
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.preview-container {
|
.preview-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
border-top: 1px dashed;
|
||||||
|
border-bottom: 1px dashed;
|
||||||
|
border-color: var(--border);
|
||||||
|
margin: 1em 0;
|
||||||
|
padding: 1em;
|
||||||
|
background-color: var(--wallpaper);
|
||||||
|
background-image: var(--body-background-image);
|
||||||
|
background-size: cover;
|
||||||
|
background-position: 50% 50%;
|
||||||
|
|
||||||
|
.theme-preview-content {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dummy {
|
||||||
|
.post {
|
||||||
|
font-family: var(--postFont);
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin-bottom: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icons {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.after-post {
|
||||||
|
margin-top: 1em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar,
|
||||||
|
.avatar-alt {
|
||||||
|
background:
|
||||||
|
linear-gradient(
|
||||||
|
135deg,
|
||||||
|
#b8e1fc 0%,
|
||||||
|
#a9d2f3 10%,
|
||||||
|
#90bae4 25%,
|
||||||
|
#90bcea 37%,
|
||||||
|
#90bff0 50%,
|
||||||
|
#6ba8e5 51%,
|
||||||
|
#a2daf5 83%,
|
||||||
|
#bdf3fd 100%
|
||||||
|
);
|
||||||
|
color: black;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-alt {
|
||||||
|
flex: 0 auto;
|
||||||
|
margin-left: 28px;
|
||||||
|
font-size: 12px;
|
||||||
|
min-width: 20px;
|
||||||
|
min-height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
flex: 0 auto;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
|
||||||
|
.checkbox {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: baseline;
|
||||||
|
margin-right: 1em;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
margin: 1em;
|
||||||
|
border-bottom: 1px solid;
|
||||||
|
border-color: var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
min-width: 3em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.underlay-preview {
|
.underlay-preview {
|
||||||
|
@ -30,7 +30,10 @@ import {
|
|||||||
|
|
||||||
import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js'
|
import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js'
|
||||||
import { init } from 'src/services/theme_data/theme_data_3.service.js'
|
import { init } from 'src/services/theme_data/theme_data_3.service.js'
|
||||||
import { getCssRules } from 'src/services/theme_data/css_utils.js'
|
import {
|
||||||
|
getCssRules,
|
||||||
|
getScopedVersion
|
||||||
|
} from 'src/services/theme_data/css_utils.js'
|
||||||
|
|
||||||
import ColorInput from 'src/components/color_input/color_input.vue'
|
import ColorInput from 'src/components/color_input/color_input.vue'
|
||||||
import RangeInput from 'src/components/range_input/range_input.vue'
|
import RangeInput from 'src/components/range_input/range_input.vue'
|
||||||
@ -703,17 +706,10 @@ export default {
|
|||||||
liteMode: true
|
liteMode: true
|
||||||
})
|
})
|
||||||
|
|
||||||
this.themeV3Preview = getCssRules(theme3.eager)
|
this.themeV3Preview = getScopedVersion(
|
||||||
.map(x => {
|
getCssRules(theme3.eager),
|
||||||
if (x.startsWith('html')) {
|
'#theme-preview'
|
||||||
return x.replace('html', '#theme-preview')
|
).join('\n')
|
||||||
} else if (x.startsWith('#content')) {
|
|
||||||
return x.replace('#content', '#theme-preview')
|
|
||||||
} else {
|
|
||||||
return '#theme-preview > ' + x
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.join('\n')
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -161,107 +161,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-container {
|
|
||||||
border-top: 1px dashed;
|
|
||||||
border-bottom: 1px dashed;
|
|
||||||
border-color: var(--border);
|
|
||||||
margin: 1em 0;
|
|
||||||
padding: 1em;
|
|
||||||
background-color: var(--wallpaper);
|
|
||||||
background-image: var(--body-background-image);
|
|
||||||
background-size: cover;
|
|
||||||
background-position: 50% 50%;
|
|
||||||
|
|
||||||
.dummy {
|
|
||||||
.post {
|
|
||||||
font-family: var(--postFont);
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.content {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin-bottom: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icons {
|
|
||||||
margin-top: 0.5em;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
i {
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.after-post {
|
|
||||||
margin-top: 1em;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar,
|
|
||||||
.avatar-alt {
|
|
||||||
background:
|
|
||||||
linear-gradient(
|
|
||||||
135deg,
|
|
||||||
#b8e1fc 0%,
|
|
||||||
#a9d2f3 10%,
|
|
||||||
#90bae4 25%,
|
|
||||||
#90bcea 37%,
|
|
||||||
#90bff0 50%,
|
|
||||||
#6ba8e5 51%,
|
|
||||||
#a2daf5 83%,
|
|
||||||
#bdf3fd 100%
|
|
||||||
);
|
|
||||||
color: black;
|
|
||||||
font-family: sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-alt {
|
|
||||||
flex: 0 auto;
|
|
||||||
margin-left: 28px;
|
|
||||||
font-size: 12px;
|
|
||||||
min-width: 20px;
|
|
||||||
min-height: 20px;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
flex: 0 auto;
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
display: flex;
|
|
||||||
align-items: baseline;
|
|
||||||
|
|
||||||
.checkbox {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: baseline;
|
|
||||||
margin-right: 1em;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
|
||||||
margin: 1em;
|
|
||||||
border-bottom: 1px solid;
|
|
||||||
border-color: var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
min-width: 3em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.radius-item {
|
.radius-item {
|
||||||
flex-basis: auto;
|
flex-basis: auto;
|
||||||
}
|
}
|
||||||
@ -314,10 +213,6 @@
|
|||||||
max-width: 50em;
|
max-width: 50em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-preview-content {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-warning {
|
.theme-warning {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
|
@ -17,6 +17,15 @@ export default {
|
|||||||
'Attachment',
|
'Attachment',
|
||||||
'PollGraph'
|
'PollGraph'
|
||||||
],
|
],
|
||||||
|
validInnerComponentsLite: [
|
||||||
|
'Text',
|
||||||
|
'Link',
|
||||||
|
'Icon',
|
||||||
|
'Border',
|
||||||
|
'ButtonUnstyled',
|
||||||
|
'RichContent',
|
||||||
|
'Avatar'
|
||||||
|
],
|
||||||
defaultRules: [
|
defaultRules: [
|
||||||
{
|
{
|
||||||
directives: {
|
directives: {
|
||||||
|
@ -234,25 +234,6 @@ const interfaceMod = {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizeThemeData = (themeData) => {
|
|
||||||
if (themeData.themeFileVerison === 1) {
|
|
||||||
return generatePreset(themeData).theme
|
|
||||||
}
|
|
||||||
// New theme presets don't have 'theme' property, they use 'source'
|
|
||||||
const themeSource = themeData.source
|
|
||||||
|
|
||||||
let out // shout, shout let it all out
|
|
||||||
if (!themeData.theme || (themeSource && themeSource.themeEngineVersion === CURRENT_VERSION)) {
|
|
||||||
out = themeSource || themeData
|
|
||||||
} else {
|
|
||||||
out = themeData.theme
|
|
||||||
}
|
|
||||||
|
|
||||||
// generatePreset here basically creates/updates "snapshot",
|
|
||||||
// while also fixing the 2.2 -> 2.3 colors/shadows/etc
|
|
||||||
return generatePreset(out).theme
|
|
||||||
}
|
|
||||||
|
|
||||||
let promise = null
|
let promise = null
|
||||||
|
|
||||||
if (themeName) {
|
if (themeName) {
|
||||||
@ -320,3 +301,38 @@ const interfaceMod = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default interfaceMod
|
export default interfaceMod
|
||||||
|
|
||||||
|
export const normalizeThemeData = (input) => {
|
||||||
|
let themeData = input
|
||||||
|
|
||||||
|
if (Array.isArray(themeData)) {
|
||||||
|
themeData = { colors: {} }
|
||||||
|
themeData.colors.bg = input[1]
|
||||||
|
themeData.colors.fg = input[2]
|
||||||
|
themeData.colors.text = input[3]
|
||||||
|
themeData.colors.link = input[4]
|
||||||
|
themeData.colors.cRed = input[5]
|
||||||
|
themeData.colors.cGreen = input[6]
|
||||||
|
themeData.colors.cBlue = input[7]
|
||||||
|
themeData.colors.cOrange = input[8]
|
||||||
|
return generatePreset(themeData).theme
|
||||||
|
}
|
||||||
|
|
||||||
|
if (themeData.themeFileVerison === 1) {
|
||||||
|
return generatePreset(themeData).theme
|
||||||
|
}
|
||||||
|
|
||||||
|
// New theme presets don't have 'theme' property, they use 'source'
|
||||||
|
const themeSource = themeData.source
|
||||||
|
|
||||||
|
let out // shout, shout let it all out
|
||||||
|
if (!themeData.theme || (themeSource && themeSource.themeEngineVersion === CURRENT_VERSION)) {
|
||||||
|
out = themeSource || themeData
|
||||||
|
} else {
|
||||||
|
out = themeData.theme
|
||||||
|
}
|
||||||
|
|
||||||
|
// generatePreset here basically creates/updates "snapshot",
|
||||||
|
// while also fixing the 2.2 -> 2.3 colors/shadows/etc
|
||||||
|
return generatePreset(out).theme
|
||||||
|
}
|
||||||
|
@ -159,3 +159,15 @@ export const getCssRules = (rules, debug) => rules.map(rule => {
|
|||||||
footer
|
footer
|
||||||
].join('\n')
|
].join('\n')
|
||||||
}).filter(x => x)
|
}).filter(x => x)
|
||||||
|
|
||||||
|
export const getScopedVersion = (rules, newScope) => {
|
||||||
|
return rules.map(x => {
|
||||||
|
if (x.startsWith('html')) {
|
||||||
|
return x.replace('html', newScope)
|
||||||
|
} else if (x.startsWith('#content')) {
|
||||||
|
return x.replace('#content', newScope)
|
||||||
|
} else {
|
||||||
|
return newScope + ' > ' + x
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -39,7 +39,23 @@ export const getAllPossibleCombinations = (array) => {
|
|||||||
return combos.reduce((acc, x) => [...acc, ...x], [])
|
return combos.reduce((acc, x) => [...acc, ...x], [])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts rule, parents and their criteria into a CSS (or path if ignoreOutOfTreeSelector == true) selector
|
/**
|
||||||
|
* Converts rule, parents and their criteria into a CSS (or path if ignoreOutOfTreeSelector == true)
|
||||||
|
* selector.
|
||||||
|
*
|
||||||
|
* "path" here refers to "fake" selector that cannot be actually used in UI but is used for internal
|
||||||
|
* purposes
|
||||||
|
*
|
||||||
|
* @param {Object} components - object containing all components definitions
|
||||||
|
*
|
||||||
|
* @returns {Function}
|
||||||
|
* @param {Object} rule - rule in question to convert to CSS selector
|
||||||
|
* @param {boolean} ignoreOutOfTreeSelector - wthether to ignore aformentioned field in
|
||||||
|
* component definition and use selector
|
||||||
|
* @param {boolean} isParent - (mostly) internal argument used when recursing
|
||||||
|
*
|
||||||
|
* @returns {String} CSS selector (or path)
|
||||||
|
*/
|
||||||
export const genericRuleToSelector = components => (rule, ignoreOutOfTreeSelector, isParent) => {
|
export const genericRuleToSelector = components => (rule, ignoreOutOfTreeSelector, isParent) => {
|
||||||
if (!rule && !isParent) return null
|
if (!rule && !isParent) return null
|
||||||
const component = components[rule.component]
|
const component = components[rule.component]
|
||||||
@ -79,6 +95,17 @@ export const genericRuleToSelector = components => (rule, ignoreOutOfTreeSelecto
|
|||||||
return selectors.trim()
|
return selectors.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if combination matches
|
||||||
|
*
|
||||||
|
* @param {Object} criteria - criteria to match against
|
||||||
|
* @param {Object} subject - rule/combination to check match
|
||||||
|
* @param {boolean} strict - strict checking:
|
||||||
|
* By default every variant and state inherits from "normal" state/variant
|
||||||
|
* so when checking if combination matches, it WILL match against "normal"
|
||||||
|
* state/variant. In strict mode inheritance is ignored an "normal" does
|
||||||
|
* not match
|
||||||
|
*/
|
||||||
export const combinationsMatch = (criteria, subject, strict) => {
|
export const combinationsMatch = (criteria, subject, strict) => {
|
||||||
if (criteria.component !== subject.component) return false
|
if (criteria.component !== subject.component) return false
|
||||||
|
|
||||||
@ -101,6 +128,15 @@ export const combinationsMatch = (criteria, subject, strict) => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for rule that matches `criteria` in set of rules
|
||||||
|
* meant to be used in a ruleset.filter() function
|
||||||
|
*
|
||||||
|
* @param {Object} criteria - criteria to search for
|
||||||
|
* @param {boolean} strict - whether search strictly or not (see combinationsMatch)
|
||||||
|
*
|
||||||
|
* @return function that returns true/false if subject matches
|
||||||
|
*/
|
||||||
export const findRules = (criteria, strict) => subject => {
|
export const findRules = (criteria, strict) => subject => {
|
||||||
// If we searching for "general" rules - ignore "specific" ones
|
// If we searching for "general" rules - ignore "specific" ones
|
||||||
if (criteria.parent === null && !!subject.parent) return false
|
if (criteria.parent === null && !!subject.parent) return false
|
||||||
@ -125,6 +161,7 @@ export const findRules = (criteria, strict) => subject => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pre-fills 'normal' state/variant if missing
|
||||||
export const normalizeCombination = rule => {
|
export const normalizeCombination = rule => {
|
||||||
rule.variant = rule.variant ?? 'normal'
|
rule.variant = rule.variant ?? 'normal'
|
||||||
rule.state = [...new Set(['normal', ...(rule.state || [])])]
|
rule.state = [...new Set(['normal', ...(rule.state || [])])]
|
||||||
|
@ -149,11 +149,30 @@ const ruleToSelector = genericRuleToSelector(components)
|
|||||||
|
|
||||||
export const getEngineChecksum = () => engineChecksum
|
export const getEngineChecksum = () => engineChecksum
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes and compiles the theme according to the ruleset
|
||||||
|
*
|
||||||
|
* @param {Object[]} inputRuleset - set of rules to compile theme against. Acts as an override to
|
||||||
|
* component default rulesets
|
||||||
|
* @param {string} ultimateBackgroundColor - Color that will be the "final" background for
|
||||||
|
* calculating contrast ratios and making text automatically accessible. Really used for cases when
|
||||||
|
* stuff is transparent.
|
||||||
|
* @param {boolean} debug - print out debug information in console, mostly just performance stuff
|
||||||
|
* @param {boolean} liteMode - use validInnerComponentsLite instead of validInnerComponents, meant to
|
||||||
|
* generatate theme previews and such that need to be compiled faster and don't require a lot of other
|
||||||
|
* components present in "normal" mode
|
||||||
|
* @param {boolean} onlyNormalState - only use components 'normal' states, meant for generating theme
|
||||||
|
* previews since states are the biggest factor for compilation time and are completely unnecessary
|
||||||
|
* when previewing multiple themes at same time
|
||||||
|
* @param {string} rootComponentName - [UNTESTED] which component to start from, meant for previewing a
|
||||||
|
* part of the theme (i.e. just the button) for themes 3 editor.
|
||||||
|
*/
|
||||||
export const init = ({
|
export const init = ({
|
||||||
inputRuleset,
|
inputRuleset,
|
||||||
ultimateBackgroundColor,
|
ultimateBackgroundColor,
|
||||||
debug = false,
|
debug = false,
|
||||||
liteMode = false,
|
liteMode = false,
|
||||||
|
onlyNormalState = false,
|
||||||
rootComponentName = 'Root'
|
rootComponentName = 'Root'
|
||||||
}) => {
|
}) => {
|
||||||
if (!inputRuleset) throw new Error('Ruleset is null or undefined!')
|
if (!inputRuleset) throw new Error('Ruleset is null or undefined!')
|
||||||
@ -402,11 +421,16 @@ export const init = ({
|
|||||||
const processInnerComponent = (component, parent) => {
|
const processInnerComponent = (component, parent) => {
|
||||||
const combinations = []
|
const combinations = []
|
||||||
const {
|
const {
|
||||||
validInnerComponents = [],
|
|
||||||
states: originalStates = {},
|
states: originalStates = {},
|
||||||
variants: originalVariants = {}
|
variants: originalVariants = {}
|
||||||
} = component
|
} = component
|
||||||
|
|
||||||
|
const validInnerComponents = (
|
||||||
|
liteMode
|
||||||
|
? (component.validInnerComponentsLite || component.validInnerComponents)
|
||||||
|
: component.validInnerComponents
|
||||||
|
) || []
|
||||||
|
|
||||||
// Normalizing states and variants to always include "normal"
|
// Normalizing states and variants to always include "normal"
|
||||||
const states = { normal: '', ...originalStates }
|
const states = { normal: '', ...originalStates }
|
||||||
const variants = { normal: '', ...originalVariants }
|
const variants = { normal: '', ...originalVariants }
|
||||||
@ -418,7 +442,11 @@ export const init = ({
|
|||||||
|
|
||||||
// Optimization: we only really need combinations without "normal" because all states implicitly have it
|
// Optimization: we only really need combinations without "normal" because all states implicitly have it
|
||||||
const permutationStateKeys = Object.keys(states).filter(s => s !== 'normal')
|
const permutationStateKeys = Object.keys(states).filter(s => s !== 'normal')
|
||||||
const stateCombinations = [
|
const stateCombinations = onlyNormalState
|
||||||
|
? [
|
||||||
|
['normal']
|
||||||
|
]
|
||||||
|
: [
|
||||||
['normal'],
|
['normal'],
|
||||||
...getAllPossibleCombinations(permutationStateKeys)
|
...getAllPossibleCombinations(permutationStateKeys)
|
||||||
.map(combination => ['normal', ...combination])
|
.map(combination => ['normal', ...combination])
|
||||||
@ -460,7 +488,9 @@ export const init = ({
|
|||||||
const t0 = performance.now()
|
const t0 = performance.now()
|
||||||
const combinations = processInnerComponent(components[rootComponentName] ?? components.Root)
|
const combinations = processInnerComponent(components[rootComponentName] ?? components.Root)
|
||||||
const t1 = performance.now()
|
const t1 = performance.now()
|
||||||
|
if (debug) {
|
||||||
console.debug('Tree traveral took ' + (t1 - t0) + ' ms')
|
console.debug('Tree traveral took ' + (t1 - t0) + ' ms')
|
||||||
|
}
|
||||||
|
|
||||||
const result = combinations.map((combination) => {
|
const result = combinations.map((combination) => {
|
||||||
if (combination.lazy) {
|
if (combination.lazy) {
|
||||||
@ -470,7 +500,9 @@ export const init = ({
|
|||||||
}
|
}
|
||||||
}).filter(x => x)
|
}).filter(x => x)
|
||||||
const t2 = performance.now()
|
const t2 = performance.now()
|
||||||
|
if (debug) {
|
||||||
console.debug('Eager processing took ' + (t2 - t1) + ' ms')
|
console.debug('Eager processing took ' + (t2 - t1) + ' ms')
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
lazy: result.filter(x => typeof x === 'function'),
|
lazy: result.filter(x => typeof x === 'function'),
|
||||||
|
Loading…
Reference in New Issue
Block a user