diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.js b/src/components/settings_modal/tabs/style_tab/style_tab.js index 2a29744d..3b5dba94 100644 --- a/src/components/settings_modal/tabs/style_tab/style_tab.js +++ b/src/components/settings_modal/tabs/style_tab/style_tab.js @@ -60,7 +60,7 @@ export default { ContrastRatio }, setup () { - // ### Meta stuff + // ## Meta stuff const name = ref('') const author = ref('') const license = ref('') @@ -77,7 +77,7 @@ export default { ].join('\n') }) - // ### Palette stuff + // ## Palette stuff const palettes = reactive([ { name: 'light', @@ -105,29 +105,15 @@ export default { cOrange: '#ffa500' } ]) - - const palettesOut = computed(() => { - return palettes.map(({ name, ...palette }) => { - const entries = Object - .entries(palette) - .map(([slot, data]) => ` ${slot}: ${data};`) - .join('\n') - - return `@palette.${name} {\n${entries}\n}` - }).join('\n\n') - }) - - const editedPalette = ref(0) - const palette = computed({ + const selectedPaletteId = ref(0) + const selectedPalette = computed({ get () { - console.log(palettes, editedPalette.value) - return palettes[editedPalette.value] + return palettes[selectedPaletteId.value] }, set (newPalette) { - palettes[editedPalette.value] = newPalette + palettes[selectedPaletteId.value] = newPalette } }) - const getNewPalette = () => ({ name: 'new palette', bg: '#121a24', @@ -141,11 +127,21 @@ export default { cOrange: '#ffa500' }) - // ### Initialization stuff + const palettesOut = computed(() => { + return palettes.map(({ name, ...palette }) => { + const entries = Object + .entries(palette) + .map(([slot, data]) => ` ${slot}: ${data};`) + .join('\n') + + return `@palette.${name} {\n${entries}\n}` + }).join('\n\n') + }) + + // ## Components stuff // Getting existing components const componentsContext = require.context('src', true, /\.style.js(on)?$/) const componentKeysAll = componentsContext.keys() - const componentsMap = new Map( componentKeysAll .map( @@ -154,23 +150,23 @@ export default { ) const componentKeys = [...componentsMap.keys()] - // Initializing selected component and its computed descendants + // selection basis const selectedComponentKey = ref(componentsMap.keys().next().value) const selectedComponent = computed(() => componentsMap.get(selectedComponentKey.value)) const selectedComponentName = computed(() => selectedComponent.value.name) - - const selectedVariant = ref('normal') - const selectedComponentVariantsAll = computed(() => { + const selectedComponentVariants = computed(() => { return Object.keys({ normal: null, ...(selectedComponent.value.variants || {}) }) }) - - const selectedState = reactive(new Set()) const selectedComponentStatesAll = computed(() => { return Object.keys({ normal: null, ...(selectedComponent.value.states || {}) }) }) const selectedComponentStates = computed(() => { return selectedComponentStatesAll.value.filter(x => x !== 'normal') }) + + // selection + const selectedVariant = ref('normal') + const selectedState = reactive(new Set()) const updateSelectedStates = (state, v) => { if (v) { selectedState.add(state) @@ -179,56 +175,6 @@ export default { } } - // ### Preview stuff - const editorHintStyle = computed(() => { - const editorHint = selectedComponent.value.editor - const styles = [] - if (editorHint && Object.keys(editorHint).length > 0) { - if (editorHint.aspect != null) { - styles.push(`aspect-ratio: ${editorHint.aspect} !important;`) - } - if (editorHint.border != null) { - styles.push(`border-width: ${editorHint.border}px !important;`) - } - } - return styles.join('; ') - }) - - // Apart from "hover" we can't really show how component looks like in - // certain states, so we have to fake them. - const simulatePseudoSelectors = css => css - .replace(selectedComponent.value.selector, '.ComponentPreview .preview-block') - .replace(':active', '.preview-active') - .replace(':hover', '.preview-hover') - .replace(':active', '.preview-active') - .replace(':focus', '.preview-focus') - .replace(':focus-within', '.preview-focus-within') - .replace(':disabled', '.preview-disabled') - - const previewRules = reactive([]) - const previewClass = computed(() => { - const selectors = [] - if (!!selectedComponent.value.variants?.normal || selectedVariant.value !== 'normal') { - selectors.push(selectedComponent.value.variants[selectedVariant.value]) - } - if (selectedState.size > 0) { - selectedState.forEach(state => { - const original = selectedComponent.value.states[state] - selectors.push(simulatePseudoSelectors(original)) - }) - } - return selectors.map(x => x.substring(1)).join('') - }) - const previewCss = computed(() => { - try { - const scoped = getCssRules(previewRules).map(simulatePseudoSelectors) - return scoped.join('\n') - } catch (e) { - console.error('Invalid ruleset', e) - return null - } - }) - // ### Rules stuff aka meat and potatoes // The native structure of separate rules and the child -> parent // relation isn't very convenient for editor, we replace the array @@ -334,21 +280,58 @@ export default { // All the editable stuff for the component const editedBackgroundColor = getEditedElement(null, 'background') + const isBackgroundColorPresent = isElementPresent(null, 'background', '#FFFFFF') const editedOpacity = getEditedElement(null, 'opacity') + const isOpacityPresent = isElementPresent(null, 'opacity', 1) const editedTextColor = getEditedElement('Text', 'textColor') + const isTextColorPresent = isElementPresent('Text', 'textColor', '#000000') const editedTextAuto = getEditedElement('Text', 'textAuto') + const isTextAutoPresent = isElementPresent('Text', 'textAuto', '#000000') const editedLinkColor = getEditedElement('Link', 'textColor') + const isLinkColorPresent = isElementPresent('Link', 'textColor', '#000080') const editedIconColor = getEditedElement('Icon', 'textColor') - const editedShadow = getEditedElement(null, 'shadow') + const isIconColorPresent = isElementPresent('Icon', 'textColor', '#909090') + // TODO this is VERY primitive right now, need to make it + // support variables, fallbacks etc. + const getContrast = (bg, text) => { + try { + const bgRgb = hex2rgb(bg) + const textRgb = hex2rgb(text) + + const ratio = getContrastRatio(bgRgb, textRgb) + return { + // TODO this ideally should be part of + ratio, + text: ratio.toPrecision(3) + ':1', + // AA level, AAA level + aa: ratio >= 4.5, + aaa: ratio >= 7, + // same but for 18pt+ texts + laa: ratio >= 3, + laaa: ratio >= 4.5 + } + } catch (e) { + console.warn('Failure computing contrast', e) + return { error: e } + } + } // Shadow is partially edited outside the ShadowControl // for better space utilization + const editedShadow = getEditedElement(null, 'shadow') const editedSubShadowId = ref(null) const editedSubShadow = computed(() => { if (editedShadow.value == null || editedSubShadowId.value == null) return null return editedShadow.value[editedSubShadowId.value] }) - + const isShadowPresent = isElementPresent(null, 'shadow', []) + const onSubShadow = (id) => { + if (id != null) { + editedSubShadowId.value = id + } else { + editedSubShadow.value = null + } + } const updateSubShadow = (axis, value) => { if (!editedSubShadow.value || editedSubShadowId.value == null) return const newEditedShadow = [...editedShadow.value] @@ -360,26 +343,105 @@ export default { editedShadow.value = newEditedShadow } - - const onSubShadow = (id) => { - if (id != null) { - editedSubShadowId.value = id - } else { - editedSubShadow.value = null - } + const isShadowTabOpen = ref(false) + const onTabSwitch = (tab) => { + isShadowTabOpen.value = tab === 'shadow' } - // Whether specific directives present in the edited rule or not - // Somewhat serves double-duty as it creates/removes the directive - // when set - const isBackgroundColorPresent = isElementPresent(null, 'background', '#FFFFFF') - const isOpacityPresent = isElementPresent(null, 'opacity', 1) - const isTextColorPresent = isElementPresent('Text', 'textColor', '#000000') - const isTextAutoPresent = isElementPresent('Text', 'textAuto', '#000000') - const isLinkColorPresent = isElementPresent('Link', 'textColor', '#000080') - const isIconColorPresent = isElementPresent('Icon', 'textColor', '#909090') - const isShadowPresent = isElementPresent(null, 'shadow', []) + // component preview + const editorHintStyle = computed(() => { + const editorHint = selectedComponent.value.editor + const styles = [] + if (editorHint && Object.keys(editorHint).length > 0) { + if (editorHint.aspect != null) { + styles.push(`aspect-ratio: ${editorHint.aspect} !important;`) + } + if (editorHint.border != null) { + styles.push(`border-width: ${editorHint.border}px !important;`) + } + } + return styles.join('; ') + }) + // Apart from "hover" we can't really show how component looks like in + // certain states, so we have to fake them. + const simulatePseudoSelectors = css => css + .replace(selectedComponent.value.selector, '.ComponentPreview .preview-block') + .replace(':active', '.preview-active') + .replace(':hover', '.preview-hover') + .replace(':active', '.preview-active') + .replace(':focus', '.preview-focus') + .replace(':focus-within', '.preview-focus-within') + .replace(':disabled', '.preview-disabled') + const previewClass = computed(() => { + const selectors = [] + if (!!selectedComponent.value.variants?.normal || selectedVariant.value !== 'normal') { + selectors.push(selectedComponent.value.variants[selectedVariant.value]) + } + if (selectedState.size > 0) { + selectedState.forEach(state => { + const original = selectedComponent.value.states[state] + selectors.push(simulatePseudoSelectors(original)) + }) + } + return selectors.map(x => x.substring(1)).join('') + }) + const previewRules = reactive([]) + const previewCss = computed(() => { + try { + const scoped = getCssRules(previewRules).map(simulatePseudoSelectors) + return scoped.join('\n') + } catch (e) { + console.error('Invalid ruleset', e) + return null + } + }) + const updatePreview = () => { + try { + const { name, ...paletteData } = selectedPalette.value + const rules = init({ + inputRuleset: editorFriendlyToOriginal.value, + initialStaticVars: { + ...paletteData + }, + ultimateBackgroundColor: '#000000', + rootComponentName: selectedComponentName.value, + editMode: true, + debug: true + }).eager + previewRules.splice(0, previewRules.length) + previewRules.push(...rules) + } catch (e) { + console.error('Could not compile preview theme', e) + } + } + const updateSelectedComponent = () => { + selectedVariant.value = 'normal' + selectedState.clear() + updatePreview() + } + updateSelectedComponent() + watch( + allEditedRules, + updatePreview + ) + + watch( + palettes, + updatePreview + ) + + watch( + selectedPalette, + updatePreview + ) + + watch( + selectedComponentName, + updateSelectedComponent + ) + + // export and import const editorFriendlyToOriginal = computed(() => { const resultRules = [] @@ -419,83 +481,36 @@ export default { return resultRules }) - const updatePreview = () => { - try { - const { name, ...paletteData } = palette.value - const rules = init({ - inputRuleset: editorFriendlyToOriginal.value, - initialStaticVars: { - ...paletteData - }, - ultimateBackgroundColor: '#000000', - rootComponentName: selectedComponentName.value, - editMode: true, - debug: true - }).eager - previewRules.splice(0, previewRules.length) - previewRules.push(...rules) - } catch (e) { - console.error('Could not compile preview theme', e) - } - } - - const updateSelectedComponent = () => { - selectedVariant.value = 'normal' - selectedState.clear() - updatePreview() - } - updateSelectedComponent() - - watch( - allEditedRules, - updatePreview - ) - - watch( - palettes, - updatePreview - ) - - watch( - editedPalette, - updatePreview - ) - - watch( - selectedComponentName, - updateSelectedComponent - ) - - // TODO this is VERY primitive right now, need to make it - // support variables, fallbacks etc. - const getContrast = (bg, text) => { - try { - const bgRgb = hex2rgb(bg) - const textRgb = hex2rgb(text) - - const ratio = getContrastRatio(bgRgb, textRgb) + // ## Variables + const allCustomVirtualDirectives = [...componentsMap.values()] + .map(c => { + console.log(c.defaultRules.filter(c => c.component === 'Root')) + return c + .defaultRules + .filter(c => c.component === 'Root') + .map(x => Object.entries(x.directives)) + .flat() + }) + .filter(x => x) + .flat() + .map(([name, value]) => { + const [valType, valVal] = value.split('|') return { - // TODO this ideally should be part of - ratio, - text: ratio.toPrecision(3) + ':1', - // AA level, AAA level - aa: ratio >= 4.5, - aaa: ratio >= 7, - // same but for 18pt+ texts - laa: ratio >= 3, - laaa: ratio >= 4.5 + name: name.substring(2), + valType: valType.trim(), + value: valVal.trim() } - } catch (e) { - console.warn('Failure computing contrast', e) - return { error: e } - } - } - - const isShadowTabOpen = ref(false) - const onTabSwitch = (tab) => { - isShadowTabOpen.value = tab === 'shadow' - } + }) + const virtualDirectives = reactive(allCustomVirtualDirectives) + const selectedVirtualDirectiveId = ref(0) + const selectedVirtualDirective = computed(() => virtualDirectives[selectedVirtualDirectiveId.value]) + const getNewDirective = () => ({ + name: 'newDirective', + valType: 'generic', + value: 'foobar' + }) + // ## Export and Import const styleExporter = newExporter({ filename: 'pleroma.palette.json', getExportedObject: () => exportStyleData @@ -512,48 +527,73 @@ export default { } return { + // ## Meta name, author, license, website, - palette, + + // ## Palette palettes, - editedPalette, + selectedPalette, + selectedPaletteId, getNewPalette, + + // ## Components componentKeys, componentsMap, + + // selection basis selectedComponent, selectedComponentName, selectedComponentKey, - selectedComponentVariantsAll, + selectedComponentVariants, selectedComponentStates, + + // selection selectedVariant, selectedState, updateSelectedStates, + + // component directives + componentHas, + + // component colors editedBackgroundColor, + isBackgroundColorPresent, editedOpacity, + isOpacityPresent, editedTextColor, + isTextColorPresent, editedTextAuto, + isTextAutoPresent, editedLinkColor, + isLinkColorPresent, editedIconColor, + isIconColorPresent, + getContrast, + + // component shadow editedShadow, editedSubShadow, + isShadowPresent, onSubShadow, updateSubShadow, - getContrast, - isBackgroundColorPresent, - isOpacityPresent, - isTextColorPresent, - isTextAutoPresent, - isLinkColorPresent, - isIconColorPresent, - isShadowPresent, - previewCss, - previewClass, - editorHintStyle, - componentHas, isShadowTabOpen, onTabSwitch, + + // component preview + editorHintStyle, + previewCss, + previewClass, + + // ## Variables + virtualDirectives, + selectedVirtualDirective, + selectedVirtualDirectiveId, + getNewDirective, + + // ## Export and Import exportStyle } } diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.vue b/src/components/settings_modal/tabs/style_tab/style_tab.vue index dff57742..326db5cc 100644 --- a/src/components/settings_modal/tabs/style_tab/style_tab.vue +++ b/src/components/settings_modal/tabs/style_tab/style_tab.vue @@ -76,7 +76,7 @@