diff --git a/src/components/button.style.js b/src/components/button.style.js index 95ef3e403..248da8bbd 100644 --- a/src/components/button.style.js +++ b/src/components/button.style.js @@ -35,11 +35,11 @@ export default { { component: 'Root', directives: { - '--defaultButtonHoverGlow': 'shadow | 0 0 4 --text / 0.5', - '--defaultButtonFocusGlow': 'shadow | 0 0 4 4 --link / 0.5', - '--defaultButtonShadow': 'shadow | 0 0 2 #000000', - '--defaultButtonBevel': 'shadow | $borderSide(#FFFFFF top 0.2 2), $borderSide(#000000 bottom 0.2 2)', - '--pressedButtonBevel': 'shadow | $borderSide(#FFFFFF bottom 0.2 2), $borderSide(#000000 top 0.2 2)' + '--buttonDefaultHoverGlow': 'shadow | 0 0 4 --text / 0.5', + '--buttonDefaultFocusGlow': 'shadow | 0 0 4 4 --link / 0.5', + '--buttonDefaultShadow': 'shadow | 0 0 2 #000000', + '--buttonDefaultBevel': 'shadow | $borderSide(#FFFFFF top 0.2 2), $borderSide(#000000 bottom 0.2 2)', + '--buttonPressedBevel': 'shadow | $borderSide(#FFFFFF bottom 0.2 2), $borderSide(#000000 top 0.2 2)' } }, { @@ -47,53 +47,53 @@ export default { // like within it directives: { background: '--fg', - shadow: ['--defaultButtonShadow', '--defaultButtonBevel'], + shadow: ['--buttonDefaultShadow', '--buttonDefaultBevel'], roundness: 3 } }, { state: ['hover'], directives: { - shadow: ['--defaultButtonHoverGlow', '--defaultButtonBevel'] + shadow: ['--buttonDefaultHoverGlow', '--buttonDefaultBevel'] } }, { state: ['focused'], directives: { - shadow: ['--defaultButtonFocusGlow', '--defaultButtonBevel'] + shadow: ['--buttonDefaultFocusGlow', '--buttonDefaultBevel'] } }, { state: ['pressed'], directives: { - shadow: ['--defaultButtonShadow', '--pressedButtonBevel'] + shadow: ['--buttonDefaultShadow', '--buttonPressedBevel'] } }, { state: ['pressed', 'hover'], directives: { - shadow: ['--pressedButtonBevel', '--defaultButtonHoverGlow'] + shadow: ['--buttonPressedBevel', '--buttonDefaultHoverGlow'] } }, { state: ['toggled'], directives: { background: '--inheritedBackground,-14.2', - shadow: ['--defaultButtonShadow', '--pressedButtonBevel'] + shadow: ['--buttonDefaultShadow', '--buttonPressedBevel'] } }, { state: ['toggled', 'hover'], directives: { background: '--inheritedBackground,-14.2', - shadow: ['--defaultButtonHoverGlow', '--pressedButtonBevel'] + shadow: ['--buttonDefaultHoverGlow', '--buttonPressedBevel'] } }, { state: ['disabled'], directives: { background: '$blend(--inheritedBackground 0.25 --parent)', - shadow: ['--defaultButtonBevel'] + shadow: ['--buttonDefaultBevel'] } }, { diff --git a/src/components/component_preview/component_preview.vue b/src/components/component_preview/component_preview.vue index b6ef235a5..e34a6f3cb 100644 --- a/src/components/component_preview/component_preview.vue +++ b/src/components/component_preview/component_preview.vue @@ -111,6 +111,8 @@ export default { "x-num x-slide . " "options options options"; grid-gap: 0.5em; + max-width: 25em; + max-height: 25em; .header { grid-area: header; diff --git a/src/components/palette_editor/palette_editor.vue b/src/components/palette_editor/palette_editor.vue index cebe73ee1..16148262c 100644 --- a/src/components/palette_editor/palette_editor.vue +++ b/src/components/palette_editor/palette_editor.vue @@ -107,7 +107,7 @@ const updatePalette = (paletteKey, value) => { grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(3, 1fr) auto; grid-gap: 0.5em; - align-items: space-between; + align-items: baseline; .palette-import-button { grid-column: 1 / span 2; diff --git a/src/components/select/select_motion.vue b/src/components/select/select_motion.vue index be42b040d..57ffd7c33 100644 --- a/src/components/select/select_motion.vue +++ b/src/components/select/select_motion.vue @@ -82,9 +82,7 @@ const moveDn = () => { } const add = () => { - const newModel = [...props.modelValue] - newModel.push(props.getAddValue()) - console.log(newModel) + const newModel = [...props.modelValue, props.getAddValue()] emit('update:modelValue', newModel) emit('update:selectedId', Math.max(newModel.length - 1, 0)) 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 fb42e193e..c75e6b6b4 100644 --- a/src/components/settings_modal/tabs/style_tab/style_tab.js +++ b/src/components/settings_modal/tabs/style_tab/style_tab.js @@ -17,12 +17,16 @@ import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue' import { init } from 'src/services/theme_data/theme_data_3.service.js' import { getCssRules } from 'src/services/theme_data/css_utils.js' import { serialize } from 'src/services/theme_data/iss_serializer.js' -import { deserialize } from 'src/services/theme_data/iss_deserializer.js' +import { parseShadow /* , deserialize */ } from 'src/services/theme_data/iss_deserializer.js' import { // rgb2hex, hex2rgb, getContrastRatio } from 'src/services/color_convert/color_convert.js' +import { + // newImporter, + newExporter +} from 'src/services/export_import/export_import.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faFloppyDisk, faFolderOpen, faFile } from '@fortawesome/free-solid-svg-icons' @@ -56,23 +60,40 @@ export default { ContrastRatio }, setup () { - // ### Meta stuff + // All rules that are made by editor + const allEditedRules = reactive({}) + + // ## Meta stuff const name = ref('') const author = ref('') const license = ref('') const website = ref('') const metaOut = computed(() => { - return `@meta { - name: ${name.value}; - author: ${author.value}; - license: ${license.value}; - website: ${website.value}; -}` + return [ + '@meta {', + ` name: ${name.value};`, + ` author: ${author.value};`, + ` license: ${license.value};`, + ` website: ${website.value};`, + '}' + ].join('\n') }) - // ### Palette stuff + // ## Palette stuff const palettes = reactive([ + { + name: 'dark', + bg: '#121a24', + fg: '#182230', + text: '#b9b9ba', + link: '#d8a070', + accent: '#d8a070', + cRed: '#FF0000', + cBlue: '#0095ff', + cGreen: '#0fa00f', + cOrange: '#ffa500' + }, { name: 'light', bg: '#f2f6f9', @@ -85,44 +106,17 @@ export default { cGreen: '#0fa00f', cOrange: '#ffa500', border: '#d8e6f9' - }, - { - name: 'dark', - bg: '#121a24', - fg: '#182230', - text: '#b9b9ba', - link: '#d8a070', - accent: '#d8a070', - cRed: '#FF0000', - cBlue: '#0095ff', - cGreen: '#0fa00f', - cOrange: '#ffa500' } ]) - - const palettesOut = computed(() => { - console.log('WORK DAMN', palettes) - 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', @@ -136,34 +130,21 @@ export default { cOrange: '#ffa500' }) - // ### I18n stuff - // The paths in i18n are getting ridicously long, this effectively shortens them - const getI18nPath = (componentName) => `settings.style.themes3.editor.components.${componentName}` - // vue i18n doesn't seem to have (working) mechanic to have a fallback so we have to - // make do ourselves - const fallbackI18n = (translated, fallback) => { - if (translated.startsWith('settings.style.themes3')) { - return fallback - } - return translated - } - const getFriendlyNamePath = (componentName) => getI18nPath(componentName) + '.friendlyName' - const getVariantPath = (componentName, variant) => { - return variant === 'normal' - ? 'settings.style.themes3.editor.components.normal.variant' - : `${getI18nPath(componentName)}.variants.${variant}` - } - const getStatePath = (componentName, state) => { - return state === 'normal' - ? 'settings.style.themes3.editor.components.normal.state' - : `${getI18nPath(componentName)}.states.${state}` - } + const palettesOut = computed(() => { + return palettes.map(({ name, ...palette }) => { + const entries = Object + .entries(palette) + .map(([slot, data]) => ` ${slot}: ${data};`) + .join('\n') - // ### Initialization stuff + 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( @@ -172,23 +153,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) @@ -197,56 +178,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 @@ -297,9 +228,6 @@ export default { return root }) - // All rules that are made by editor - const allEditedRules = reactive({}) - // Checkging whether component can support some "directives" which // are actually virtual subcomponents, i.e. Text, Link etc const componentHas = (subComponent) => { @@ -331,7 +259,7 @@ export default { } }) - const getEditedElement = (component, directive) => computed({ + const getEditedElement = (component, directive, postProcess = x => x) => computed({ get () { let usedRule const fallback = editorFriendlyFallbackStructure.value @@ -343,7 +271,11 @@ export default { usedRule = get(fallback, path) } - return usedRule + if (directive === 'shadow') { + console.log('EDITED', usedRule) + console.log('PP', postProcess(usedRule)) + } + return postProcess(usedRule) }, set (value) { set(allEditedRules, getPath(component, directive), value) @@ -352,21 +284,71 @@ 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 } + } + } + + const normalizeShadows = (shadows) => { + console.log('NORMALIZE') + return shadows?.map(shadow => { + if (typeof shadow === 'object') { + return shadow + } + if (typeof shadow === 'string') { + return parseShadow(shadow) + } + return null + }) + } // Shadow is partially edited outside the ShadowControl // for better space utilization + const editedShadow = getEditedElement(null, 'shadow', normalizeShadows) 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] @@ -378,26 +360,58 @@ 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 editorFriendlyToOriginal = computed(() => { const resultRules = [] @@ -439,8 +453,11 @@ export default { const updatePreview = () => { try { - const { name, ...paletteData } = palette.value - console.log('WORK', paletteData) + const { name, ...paletteData } = selectedPalette.value + // This normally would be handled by Root but since we pass something + // else we have to make do ourselves + paletteData.accent = paletteData.accent || paletteData.link + paletteData.link = paletteData.link || paletteData.accent const rules = init({ inputRuleset: editorFriendlyToOriginal.value, initialStaticVars: { @@ -465,6 +482,7 @@ export default { } updateSelectedComponent() + // export and import watch( allEditedRules, updatePreview @@ -476,7 +494,7 @@ export default { ) watch( - editedPalette, + selectedPalette, updatePreview ) @@ -485,97 +503,139 @@ export default { 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 => { + 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() + } + }) + const virtualDirectives = reactive(allCustomVirtualDirectives) + const selectedVirtualDirectiveId = ref(0) + const selectedVirtualDirective = computed(() => virtualDirectives[selectedVirtualDirectiveId.value]) + const selectedVirtualDirectiveParsed = computed({ + get () { + switch (selectedVirtualDirective.value.valType) { + case 'shadow': { + const directiveValue = selectedVirtualDirective.value.value + if (Array.isArray(directiveValue)) { + return normalizeShadows(directiveValue) + } else { + const splitShadow = directiveValue.split(/,/g).map(x => x.trim()) + return normalizeShadows(splitShadow) + } + } + default: + return null } - } catch (e) { - console.warn('Failure computing contrast', e) - return { error: e } } - } + }) - const isShadowTabOpen = ref(false) - const onTabSwitch = (tab) => { - isShadowTabOpen.value = tab === 'shadow' - } + const getNewDirective = () => ({ + name: 'newDirective', + valType: 'generic', + value: 'foobar' + }) - const exportStyle = () => { - console.log('ORIG', toValue(editorFriendlyToOriginal.value)) - console.log('SERI', serialize(editorFriendlyToOriginal.value)) - - const result = [ + // ## Export and Import + const styleExporter = newExporter({ + filename: name.value || 'pleroma_theme', + mime: 'text/plain', + extension: 'piss', + getExportedObject: () => exportStyleData.value + }) + const exportStyleData = computed(() => { + return [ metaOut.value, palettesOut.value, serialize(editorFriendlyToOriginal.value) ].join('\n\n') - - console.log('RESULT', result) - console.log('DESERI', deserialize(result)) + }) + const exportStyle = () => { + styleExporter.exportData() } 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, - getFriendlyNamePath, - fallbackI18n, - getVariantPath, - getStatePath, - componentHas, isShadowTabOpen, onTabSwitch, + + // component preview + editorHintStyle, + previewCss, + previewClass, + + // ## Variables + virtualDirectives, + selectedVirtualDirective, + selectedVirtualDirectiveId, + selectedVirtualDirectiveParsed, + getNewDirective, + + // ## Export and Import exportStyle } } diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.scss b/src/components/settings_modal/tabs/style_tab/style_tab.scss index f557b7d59..51d4a1a75 100644 --- a/src/components/settings_modal/tabs/style_tab/style_tab.scss +++ b/src/components/settings_modal/tabs/style_tab/style_tab.scss @@ -87,48 +87,72 @@ } } - .palette-editor { + .list-editor { display: grid; grid-template-areas: "label editor" "selector editor" - "motion editor"; - grid-template-columns: auto 1fr; + "movement editor"; + grid-template-columns: 10em 1fr; grid-template-rows: auto 1fr auto; grid-gap: 0.5em; - .palette-editor-edit { + .list-edit-area { grid-area: editor; } - .palette-selector { + .list-select { + grid-area: selector; + margin: 0; + &-label { font-weight: bold; grid-area: label; margin: 0; + align-self: baseline; } - } - - .palette-list { - grid-area: selector; - margin: 0; &-movement { - grid-area: motion; + grid-area: movement; margin: 0; } } } + .palettes-editor { + .list-edit-area { + align-self: baseline; + } + } + + .variables-editor { + .variable-selector { + display: grid; + grid-template-columns: auto 1fr auto 10em; + grid-template-rows: subgrid; + align-items: baseline; + grid-gap: 0 0.5em; + } + + .list-edit-area { + display: grid; + grid-template-rows: subgrid; + } + + .shadow-control { + grid-row: 2 / span 2; + } + } + .component-editor { display: grid; grid-template-columns: 6fr 3fr 4fr; grid-template-rows: auto auto 1fr; grid-gap: 0.5em; grid-template-areas: - "component component variant" - "state state state" - "preview settings settings"; + "component component variant" + "state state state" + "preview settings settings"; .component-selector { grid-area: component; 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 704cdc1bc..1ccd7ab8b 100644 --- a/src/components/settings_modal/tabs/style_tab/style_tab.vue +++ b/src/components/settings_modal/tabs/style_tab/style_tab.vue @@ -4,6 +4,7 @@ diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js index fc227b5be..03797fc53 100644 --- a/src/components/shadow_control/shadow_control.js +++ b/src/components/shadow_control/shadow_control.js @@ -41,14 +41,17 @@ const toModel = (input) => { export default { props: [ - 'modelValue', 'fallback', 'separateInset', 'noPreview', 'disabled' + 'modelValue', + 'fallback', + 'separateInset', + 'noPreview', + 'disabled', + 'compact' ], emits: ['update:modelValue', 'subShadowSelected'], data () { return { - selectedId: 0, - // TODO there are some bugs regarding display of array (it's not getting updated when deleting for some reason) - cValue: (this.modelValue ?? this.fallback ?? []).map(toModel) + selectedId: 0 } }, components: { @@ -61,6 +64,14 @@ export default { ComponentPreview }, computed: { + cValue: { + get () { + return (this.modelValue ?? this.fallback ?? []).map(toModel) + }, + set (newVal) { + this.$emit('update:modelValue', newVal) + } + }, selectedType: { get () { return typeof this.selected @@ -115,9 +126,6 @@ export default { } }, watch: { - modelValue (value) { - if (!value) this.cValue = (this.modelValue ?? this.fallback ?? []).map(toModel) - }, selected (value) { this.$emit('subShadowSelected', this.selectedId) } diff --git a/src/components/shadow_control/shadow_control.scss b/src/components/shadow_control/shadow_control.scss index de4159c11..06381b1b3 100644 --- a/src/components/shadow_control/shadow_control.scss +++ b/src/components/shadow_control/shadow_control.scss @@ -1,12 +1,31 @@ -.settings-modal .settings-modal-panel .shadow-control { - display: flex; - flex-wrap: wrap; +.ShadowControl { + display: grid; + grid-template-columns: 10em 1fr 1fr; + grid-template-rows: 1fr; + grid-template-areas: "selector preview tweak"; + grid-gap: 0.5em; justify-content: stretch; - grid-gap: 0.25em; margin-bottom: 1em; width: 100%; + &.-compact { + grid-template-columns: 10em 1fr; + grid-template-rows: auto auto; + grid-template-areas: + "selector preview" + "tweak tweak"; + + &.-no-preview { + grid-template-columns: 1fr; + grid-template-rows: 10em 1fr; + grid-template-areas: + "selector" + "tweak"; + } + } + .shadow-switcher { + grid-area: selector; order: 1; flex: 1 0 6em; min-width: 6em; @@ -20,6 +39,7 @@ } .shadow-tweak { + grid-area: tweak; order: 3; flex: 2 0 10em; min-width: 10em; @@ -65,6 +85,10 @@ } &.-no-preview { + grid-template-columns: 10em 1fr; + grid-template-rows: 1fr; + grid-template-areas: "selector tweak"; + .shadow-tweak { order: 0; flex: 2 0 8em; @@ -87,11 +111,11 @@ } .shadow-preview { - order: 2; - flex: 3 3 15em; + grid-area: preview; min-width: 10em; margin-left: 0.125em; align-self: start; + justify-self: center; } } diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index 29adfff4a..4f0906c70 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -1,7 +1,7 @@