From 521d308a6c6777a45c94183751f3305ce23bdad3 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 18 Jan 2024 14:35:25 +0200 Subject: themes 3 initial work --- src/components/text.style.js | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/components/text.style.js (limited to 'src/components/text.style.js') diff --git a/src/components/text.style.js b/src/components/text.style.js new file mode 100644 index 00000000..f87268bb --- /dev/null +++ b/src/components/text.style.js @@ -0,0 +1,9 @@ +export default { + name: 'Text', + states: { + faint: '.faint' + }, + variants: { + green: '/.greentext' + } +} -- cgit v1.2.3-70-g09d2 From 0729b529d7da2002f25039e1dad2732302009cf3 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 23 Jan 2024 00:43:46 +0200 Subject: some more stuff, generating CSS selectors from rules --- src/components/button.style.js | 1 + src/components/icon.style.js | 3 +- src/components/panel.style.js | 1 + src/components/text.style.js | 4 +- src/components/underlay.style.js | 3 +- src/services/theme_data/pleromafe.t3.js | 26 +++++++++ src/services/theme_data/theme_data_3.service.js | 62 +++++++++++++++++----- .../specs/services/theme_data/theme_data3.spec.js | 26 +++++++++ 8 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 src/services/theme_data/pleromafe.t3.js create mode 100644 test/unit/specs/services/theme_data/theme_data3.spec.js (limited to 'src/components/text.style.js') diff --git a/src/components/button.style.js b/src/components/button.style.js index 8f2e8f82..1c229f43 100644 --- a/src/components/button.style.js +++ b/src/components/button.style.js @@ -1,5 +1,6 @@ export default { name: 'Button', + selector: '.btn', states: { hover: ':hover', disabled: ':disabled', diff --git a/src/components/icon.style.js b/src/components/icon.style.js index 1e2781d6..732cf16f 100644 --- a/src/components/icon.style.js +++ b/src/components/icon.style.js @@ -1,3 +1,4 @@ export default { - name: 'Icon' + name: 'Icon', + selector: '.icon' } diff --git a/src/components/panel.style.js b/src/components/panel.style.js index 1666d923..d34d5434 100644 --- a/src/components/panel.style.js +++ b/src/components/panel.style.js @@ -1,5 +1,6 @@ export default { name: 'Panel', + selector: '.panel', validInnerComponents: [ 'Text', 'Icon', diff --git a/src/components/text.style.js b/src/components/text.style.js index f87268bb..2aa5e745 100644 --- a/src/components/text.style.js +++ b/src/components/text.style.js @@ -1,9 +1,7 @@ export default { name: 'Text', + selector: '', states: { faint: '.faint' - }, - variants: { - green: '/.greentext' } } diff --git a/src/components/underlay.style.js b/src/components/underlay.style.js index bae9fc0b..426df1d7 100644 --- a/src/components/underlay.style.js +++ b/src/components/underlay.style.js @@ -1,5 +1,6 @@ export default { - name: 'Panel', + name: 'Underlay', + selector: '.underlay', validInnerComponents: [ 'Panel' ] diff --git a/src/services/theme_data/pleromafe.t3.js b/src/services/theme_data/pleromafe.t3.js new file mode 100644 index 00000000..b44a395e --- /dev/null +++ b/src/services/theme_data/pleromafe.t3.js @@ -0,0 +1,26 @@ +export const sampleRules = [ + { + component: 'Underlay', + // variant: 'normal', + // state: 'normal' + directives: { + background: '#000', + opacity: 0.2 + } + }, + { + component: 'Panel', + directives: { + background: '#FFFFFF', + opacity: 0.9 + } + }, + { + component: 'Button', + directives: { + background: '#808080', + text: '#FFFFFF', + opacity: 0.5 + } + } +] diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js index 3a6fd552..39a9998d 100644 --- a/src/services/theme_data/theme_data_3.service.js +++ b/src/services/theme_data/theme_data_3.service.js @@ -14,7 +14,7 @@ const components = { } // This gives you an array of arrays of all possible unique (i.e. order-insensitive) combinations -const getAllPossibleCombinations = (array) => { +export const getAllPossibleCombinations = (array) => { const combos = [array.map(x => [x])] for (let comboSize = 2; comboSize <= array.length; comboSize++) { const previous = combos[combos.length - 1] @@ -30,15 +30,52 @@ const getAllPossibleCombinations = (array) => { return combos.reduce((acc, x) => [...acc, ...x], []) } -export const init = () => { +export const ruleToSelector = (rule) => { + const component = components[rule.component] + const { states, variants, selector } = component + + const applicableStates = (rule.state.filter(x => x !== 'normal') || []).map(state => states[state]) + + const applicableVariantName = (rule.variant || 'normal') + let applicableVariant = '' + if (applicableVariantName !== 'normal') { + applicableVariant = variants[applicableVariantName] + } + + const selectors = [selector, applicableVariant, ...applicableStates] + .toSorted((a, b) => { + if (a.startsWith(':')) return 1 + else return -1 + }) + .join('') + + if (rule.parent) { + return ruleToSelector(rule.parent) + ' ' + selectors + } + return selectors +} + +export const init = (ruleset) => { const rootName = root.name const rules = [] + const rulesByComponent = {} + + const addRule = (rule) => { + rules.push(rule) + rulesByComponent[rule.component] = rulesByComponent[rule.component] || [] + rulesByComponent.push(rule) + } + + ruleset.forEach(rule => { + + }) const processInnerComponent = (component, parent) => { const { - validInnerComponents, + validInnerComponents = [], states: originalStates = {}, - variants: originalVariants = {} + variants: originalVariants = {}, + name } = component const states = { normal: '', ...originalStates } @@ -51,16 +88,17 @@ export const init = () => { }).reduce((acc, x) => [...acc, ...x], []) stateVariantCombination.forEach(combination => { - rules.push(({ - parent, - component: component.name, - state: combination.state, - variant: combination.variant - })) - - innerComponents.forEach(innerComponent => processInnerComponent(innerComponent, combination)) + // addRule(({ + // parent, + // component: component.name, + // state: combination.state, + // variant: combination.variant + // })) + + innerComponents.forEach(innerComponent => processInnerComponent(innerComponent, { parent, component: name, ...combination })) }) } processInnerComponent(components[rootName]) + return rules } diff --git a/test/unit/specs/services/theme_data/theme_data3.spec.js b/test/unit/specs/services/theme_data/theme_data3.spec.js new file mode 100644 index 00000000..e76200c0 --- /dev/null +++ b/test/unit/specs/services/theme_data/theme_data3.spec.js @@ -0,0 +1,26 @@ +// import { topoSort } from 'src/services/theme_data/theme_data.service.js' +import { + getAllPossibleCombinations, + init, + ruleToSelector +} from 'src/services/theme_data/theme_data_3.service.js' +import { + sampleRules +} from 'src/services/theme_data/pleromafe.t3.js' + +describe.only('Theme Data 3', () => { + describe('getAllPossibleCombinations', () => { + it('test simple case', () => { + const out = getAllPossibleCombinations([1, 2, 3]).map(x => x.sort((a, b) => a - b)) + expect(out).to.eql([[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]) + }) + }) + + describe('init', () => { + it('test simple case', () => { + const out = init(sampleRules) + console.log(JSON.stringify(out, null, 2)) + out.forEach(r => console.log(ruleToSelector(r))) + }) + }) +}) -- cgit v1.2.3-70-g09d2 From 22b32f149d7753592fddcfb1cd2679b3fbac33d6 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 23 Jan 2024 19:18:55 +0200 Subject: shit more or less works for the very basic stuff --- src/components/button.style.js | 4 +- src/components/panel.style.js | 3 +- src/components/panel_header.style.js | 9 ++ src/components/text.style.js | 2 +- src/components/underlay.style.js | 2 +- src/services/color_convert/color_convert.js | 1 + src/services/theme_data/pleromafe.t3.js | 10 +- src/services/theme_data/theme_data_3.service.js | 116 +++++++++++++++++++-- .../specs/services/theme_data/theme_data3.spec.js | 7 +- 9 files changed, 132 insertions(+), 22 deletions(-) create mode 100644 src/components/panel_header.style.js (limited to 'src/components/text.style.js') diff --git a/src/components/button.style.js b/src/components/button.style.js index 1c229f43..51f781e1 100644 --- a/src/components/button.style.js +++ b/src/components/button.style.js @@ -2,10 +2,10 @@ export default { name: 'Button', selector: '.btn', states: { - hover: ':hover', disabled: ':disabled', + toggled: '.toggled', pressed: ':active', - toggled: '.toggled' + hover: ':hover' }, variants: { danger: '.danger', diff --git a/src/components/panel.style.js b/src/components/panel.style.js index d34d5434..e3da4d1a 100644 --- a/src/components/panel.style.js +++ b/src/components/panel.style.js @@ -4,6 +4,7 @@ export default { validInnerComponents: [ 'Text', 'Icon', - 'Button' + 'Button', + 'PanelHeader' ] } diff --git a/src/components/panel_header.style.js b/src/components/panel_header.style.js new file mode 100644 index 00000000..aaa8bea9 --- /dev/null +++ b/src/components/panel_header.style.js @@ -0,0 +1,9 @@ +export default { + name: 'PanelHeader', + selector: '.panel-heading', + validInnerComponents: [ + 'Text', + 'Icon', + 'Button' + ] +} diff --git a/src/components/text.style.js b/src/components/text.style.js index 2aa5e745..050194cb 100644 --- a/src/components/text.style.js +++ b/src/components/text.style.js @@ -1,6 +1,6 @@ export default { name: 'Text', - selector: '', + selector: '/*text*/', states: { faint: '.faint' } diff --git a/src/components/underlay.style.js b/src/components/underlay.style.js index 426df1d7..be1ecc56 100644 --- a/src/components/underlay.style.js +++ b/src/components/underlay.style.js @@ -1,6 +1,6 @@ export default { name: 'Underlay', - selector: '.underlay', + selector: '#app', validInnerComponents: [ 'Panel' ] diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js index 47d6344e..23e4ca61 100644 --- a/src/services/color_convert/color_convert.js +++ b/src/services/color_convert/color_convert.js @@ -188,6 +188,7 @@ export const rgba2css = function (rgba) { */ export const getTextColor = function (bg, text, preserve) { const contrast = getContrastRatio(bg, text) + console.log(contrast) if (contrast < 4.5) { const base = typeof text.a !== 'undefined' ? { a: text.a } : {} diff --git a/src/services/theme_data/pleromafe.t3.js b/src/services/theme_data/pleromafe.t3.js index b44a395e..b7b12573 100644 --- a/src/services/theme_data/pleromafe.t3.js +++ b/src/services/theme_data/pleromafe.t3.js @@ -4,7 +4,7 @@ export const sampleRules = [ // variant: 'normal', // state: 'normal' directives: { - background: '#000', + background: '#000000', opacity: 0.2 } }, @@ -15,11 +15,17 @@ export const sampleRules = [ opacity: 0.9 } }, + { + component: 'PanelHeader', + directives: { + background: '#000000', + opacity: 0.9 + } + }, { component: 'Button', directives: { background: '#808080', - text: '#FFFFFF', opacity: 0.5 } } diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js index 39a9998d..d65d5352 100644 --- a/src/services/theme_data/theme_data_3.service.js +++ b/src/services/theme_data/theme_data_3.service.js @@ -1,5 +1,9 @@ +import { convert } from 'chromatism' +import { alphaBlend, getTextColor, rgba2css } from '../color_convert/color_convert.js' + import Underlay from 'src/components/underlay.style.js' import Panel from 'src/components/panel.style.js' +import PanelHeader from 'src/components/panel_header.style.js' import Button from 'src/components/button.style.js' import Text from 'src/components/text.style.js' import Icon from 'src/components/icon.style.js' @@ -8,6 +12,7 @@ const root = Underlay const components = { Underlay, Panel, + PanelHeader, Button, Text, Icon @@ -34,7 +39,7 @@ export const ruleToSelector = (rule) => { const component = components[rule.component] const { states, variants, selector } = component - const applicableStates = (rule.state.filter(x => x !== 'normal') || []).map(state => states[state]) + const applicableStates = ((rule.state || []).filter(x => x !== 'normal')).map(state => states[state]) const applicableVariantName = (rule.variant || 'normal') let applicableVariant = '' @@ -63,12 +68,37 @@ export const init = (ruleset) => { const addRule = (rule) => { rules.push(rule) rulesByComponent[rule.component] = rulesByComponent[rule.component] || [] - rulesByComponent.push(rule) + rulesByComponent[rule.component].push(rule) } - ruleset.forEach(rule => { + const findRules = (combination) => rule => { + if (combination.component !== rule.component) return false + if (Object.prototype.hasOwnProperty.call(rule, 'variant')) { + if (combination.variant !== rule.variant) return false + } else { + if (combination.variant !== 'normal') return false + } + + if (Object.prototype.hasOwnProperty.call(rule, 'state')) { + const ruleStatesSet = new Set(['normal', ...(rule.state || [])]) + return combination.state.every(state => ruleStatesSet.has(state)) + } else { + if (combination.state.length !== 1 || combination.state[0] !== 'normal') return false + return true + } + } - }) + const findLowerLevelRule = (parent, filter = () => true) => { + let lowerLevelComponent = null + let currentParent = parent + while (currentParent) { + const rulesParent = ruleset.filter(findRules(currentParent, true)) + lowerLevelComponent = rulesParent[rulesParent.length - 1] + currentParent = currentParent.parent + if (lowerLevelComponent && filter(lowerLevelComponent)) currentParent = null + } + return filter(lowerLevelComponent) ? lowerLevelComponent : null + } const processInnerComponent = (component, parent) => { const { @@ -87,18 +117,82 @@ export const init = (ruleset) => { return stateCombinations.map(state => ({ variant, state })) }).reduce((acc, x) => [...acc, ...x], []) + const VIRTUAL_COMPONENTS = new Set(['Text', 'Link', 'Icon']) + stateVariantCombination.forEach(combination => { - // addRule(({ - // parent, - // component: component.name, - // state: combination.state, - // variant: combination.variant - // })) + const existingRules = ruleset.filter(findRules({ component: component.name, ...combination })) + const lastRule = existingRules[existingRules.length - 1] + + if (existingRules.length !== 0) { + const { directives } = lastRule + const rgb = convert(directives.background).rgb + + // TODO: DEFAULT TEXT COLOR + const bg = findLowerLevelRule(parent)?.cache.background || convert('#FFFFFF').rgb + + if (!lastRule.cache?.background) { + const blend = directives.opacity < 1 ? alphaBlend(rgb, directives.opacity, bg) : rgb + lastRule.cache = lastRule.cache || {} + lastRule.cache.background = blend + + addRule(lastRule) + } + } else { + if (VIRTUAL_COMPONENTS.has(component.name)) { + const selector = component.name + ruleToSelector({ component: component.name, ...combination }) + + const lowerLevel = findLowerLevelRule(parent, (r) => { + if (components[r.component].validInnerComponents.indexOf(component.name) < 0) return false + if (r.cache?.background === undefined) return false + if (r.cache.textDefined) { + return !r.cache.textDefined[selector] + } + return true + }) + if (!lowerLevel) return + lowerLevel.cache.textDefined = lowerLevel.cache.textDefined || {} + lowerLevel.cache.textDefined[selector] = true + addRule({ + parent, + component: component.name, + ...combination, + directives: { + // TODO: DEFAULT TEXT COLOR + textColor: getTextColor(convert(lowerLevel.cache.background).rgb, convert('#FFFFFF').rgb, component.name === 'Link'), + // Debug: lets you see what it think background color should be + background: convert(lowerLevel.cache.background).hex + } + }) + } + } innerComponents.forEach(innerComponent => processInnerComponent(innerComponent, { parent, component: name, ...combination })) }) } processInnerComponent(components[rootName]) - return rules + + // console.info(rules.map(x => [ + // (parent?.component || 'root') + ' -> ' + x.component, + // // 'Cached background:' + convert(bg).hex, + // // 'Color: ' + convert(x.directives.background).hex + ' A:' + x.directives.opacity, + // JSON.stringify(x.directives) + // // '=> Blend: ' + convert(x.cache.background).hex + // ].join(' '))) + + return { + raw: rules, + css: rules.map(rule => { + const header = ruleToSelector(rule) + ' {' + const footer = '}' + const directives = Object.entries(rule.directives).map(([k, v]) => { + switch (k) { + case 'background': return 'background-color: ' + rgba2css({ ...convert(v).rgb, a: rule.directives.opacity ?? 1 }) + case 'textColor': return 'color: ' + rgba2css({ ...convert(v).rgb, a: rule.directives.opacity ?? 1 }) + default: return '' + } + }).filter(x => x).map(x => ' ' + x).join(';\n') + return [header, directives, footer].join('\n') + }) + } } diff --git a/test/unit/specs/services/theme_data/theme_data3.spec.js b/test/unit/specs/services/theme_data/theme_data3.spec.js index e76200c0..915bb5ce 100644 --- a/test/unit/specs/services/theme_data/theme_data3.spec.js +++ b/test/unit/specs/services/theme_data/theme_data3.spec.js @@ -1,8 +1,7 @@ // import { topoSort } from 'src/services/theme_data/theme_data.service.js' import { getAllPossibleCombinations, - init, - ruleToSelector + init } from 'src/services/theme_data/theme_data_3.service.js' import { sampleRules @@ -19,8 +18,8 @@ describe.only('Theme Data 3', () => { describe('init', () => { it('test simple case', () => { const out = init(sampleRules) - console.log(JSON.stringify(out, null, 2)) - out.forEach(r => console.log(ruleToSelector(r))) + // console.log(JSON.stringify(out, null, 2)) + console.log('\n' + out.css.join('\n') + '\n') }) }) }) -- cgit v1.2.3-70-g09d2 From 53a4b1f9a6a9aa6bc044609c3accb074d924daf9 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 31 Jan 2024 17:39:51 +0200 Subject: better virtual components and stuff --- src/App.scss | 28 +- src/components/icon.style.js | 13 +- src/components/link.style.js | 25 ++ src/components/notification/notification.vue | 2 +- src/components/notifications/notifications.scss | 16 +- src/components/panel.style.js | 1 + src/components/panel_header.style.js | 1 + src/components/status/status.vue | 2 +- src/components/status_body/status_body.scss | 5 +- src/components/text.style.js | 38 ++- src/components/underlay.style.js | 3 +- src/panel.scss | 11 - src/services/color_convert/color_convert.js | 3 +- src/services/style_setter/style_setter.js | 19 +- src/services/theme_data/pleromafe.t3.js | 16 +- src/services/theme_data/theme_data_3.service.js | 304 ++++++++++++++++----- .../specs/services/theme_data/theme_data3.spec.js | 2 +- 17 files changed, 353 insertions(+), 136 deletions(-) create mode 100644 src/components/link.style.js (limited to 'src/components/text.style.js') diff --git a/src/App.scss b/src/App.scss index ef68ac50..8e9f3171 100644 --- a/src/App.scss +++ b/src/App.scss @@ -24,8 +24,7 @@ body { font-family: sans-serif; font-family: var(--interfaceFont, sans-serif); margin: 0; - color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--text); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; overscroll-behavior-y: none; @@ -111,8 +110,7 @@ body { a { text-decoration: none; - color: $fallback--link; - color: var(--link, $fallback--link); + color: var(--link); } h4 { @@ -128,8 +126,7 @@ h4 { i[class*="icon-"], .svg-inline--fa, .iconLetter { - color: $fallback--icon; - color: var(--icon, $fallback--icon); + color: var(--icon); } .button-unstyled:hover, @@ -763,17 +760,11 @@ option { } .faint { - color: $fallback--faint; - color: var(--faint, $fallback--faint); -} - -.faint-link { - color: $fallback--faint; - color: var(--faint, $fallback--faint); + --text: var(--textFaint); + --textGreentext: var(--textGreentextFaint); + --link: var(--linkFaint); - &:hover { - text-decoration: underline; - } + color: var(--text); } .visibility-notice { @@ -816,6 +807,11 @@ option { opacity: 0.25; } +.timeago { + --link: var(--text); + --linkFaint: var(--textFaint); +} + .login-hint { text-align: center; diff --git a/src/components/icon.style.js b/src/components/icon.style.js index 732cf16f..adc72fc5 100644 --- a/src/components/icon.style.js +++ b/src/components/icon.style.js @@ -1,4 +1,15 @@ export default { name: 'Icon', - selector: '.icon' + virtual: true, + selector: '.svg-inline--fa', + defaultRules: [ + { + component: 'Icon', + directives: { + textColor: '--text', + textOpacity: 0.5, + textOpacityMode: 'mixrgb' + } + } + ] } diff --git a/src/components/link.style.js b/src/components/link.style.js new file mode 100644 index 00000000..0128fd91 --- /dev/null +++ b/src/components/link.style.js @@ -0,0 +1,25 @@ +export default { + name: 'Link', + selector: 'a', + virtual: true, + states: { + faint: '.faint' + }, + defaultRules: [ + { + component: 'Link', + directives: { + textColor: '--link' + } + }, + { + component: 'Link', + state: ['faint'], + directives: { + textColor: '--link', + textOpacity: 0.5, + textOpacityMode: 'fake' + } + } + ] +} diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index a8eceab0..5c425200 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -155,7 +155,7 @@ .button-default { flex-shrink: 0; diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js index 23e4ca61..d92bbbe0 100644 --- a/src/services/color_convert/color_convert.js +++ b/src/services/color_convert/color_convert.js @@ -173,7 +173,7 @@ export const mixrgb = (a, b) => { * @returns {String} CSS rgba() color */ export const rgba2css = function (rgba) { - return `rgba(${Math.floor(rgba.r)}, ${Math.floor(rgba.g)}, ${Math.floor(rgba.b)}, ${rgba.a})` + return `rgba(${Math.floor(rgba.r)}, ${Math.floor(rgba.g)}, ${Math.floor(rgba.b)}, ${rgba.a ?? 1})` } /** @@ -188,7 +188,6 @@ export const rgba2css = function (rgba) { */ export const getTextColor = function (bg, text, preserve) { const contrast = getContrastRatio(bg, text) - console.log(contrast) if (contrast < 4.5) { const base = typeof text.a !== 'undefined' ? { a: text.a } : {} diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 43fe3c73..ba98c4d9 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -1,10 +1,15 @@ import { convert } from 'chromatism' import { rgb2hex, hex2rgb, rgba2css, getCssColor, relativeLuminance } from '../color_convert/color_convert.js' import { getColors, computeDynamicColor, getOpacitySlot } from '../theme_data/theme_data.service.js' +import { init } from '../theme_data/theme_data_3.service.js' +import { + sampleRules +} from 'src/services/theme_data/pleromafe.t3.js' import { defaultState } from '../../modules/config.js' export const applyTheme = (input) => { - const { rules } = generatePreset(input) + const { rules, t3b } = generatePreset(input) + const themes3 = init(sampleRules, t3b) const head = document.head const body = document.body body.classList.add('hidden') @@ -18,6 +23,10 @@ export const applyTheme = (input) => { styleSheet.insertRule(`:root { ${rules.colors} }`, 'index-max') styleSheet.insertRule(`:root { ${rules.shadows} }`, 'index-max') styleSheet.insertRule(`:root { ${rules.fonts} }`, 'index-max') + themes3.css.forEach(rule => { + console.log(rule) + styleSheet.insertRule(rule, 'index-max') + }) body.classList.remove('hidden') } @@ -326,7 +335,7 @@ export const generateShadows = (input, colors) => { } } -export const composePreset = (colors, radii, shadows, fonts) => { +export const composePreset = (colors, radii, shadows, fonts, t3b) => { return { rules: { ...shadows.rules, @@ -339,7 +348,8 @@ export const composePreset = (colors, radii, shadows, fonts) => { ...colors.theme, ...radii.theme, ...fonts.theme - } + }, + t3b } } @@ -349,7 +359,8 @@ export const generatePreset = (input) => { colors, generateRadii(input), generateShadows(input, colors.theme.colors, colors.mod), - generateFonts(input) + generateFonts(input), + colors.theme.colors ) } diff --git a/src/services/theme_data/pleromafe.t3.js b/src/services/theme_data/pleromafe.t3.js index 8d8e19cd..9fadf0ee 100644 --- a/src/services/theme_data/pleromafe.t3.js +++ b/src/services/theme_data/pleromafe.t3.js @@ -11,30 +11,30 @@ export const sampleRules = [ { component: 'Panel', directives: { - background: '#FFFFFF', - opacity: 0.9 + background: '--fg' + // opacity: 0.9 } }, { component: 'PanelHeader', directives: { - background: '#000000', - opacity: 0.9 + background: '--fg' + // opacity: 0.9 } }, { component: 'Button', directives: { - background: '#000000', - opacity: 0.8 + background: '--fg' + // opacity: 0.8 } }, { component: 'Button', state: ['hover'], directives: { - background: '#FF00FF', - opacity: 0.9 + background: '#FFFFFF' + // opacity: 0.9 } } ] diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js index 2ef5e17f..c9468d07 100644 --- a/src/services/theme_data/theme_data_3.service.js +++ b/src/services/theme_data/theme_data_3.service.js @@ -1,11 +1,12 @@ import { convert } from 'chromatism' -import { alphaBlend, getTextColor, rgba2css } from '../color_convert/color_convert.js' +import { alphaBlend, getTextColor, rgba2css, mixrgb } from '../color_convert/color_convert.js' import Underlay from 'src/components/underlay.style.js' import Panel from 'src/components/panel.style.js' import PanelHeader from 'src/components/panel_header.style.js' import Button from 'src/components/button.style.js' import Text from 'src/components/text.style.js' +import Link from 'src/components/link.style.js' import Icon from 'src/components/icon.style.js' const root = Underlay @@ -15,6 +16,7 @@ const components = { PanelHeader, Button, Text, + Link, Icon } @@ -35,9 +37,9 @@ export const getAllPossibleCombinations = (array) => { return combos.reduce((acc, x) => [...acc, ...x], []) } -export const ruleToSelector = (rule) => { +export const ruleToSelector = (rule, isParent) => { const component = components[rule.component] - const { states, variants, selector } = component + const { states, variants, selector, outOfTreeSelector } = component const applicableStates = ((rule.state || []).filter(x => x !== 'normal')).map(state => states[state]) @@ -47,56 +49,104 @@ export const ruleToSelector = (rule) => { applicableVariant = variants[applicableVariantName] } - const selectors = [selector, applicableVariant, ...applicableStates] + let realSelector + if (isParent) { + realSelector = selector + } else { + if (outOfTreeSelector) realSelector = outOfTreeSelector + else realSelector = selector + } + + const selectors = [realSelector, applicableVariant, ...applicableStates] .toSorted((a, b) => { if (a.startsWith(':')) return 1 - else return -1 + if (!a.startsWith('.')) return -1 + else return 0 }) .join('') if (rule.parent) { - return ruleToSelector(rule.parent) + ' ' + selectors + return ruleToSelector(rule.parent, true) + ' ' + selectors } return selectors } -export const init = (ruleset) => { +export const init = (extraRuleset, palette) => { const rootName = root.name const rules = [] const rulesByComponent = {} + const ruleset = [ + ...Object.values(components).map(c => c.defaultRules || []).reduce((acc, arr) => [...acc, ...arr], []), + ...extraRuleset + ] + const addRule = (rule) => { rules.push(rule) rulesByComponent[rule.component] = rulesByComponent[rule.component] || [] rulesByComponent[rule.component].push(rule) } - const findRules = (combination) => rule => { - if (combination.component !== rule.component) return false - if (Object.prototype.hasOwnProperty.call(rule, 'variant')) { - if (combination.variant !== rule.variant) return false - } else { - if (combination.variant !== 'normal') return false + const findRules = (combination, parent) => rule => { + // inexact search + const doesCombinationMatch = () => { + if (combination.component !== rule.component) return false + if (Object.prototype.hasOwnProperty.call(rule, 'variant')) { + if (combination.variant !== rule.variant) return false + } else { + if (combination.variant !== 'normal') return false + } + + if (Object.prototype.hasOwnProperty.call(rule, 'state')) { + const ruleStatesSet = new Set(['normal', ...(rule.state || [])]) + const combinationSet = new Set(['normal', ...combination.state]) + const setsAreEqual = combination.state.every(state => ruleStatesSet.has(state)) && + [...ruleStatesSet].every(state => combinationSet.has(state)) + return setsAreEqual + } else { + if (combination.state.length !== 1 || combination.state[0] !== 'normal') return false + return true + } } + const combinationMatches = doesCombinationMatch() + if (!parent || !combinationMatches) return combinationMatches - if (Object.prototype.hasOwnProperty.call(rule, 'state')) { - const ruleStatesSet = new Set(['normal', ...(rule.state || [])]) - const combinationSet = new Set(['normal', ...combination.state]) - const setsAreEqual = combination.state.every(state => ruleStatesSet.has(state)) && - [...ruleStatesSet].every(state => combinationSet.has(state)) - return setsAreEqual - } else { - if (combination.state.length !== 1 || combination.state[0] !== 'normal') return false - return true + // exact search + + // unroll parents into array + const unroll = (item) => { + const out = [] + let currentParent = item.parent + while (currentParent) { + const { parent: newParent, ...rest } = currentParent + out.push(rest) + currentParent = newParent + } + return out } + const { parent: _, ...rest } = parent + const pathSearch = [rest, ...unroll(parent)] + const pathRule = unroll(rule) + if (pathSearch.length !== pathRule.length) return false + const pathsMatch = pathSearch.every((searchRule, i) => { + const existingRule = pathRule[i] + if (existingRule.component !== searchRule.component) return false + if (existingRule.variant !== searchRule.variant) return false + const existingRuleStatesSet = new Set(['normal', ...(existingRule.state || [])]) + const searchStatesSet = new Set(['normal', ...(searchRule.state || [])]) + const setsAreEqual = existingRule.state.every(state => searchStatesSet.has(state)) && + [...searchStatesSet].every(state => existingRuleStatesSet.has(state)) + return setsAreEqual + }) + return pathsMatch } const findLowerLevelRule = (parent, filter = () => true) => { let lowerLevelComponent = null let currentParent = parent while (currentParent) { - const rulesParent = ruleset.filter(findRules(currentParent, true)) - rulesParent > 1 && console.log('OOPS') + const rulesParent = ruleset.filter(findRules(currentParent)) + rulesParent > 1 && console.warn('OOPS') lowerLevelComponent = rulesParent[rulesParent.length - 1] currentParent = currentParent.parent if (lowerLevelComponent && filter(lowerLevelComponent)) currentParent = null @@ -104,6 +154,35 @@ export const init = (ruleset) => { return filter(lowerLevelComponent) ? lowerLevelComponent : null } + const findColor = (color) => { + if (typeof color === 'string' && color.startsWith('--')) { + const name = color.substring(2) + return palette[name] + } + return color + } + + const getTextColorAlpha = (rule, lowerRule, value) => { + const opacity = rule.directives.textOpacity + const textColor = convert(findColor(value)).rgb + if (opacity === null || opacity === undefined || opacity >= 1) { + return convert(textColor).hex + } + const backgroundColor = convert(lowerRule.cache.background).rgb + if (opacity === 0) { + return convert(backgroundColor).hex + } + const opacityMode = rule.directives.textOpacityMode + switch (opacityMode) { + case 'fake': + return convert(alphaBlend(textColor, opacity, backgroundColor)).hex + case 'mixrgb': + return convert(mixrgb(backgroundColor, textColor)).hex + default: + return rgba2css({ a: opacity, ...textColor }) + } + } + const processInnerComponent = (component, parent) => { const { validInnerComponents = [], @@ -124,79 +203,156 @@ export const init = (ruleset) => { const VIRTUAL_COMPONENTS = new Set(['Text', 'Link', 'Icon']) stateVariantCombination.forEach(combination => { - const existingRules = ruleset.filter(findRules({ component: component.name, ...combination })) - const lastRule = existingRules[existingRules.length - 1] + let needRuleAdd = false - if (existingRules.length !== 0) { - const { directives } = lastRule - const rgb = convert(directives.background).rgb + if (VIRTUAL_COMPONENTS.has(component.name)) { + const selector = component.name + ruleToSelector({ component: component.name, ...combination }) + const virtualName = [ + '--', + component.name.toLowerCase(), + combination.variant === 'normal' + ? '' + : combination.variant[0].toUpperCase() + combination.variant.slice(1).toLowerCase(), + ...combination.state.filter(x => x !== 'normal').toSorted().map(state => state[0].toUpperCase() + state.slice(1).toLowerCase()) + ].join('') - // TODO: DEFAULT TEXT COLOR - const bg = findLowerLevelRule(parent)?.cache.background || convert('#FFFFFF').rgb + const lowerLevel = findLowerLevelRule(parent, (r) => { + if (components[r.component].validInnerComponents.indexOf(component.name) < 0) return false + if (r.cache.background === undefined) return false + if (r.cache.textDefined) { + return !r.cache.textDefined[selector] + } + return true + }) - if (!lastRule.cache?.background) { - const blend = directives.opacity < 1 ? alphaBlend(rgb, directives.opacity, bg) : rgb - lastRule.cache = lastRule.cache || {} - lastRule.cache.background = blend + if (!lowerLevel) return + + let inheritedTextColorRule + const inheritedTextColorRules = findLowerLevelRule(parent, (r) => { + return r.cache?.textDefined?.[selector] + }) + + if (!inheritedTextColorRule) { + const generalTextColorRules = ruleset.filter(findRules({ component: component.name, ...combination }, null, true)) + inheritedTextColorRule = generalTextColorRules[generalTextColorRules.length - 1] + } else { + inheritedTextColorRule = inheritedTextColorRules[inheritedTextColorRules.length - 1] + } - addRule(lastRule) + let inheritedTextColor + let inheritedTextOpacity = {} + if (inheritedTextColorRule) { + inheritedTextColor = findColor(inheritedTextColorRule.directives.textColor) + // also inherit opacity settings + const { textOpacity, textOpacityMode } = inheritedTextColorRule.directives + inheritedTextOpacity = { textOpacity, textOpacityMode } + } else { + // Emergency fallback + inheritedTextColor = '#000000' } + + const textColor = getTextColor( + convert(lowerLevel.cache.background).rgb, + convert(inheritedTextColor).rgb, + component.name === 'Link' // make it configurable? + ) + + lowerLevel.cache.textDefined = lowerLevel.cache.textDefined || {} + lowerLevel.cache.textDefined[selector] = textColor + lowerLevel.virtualDirectives = lowerLevel.virtualDirectives || {} + lowerLevel.virtualDirectives[virtualName] = getTextColorAlpha(inheritedTextColorRule, lowerLevel, textColor) + + const directives = { + textColor, + ...inheritedTextOpacity + } + + // Debug: lets you see what it think background color should be + directives.background = convert(lowerLevel.cache.background).hex + + addRule({ + parent, + virtual: true, + component: component.name, + ...combination, + cache: { background: lowerLevel.cache.background }, + directives + }) } else { - if (VIRTUAL_COMPONENTS.has(component.name)) { - const selector = component.name + ruleToSelector({ component: component.name, ...combination }) - - const lowerLevel = findLowerLevelRule(parent, (r) => { - if (components[r.component].validInnerComponents.indexOf(component.name) < 0) return false - if (r.cache?.background === undefined) return false - if (r.cache.textDefined) { - return !r.cache.textDefined[selector] - } - return true - }) - if (!lowerLevel) return - lowerLevel.cache.textDefined = lowerLevel.cache.textDefined || {} - lowerLevel.cache.textDefined[selector] = true - addRule({ - parent, - component: component.name, - ...combination, - directives: { - // TODO: DEFAULT TEXT COLOR - textColor: getTextColor(convert(lowerLevel.cache.background).rgb, convert('#FFFFFF').rgb, component.name === 'Link'), - // Debug: lets you see what it think background color should be - background: convert(lowerLevel.cache.background).hex + const existingGlobalRules = ruleset.filter(findRules({ component: component.name, ...combination }, null)) + const existingRules = ruleset.filter(findRules({ component: component.name, ...combination }, parent)) + + // Global (general) rules + if (existingGlobalRules.length !== 0) { + const lastRule = existingGlobalRules[existingGlobalRules.length - 1] + const { directives } = lastRule + lastRule.cache = lastRule.cache || {} + + if (directives.background) { + const rgb = convert(findColor(directives.background)).rgb + + // TODO: DEFAULT TEXT COLOR + const bg = findLowerLevelRule(parent)?.cache.background || convert('#FFFFFF').rgb + + if (!lastRule.cache.background) { + const blend = directives.opacity < 1 ? alphaBlend(rgb, directives.opacity, bg) : rgb + lastRule.cache.background = blend + + needRuleAdd = true } - }) + } + + if (needRuleAdd) { + addRule(lastRule) + } } - } + if (existingRules.length !== 0) { + console.warn('MORE EXISTING RULES', existingRules) + } + } innerComponents.forEach(innerComponent => processInnerComponent(innerComponent, { parent, component: name, ...combination })) }) } processInnerComponent(components[rootName]) - // console.info(rules.map(x => [ - // (parent?.component || 'root') + ' -> ' + x.component, - // // 'Cached background:' + convert(bg).hex, - // // 'Color: ' + convert(x.directives.background).hex + ' A:' + x.directives.opacity, - // JSON.stringify(x.directives) - // // '=> Blend: ' + convert(x.cache.background).hex - // ].join(' '))) - return { raw: rules, css: rules.map(rule => { - const header = ruleToSelector(rule) + ' {' + if (rule.virtual) return '' + + let selector = ruleToSelector(rule).replace(/\/\*.*\*\//g, '') + if (!selector) { + selector = 'body' + } + const header = selector + ' {' const footer = '}' + + const virtualDirectives = Object.entries(rule.virtualDirectives || {}).map(([k, v]) => { + return ' ' + k + ': ' + v + }).join(';\n') + const directives = Object.entries(rule.directives).map(([k, v]) => { switch (k) { - case 'background': return 'background-color: ' + rgba2css({ ...convert(v).rgb, a: rule.directives.opacity ?? 1 }) - case 'textColor': return 'color: ' + rgba2css({ ...convert(v).rgb, a: rule.directives.opacity ?? 1 }) + case 'background': { + return 'background-color: ' + rgba2css({ ...convert(findColor(v)).rgb, a: rule.directives.opacity ?? 1 }) + } + case 'textColor': { + return 'color: ' + v + } default: return '' } }).filter(x => x).map(x => ' ' + x).join(';\n') - return [header, directives, footer].join('\n') - }) + + return [ + header, + directives + ';', + ' color: var(--text);', + '', + virtualDirectives, + footer + ].join('\n') + }).filter(x => x) } } diff --git a/test/unit/specs/services/theme_data/theme_data3.spec.js b/test/unit/specs/services/theme_data/theme_data3.spec.js index 915bb5ce..0d5870cc 100644 --- a/test/unit/specs/services/theme_data/theme_data3.spec.js +++ b/test/unit/specs/services/theme_data/theme_data3.spec.js @@ -17,7 +17,7 @@ describe.only('Theme Data 3', () => { describe('init', () => { it('test simple case', () => { - const out = init(sampleRules) + const out = init(sampleRules, palette) // console.log(JSON.stringify(out, null, 2)) console.log('\n' + out.css.join('\n') + '\n') }) -- cgit v1.2.3-70-g09d2 From d4795d2e3c363065319a978fd2d9237fb62f2fe6 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 1 Feb 2024 01:27:30 +0200 Subject: moved default rules to component style.js files, added some basic text inheritance --- src/components/button.style.js | 15 ++++++ src/components/link.style.js | 1 - src/components/panel.style.js | 8 +++ src/components/panel_header.style.js | 9 ++++ src/components/text.style.js | 10 ---- src/components/underlay.style.js | 11 ++++ src/services/theme_data/pleromafe.t3.js | 38 -------------- src/services/theme_data/theme_data_3.service.js | 69 ++++++++++++++++--------- 8 files changed, 89 insertions(+), 72 deletions(-) (limited to 'src/components/text.style.js') diff --git a/src/components/button.style.js b/src/components/button.style.js index 51f781e1..49147c8d 100644 --- a/src/components/button.style.js +++ b/src/components/button.style.js @@ -15,5 +15,20 @@ export default { validInnerComponents: [ 'Text', 'Icon' + ], + defaultRules: [ + { + component: 'Button', + directives: { + background: '--fg' + } + }, + { + component: 'Button', + state: ['hover'], + directives: { + background: '#FFFFFF' + } + } ] } diff --git a/src/components/link.style.js b/src/components/link.style.js index 0128fd91..d13cef33 100644 --- a/src/components/link.style.js +++ b/src/components/link.style.js @@ -16,7 +16,6 @@ export default { component: 'Link', state: ['faint'], directives: { - textColor: '--link', textOpacity: 0.5, textOpacityMode: 'fake' } diff --git a/src/components/panel.style.js b/src/components/panel.style.js index 870f5099..a9de7e56 100644 --- a/src/components/panel.style.js +++ b/src/components/panel.style.js @@ -7,5 +7,13 @@ export default { 'Icon', 'Button', 'PanelHeader' + ], + defaultRules: [ + { + component: 'Panel', + directives: { + background: '--fg' + } + } ] } diff --git a/src/components/panel_header.style.js b/src/components/panel_header.style.js index e83e1f15..01f8a67f 100644 --- a/src/components/panel_header.style.js +++ b/src/components/panel_header.style.js @@ -6,5 +6,14 @@ export default { 'Link', 'Icon', 'Button' + ], + defaultRules: [ + { + component: 'PanelHeader', + directives: { + background: '--fg' + // opacity: 0.9 + } + } ] } diff --git a/src/components/text.style.js b/src/components/text.style.js index e52b6f68..18472032 100644 --- a/src/components/text.style.js +++ b/src/components/text.style.js @@ -19,7 +19,6 @@ export default { component: 'Text', state: ['faint'], directives: { - textColor: '--text', textOpacity: 0.5 } }, @@ -29,15 +28,6 @@ export default { directives: { textColor: '--cGreen' } - }, - { - component: 'Text', - variant: 'greentext', - state: ['faint'], - directives: { - textColor: '--cGreen', - textOpacity: 0.5 - } } ] } diff --git a/src/components/underlay.style.js b/src/components/underlay.style.js index c35fbada..380ea26e 100644 --- a/src/components/underlay.style.js +++ b/src/components/underlay.style.js @@ -4,5 +4,16 @@ export default { outOfTreeSelector: '.underlay', validInnerComponents: [ 'Panel' + ], + defaultRules: [ + { + component: 'Underlay', + // variant: 'normal', + // state: 'normal' + directives: { + background: '#000000', + opacity: 0.2 + } + } ] } diff --git a/src/services/theme_data/pleromafe.t3.js b/src/services/theme_data/pleromafe.t3.js index 9fadf0ee..db612a5b 100644 --- a/src/services/theme_data/pleromafe.t3.js +++ b/src/services/theme_data/pleromafe.t3.js @@ -1,40 +1,2 @@ export const sampleRules = [ - { - component: 'Underlay', - // variant: 'normal', - // state: 'normal' - directives: { - background: '#000000', - opacity: 0.2 - } - }, - { - component: 'Panel', - directives: { - background: '--fg' - // opacity: 0.9 - } - }, - { - component: 'PanelHeader', - directives: { - background: '--fg' - // opacity: 0.9 - } - }, - { - component: 'Button', - directives: { - background: '--fg' - // opacity: 0.8 - } - }, - { - component: 'Button', - state: ['hover'], - directives: { - background: '#FFFFFF' - // opacity: 0.9 - } - } ] diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js index c9468d07..b7679021 100644 --- a/src/services/theme_data/theme_data_3.service.js +++ b/src/services/theme_data/theme_data_3.service.js @@ -1,5 +1,6 @@ -import { convert } from 'chromatism' -import { alphaBlend, getTextColor, rgba2css, mixrgb } from '../color_convert/color_convert.js' +import { convert, brightness } from 'chromatism' +import merge from 'lodash.merge' +import { alphaBlend, getTextColor, rgba2css, mixrgb, relativeLuminance } from '../color_convert/color_convert.js' import Underlay from 'src/components/underlay.style.js' import Panel from 'src/components/panel.style.js' @@ -87,27 +88,35 @@ export const init = (extraRuleset, palette) => { rulesByComponent[rule.component].push(rule) } - const findRules = (combination, parent) => rule => { + const findRules = (searchCombination, parent) => rule => { // inexact search const doesCombinationMatch = () => { - if (combination.component !== rule.component) return false - if (Object.prototype.hasOwnProperty.call(rule, 'variant')) { - if (combination.variant !== rule.variant) return false + if (searchCombination.component !== rule.component) return false + const ruleVariant = Object.prototype.hasOwnProperty.call(rule, 'variant') ? rule.variant : 'normal' + + if (ruleVariant !== 'normal') { + if (searchCombination.variant !== rule.variant) return false + } + + const ruleHasStateDefined = Object.prototype.hasOwnProperty.call(rule, 'state') + let ruleStateSet + if (ruleHasStateDefined) { + ruleStateSet = new Set(['normal', ...rule.state]) } else { - if (combination.variant !== 'normal') return false + ruleStateSet = new Set(['normal']) } - if (Object.prototype.hasOwnProperty.call(rule, 'state')) { - const ruleStatesSet = new Set(['normal', ...(rule.state || [])]) - const combinationSet = new Set(['normal', ...combination.state]) - const setsAreEqual = combination.state.every(state => ruleStatesSet.has(state)) && + if (ruleStateSet.size > 1) { + const ruleStatesSet = ruleStateSet + const combinationSet = new Set(['normal', ...searchCombination.state]) + const setsAreEqual = searchCombination.state.every(state => ruleStatesSet.has(state)) && [...ruleStatesSet].every(state => combinationSet.has(state)) return setsAreEqual } else { - if (combination.state.length !== 1 || combination.state[0] !== 'normal') return false return true } } + const combinationMatches = doesCombinationMatch() if (!parent || !combinationMatches) return combinationMatches @@ -154,21 +163,31 @@ export const init = (extraRuleset, palette) => { return filter(lowerLevelComponent) ? lowerLevelComponent : null } - const findColor = (color) => { - if (typeof color === 'string' && color.startsWith('--')) { - const name = color.substring(2) - return palette[name] + const findColor = (color, background) => { + if (typeof color !== 'string' || !color.startsWith('--')) return color + let targetColor = null + // Color references other color + const [variable, modifier] = color.split(/,/g).map(str => str.trim()) + const variableSlot = variable.substring(2) + targetColor = palette[variableSlot] + + if (modifier) { + const effectiveBackground = background ?? targetColor + const isLightOnDark = relativeLuminance(convert(effectiveBackground).rgb) < 0.5 + const mod = isLightOnDark ? 1 : -1 + targetColor = brightness(Number.parseFloat(modifier) * mod, targetColor).rgb } - return color + + return targetColor } const getTextColorAlpha = (rule, lowerRule, value) => { const opacity = rule.directives.textOpacity - const textColor = convert(findColor(value)).rgb + const backgroundColor = convert(lowerRule.cache.background).rgb + const textColor = convert(findColor(value, backgroundColor)).rgb if (opacity === null || opacity === undefined || opacity >= 1) { return convert(textColor).hex } - const backgroundColor = convert(lowerRule.cache.background).rgb if (opacity === 0) { return convert(backgroundColor).hex } @@ -217,6 +236,7 @@ export const init = (extraRuleset, palette) => { ].join('') const lowerLevel = findLowerLevelRule(parent, (r) => { + if (!r) return false if (components[r.component].validInnerComponents.indexOf(component.name) < 0) return false if (r.cache.background === undefined) return false if (r.cache.textDefined) { @@ -234,15 +254,15 @@ export const init = (extraRuleset, palette) => { if (!inheritedTextColorRule) { const generalTextColorRules = ruleset.filter(findRules({ component: component.name, ...combination }, null, true)) - inheritedTextColorRule = generalTextColorRules[generalTextColorRules.length - 1] + inheritedTextColorRule = generalTextColorRules.reduce((acc, rule) => merge(acc, rule), {}) } else { - inheritedTextColorRule = inheritedTextColorRules[inheritedTextColorRules.length - 1] + inheritedTextColorRule = inheritedTextColorRules.reduce((acc, rule) => merge(acc, rule), {}) } let inheritedTextColor let inheritedTextOpacity = {} if (inheritedTextColorRule) { - inheritedTextColor = findColor(inheritedTextColorRule.directives.textColor) + inheritedTextColor = findColor(inheritedTextColorRule.directives.textColor, convert(lowerLevel.cache.background).rgb) // also inherit opacity settings const { textOpacity, textOpacityMode } = inheritedTextColorRule.directives inheritedTextOpacity = { textOpacity, textOpacityMode } @@ -284,8 +304,11 @@ export const init = (extraRuleset, palette) => { // Global (general) rules if (existingGlobalRules.length !== 0) { + const totalRule = existingGlobalRules.reduce((acc, rule) => merge(acc, rule), {}) + const { directives } = totalRule + + // last rule is used as a cache const lastRule = existingGlobalRules[existingGlobalRules.length - 1] - const { directives } = lastRule lastRule.cache = lastRule.cache || {} if (directives.background) { -- cgit v1.2.3-70-g09d2 From a9efbd2553119598e615f718037ea57696d023f6 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 7 Feb 2024 16:09:29 +0200 Subject: add directive to preserve or not the text color --- src/components/text.style.js | 6 ++++-- src/services/theme_data/theme_data_3.service.js | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'src/components/text.style.js') diff --git a/src/components/text.style.js b/src/components/text.style.js index 18472032..905dbbee 100644 --- a/src/components/text.style.js +++ b/src/components/text.style.js @@ -12,7 +12,8 @@ export default { { component: 'Text', directives: { - textColor: '--text' + textColor: '--text', + textAuto: 'no-preserve' } }, { @@ -26,7 +27,8 @@ export default { component: 'Text', variant: 'greentext', directives: { - textColor: '--cGreen' + textColor: '--cGreen', + textAuto: 'preserve' } } ] diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js index 1228efc2..c1f8c730 100644 --- a/src/services/theme_data/theme_data_3.service.js +++ b/src/services/theme_data/theme_data_3.service.js @@ -365,6 +365,7 @@ export const init = (extraRuleset, palette) => { ].join('') let inheritedTextColor = computedDirectives.textColor + let inheritedTextAuto = computedDirectives.textAuto let inheritedTextOpacity = computedDirectives.textOpacity let inheritedTextOpacityMode = computedDirectives.textOpacityMode const lowerLevelTextSelector = [...selector.split(/ /g).slice(0, -1), soloSelector].join(' ') @@ -372,6 +373,7 @@ export const init = (extraRuleset, palette) => { if (inheritedTextColor == null || inheritedTextOpacity == null || inheritedTextOpacityMode == null) { inheritedTextColor = computedDirectives.textColor ?? lowerLevelTextRule.textColor + inheritedTextAuto = computedDirectives.textAuto ?? lowerLevelTextRule.textAuto inheritedTextOpacity = computedDirectives.textOpacity ?? lowerLevelTextRule.textOpacity inheritedTextOpacityMode = computedDirectives.textOpacityMode ?? lowerLevelTextRule.textOpacityMode } @@ -381,6 +383,7 @@ export const init = (extraRuleset, palette) => { directives: { ...computedRule.directives, textColor: inheritedTextColor, + textAuto: inheritedTextAuto ?? 'preserve', textOpacity: inheritedTextOpacity, textOpacityMode: inheritedTextOpacityMode } @@ -393,7 +396,7 @@ export const init = (extraRuleset, palette) => { convert(lowerLevelBackground).rgb, // TODO properly provide "parent" text color? convert(findColor(inheritedTextColor, null, lowerLevelBackground)).rgb, - true // component.name === 'Link' || combination.variant === 'greentext' // make it configurable? + newTextRule.directives.textAuto === 'preserve' ) // Storing color data in lower layer to use as custom css properties @@ -444,8 +447,6 @@ export const init = (extraRuleset, palette) => { // TODO: DEFAULT TEXT COLOR const bg = cache[lowerLevelSelector]?.background || convert('#FFFFFF').rgb - console.log('SELECTOR', lowerLevelSelector) - const rgb = convert(findColor(computedDirectives.background, inheritedBackground, cache[lowerLevelSelector].background)).rgb if (!cache[selector].background) { -- cgit v1.2.3-70-g09d2 From 48f106b438e22d8289904c9f474819ad7b85d0e8 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 12 Feb 2024 19:17:17 +0200 Subject: separate greentext into "fun text" and make Post/Notification related components --- src/App.scss | 2 -- src/components/fun_text.style.js | 40 ++++++++++++++++++++++++ src/components/notification.style.js | 21 +++++++++++++ src/components/notification/notification.scss | 3 +- src/components/panel.style.js | 4 ++- src/components/popover.style.js | 3 +- src/components/post.style.js | 21 +++++++++++++ src/components/rich_content.style.js | 17 ++++++++++ src/components/rich_content/rich_content.scss | 8 +++++ src/components/status_body/status_body.scss | 8 ----- src/components/status_content/status_content.vue | 10 ++++++ src/components/text.style.js | 13 -------- src/services/theme_data/theme_data_3.service.js | 13 ++++++-- 13 files changed, 134 insertions(+), 29 deletions(-) create mode 100644 src/components/fun_text.style.js create mode 100644 src/components/notification.style.js create mode 100644 src/components/post.style.js create mode 100644 src/components/rich_content.style.js (limited to 'src/components/text.style.js') diff --git a/src/App.scss b/src/App.scss index 27fee4b4..6a634926 100644 --- a/src/App.scss +++ b/src/App.scss @@ -673,8 +673,6 @@ option { .faint { --text: var(--textFaint); - --textGreentext: var(--textGreentextFaint); - --textCyantext: var(--textCyantextFaint); --link: var(--linkFaint); color: var(--text); diff --git a/src/components/fun_text.style.js b/src/components/fun_text.style.js new file mode 100644 index 00000000..2d3ac154 --- /dev/null +++ b/src/components/fun_text.style.js @@ -0,0 +1,40 @@ +export default { + name: 'FunText', + selector: '/*fun-text*/', + virtual: true, + variants: { + greentext: '.greentext', + cyantext: '.cyantext' + }, + states: { + faint: '.faint' + }, + defaultRules: [ + { + directives: { + textColor: '--text', + textAuto: 'preserve' + } + }, + { + state: ['faint'], + directives: { + textOpacity: 0.5 + } + }, + { + variant: 'greentext', + directives: { + textColor: '--cGreen', + textAuto: 'preserve' + } + }, + { + variant: 'cyantext', + directives: { + textColor: '--cBlue', + textAuto: 'preserve' + } + } + ] +} diff --git a/src/components/notification.style.js b/src/components/notification.style.js new file mode 100644 index 00000000..057b1c7a --- /dev/null +++ b/src/components/notification.style.js @@ -0,0 +1,21 @@ +export default { + name: 'Notification', + selector: '.Notification', + validInnerComponents: [ + 'Text', + 'Link', + 'Icon', + 'Border', + 'Button', + 'ButtonUnstyled', + 'RichContent', + 'Input' + ], + defaultRules: [ + { + directives: { + background: '--bg' + } + } + ] +} diff --git a/src/components/notification/notification.scss b/src/components/notification/notification.scss index 654aca3c..86d3e31d 100644 --- a/src/components/notification/notification.scss +++ b/src/components/notification/notification.scss @@ -3,8 +3,7 @@ // TODO Copypaste from Status, should unify it somehow .Notification { border-bottom: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); + border-color: var(--border); word-wrap: break-word; word-break: break-word; diff --git a/src/components/panel.style.js b/src/components/panel.style.js index ce9812a6..1e8039c6 100644 --- a/src/components/panel.style.js +++ b/src/components/panel.style.js @@ -9,7 +9,9 @@ export default { 'ButtonUnstyled', 'Input', 'PanelHeader', - 'MenuItem' + 'MenuItem', + 'Post', + 'Notification' ], defaultRules: [ { diff --git a/src/components/popover.style.js b/src/components/popover.style.js index a3a554fd..6a121cae 100644 --- a/src/components/popover.style.js +++ b/src/components/popover.style.js @@ -13,7 +13,8 @@ export default { 'ButtonUnstyled', 'Input', 'PanelHeader', - 'MenuItem' + 'MenuItem', + 'Post' ], defaultRules: [ { diff --git a/src/components/post.style.js b/src/components/post.style.js new file mode 100644 index 00000000..92ec230a --- /dev/null +++ b/src/components/post.style.js @@ -0,0 +1,21 @@ +export default { + name: 'Post', + selector: '.Status', + validInnerComponents: [ + 'Text', + 'Link', + 'Icon', + 'Border', + 'Button', + 'ButtonUnstyled', + 'RichContent', + 'Input' + ], + defaultRules: [ + { + directives: { + background: '--bg' + } + } + ] +} diff --git a/src/components/rich_content.style.js b/src/components/rich_content.style.js new file mode 100644 index 00000000..aaf126a5 --- /dev/null +++ b/src/components/rich_content.style.js @@ -0,0 +1,17 @@ +export default { + name: 'RichContent', + selector: '.RichContent', + validInnerComponents: [ + 'Text', + 'FunText', + 'Link' + ], + defaultRules: [ + { + directives: { + background: '--bg', + textNoCssColor: 'yes' + } + } + ] +} diff --git a/src/components/rich_content/rich_content.scss b/src/components/rich_content/rich_content.scss index e5d353ac..3bb5b16b 100644 --- a/src/components/rich_content/rich_content.scss +++ b/src/components/rich_content/rich_content.scss @@ -65,4 +65,12 @@ vertical-align: middle; object-fit: contain; } + + .greentext { + color: var(--funtextGreentext); + } + + .cyantext { + color: var(--funtextCyantext); + } } diff --git a/src/components/status_body/status_body.scss b/src/components/status_body/status_body.scss index a8878745..930ed803 100644 --- a/src/components/status_body/status_body.scss +++ b/src/components/status_body/status_body.scss @@ -112,14 +112,6 @@ } } - .greentext { - color: var(--textGreentext); - } - - .cyantext { - color: var(--textCyantext); - } - &.-compact { align-items: top; flex-direction: row; diff --git a/src/components/status_content/status_content.vue b/src/components/status_content/status_content.vue index e977d489..a9db6b12 100644 --- a/src/components/status_content/status_content.vue +++ b/src/components/status_content/status_content.vue @@ -62,5 +62,15 @@ .StatusContent { flex: 1; min-width: 0; + + &.faint { + .greentext { + color: var(--funtextGreentextFaint); + } + + .cyantext { + color: var(--funtextCyantextFaint); + } + } } diff --git a/src/components/text.style.js b/src/components/text.style.js index 905dbbee..a254ceb4 100644 --- a/src/components/text.style.js +++ b/src/components/text.style.js @@ -2,34 +2,21 @@ export default { name: 'Text', selector: '/*text*/', virtual: true, - variants: { - greentext: '.greentext' - }, states: { faint: '.faint' }, defaultRules: [ { - component: 'Text', directives: { textColor: '--text', textAuto: 'no-preserve' } }, { - component: 'Text', state: ['faint'], directives: { textOpacity: 0.5 } - }, - { - component: 'Text', - variant: 'greentext', - directives: { - textColor: '--cGreen', - textAuto: 'preserve' - } } ] } diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js index 0d205c73..9cab6dd4 100644 --- a/src/services/theme_data/theme_data_3.service.js +++ b/src/services/theme_data/theme_data_3.service.js @@ -19,9 +19,13 @@ import Button from 'src/components/button.style.js' import ButtonUnstyled from 'src/components/button_unstyled.style.js' import Input from 'src/components/input.style.js' import Text from 'src/components/text.style.js' +import FunText from 'src/components/fun_text.style.js' import Link from 'src/components/link.style.js' import Icon from 'src/components/icon.style.js' import Border from 'src/components/border.style.js' +import Post from 'src/components/post.style.js' +import Notification from 'src/components/notification.style.js' +import RichContent from 'src/components/rich_content.style.js' const DEBUG = false @@ -39,6 +43,7 @@ export const DEFAULT_SHADOWS = { const components = { Root, Text, + FunText, Link, Icon, Border, @@ -51,7 +56,10 @@ const components = { TopBar, Button, ButtonUnstyled, - Input + Input, + Post, + Notification, + RichContent } // "Unrolls" a tree structure of item: { parent: { ...item2, parent: { ...item3, parent: {...} } }} @@ -582,6 +590,7 @@ export const init = (extraRuleset, palette) => { ].join(';\n') } case 'textColor': { + if (rule.directives.textNoCssColor === 'yes') { return '' } return 'color: ' + v } default: return '' @@ -594,7 +603,7 @@ export const init = (extraRuleset, palette) => { return [ header, directives + ';', - !rule.virtual ? ' color: var(--text);' : '', + (!rule.virtual && rule.directives.textNoCssColor !== 'yes') ? ' color: var(--text);' : '', '', virtualDirectives, footer -- cgit v1.2.3-70-g09d2