From e1d3ebc943f12bd7928878982405307c29d1b32b Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 24 Sep 2024 21:32:13 +0300 Subject: [PATCH] some initial drafts of component editor --- .../component_preview/component_preview.vue | 9 +- .../tabs/style_tab/style_tab.js | 217 +++++++++++++++--- .../tabs/style_tab/style_tab.scss | 53 ++++- .../tabs/style_tab/style_tab.vue | 124 +++++----- src/i18n/en.json | 38 ++- .../theme_data/theme_data_3.service.js | 26 ++- 6 files changed, 352 insertions(+), 115 deletions(-) diff --git a/src/components/component_preview/component_preview.vue b/src/components/component_preview/component_preview.vue index f29284c8..f8f51620 100644 --- a/src/components/component_preview/component_preview.vue +++ b/src/components/component_preview/component_preview.vue @@ -33,8 +33,11 @@ >
+ > + TEST +
@@ -163,6 +165,9 @@ } .preview-block { + display: flex; + justify-content: center; + align-items: center; width: 33%; height: 33%; border-radius: var(--roundness); 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 a2e6f7db..54920805 100644 --- a/src/components/settings_modal/tabs/style_tab/style_tab.js +++ b/src/components/settings_modal/tabs/style_tab/style_tab.js @@ -1,40 +1,183 @@ -// import { -// rgb2hex, -// hex2rgb, -// getContrastRatioLayers, -// relativeLuminance -// } from 'src/services/color_convert/color_convert.js' - -// import { -// getThemes -// } from 'src/services/style_setter/style_setter.js' - -// import { -// newImporter, -// newExporter -// } from 'src/services/export_import/export_import.js' - -// import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js' -// import { init } from 'src/services/theme_data/theme_data_3.service.js' -// import { -// getCssRules, -// getScopedVersion -// } from 'src/services/theme_data/css_utils.js' - -// import ColorInput from 'src/components/color_input/color_input.vue' -// import RangeInput from 'src/components/range_input/range_input.vue' -// import OpacityInput from 'src/components/opacity_input/opacity_input.vue' -// import ShadowControl from 'src/components/shadow_control/shadow_control.vue' -// import FontControl from 'src/components/font_control/font_control.vue' -// import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue' -// import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' -// import Checkbox from 'src/components/checkbox/checkbox.vue' -/* eslint-disable no-unused-vars */ +import { ref, reactive, computed, watch } from 'vue' import Select from 'src/components/select/select.vue' -import Preview from './theme_preview.vue' +import Checkbox from 'src/components/checkbox/checkbox.vue' +import ComponentPreview from 'src/components/component_preview/component_preview.vue' +import StringSetting from '../../helpers/string_setting.vue' -import { defineOptions, ref } from 'vue' -const componentsContext = require.context('src', true, /\.style.js(on)?$/) -const componentNames = componentsContext.keys() -const componentName = ref(componentNames[0]) +import { init } from 'src/services/theme_data/theme_data_3.service.js' +import { getCssRules } from 'src/services/theme_data/css_utils.js' + +import { library } from '@fortawesome/fontawesome-svg-core' +import { faFloppyDisk, faFolderOpen, faFile } from '@fortawesome/free-solid-svg-icons' + +library.add( + faFile, + faFloppyDisk, + faFolderOpen +) + +export default { + components: { + Select, + Checkbox, + StringSetting, + ComponentPreview + }, + setup () { + const name = ref('') + const author = ref('') + const license = ref('') + const website = ref('') + + const palette = { + // These are here just to establish order, + // themes should override those + bg: '#121a24', + fg: '#182230', + text: '#b9b9ba', + link: '#d8a070', + accent: '#d8a070', + cRed: '#FF0000', + cBlue: '#0095ff', + cGreen: '#0fa00f', + cOrange: '#ffa500' + } + + const getI18nPath = (componentName) => `settings.style.themes3.editor.components.${componentName}` + + 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}` + } + + // Getting existing components + const componentsContext = require.context('src', true, /\.style.js(on)?$/) + const componentKeysRaw = componentsContext.keys() + + const componentsMap = new Map( + componentKeysRaw + .map( + key => [key, componentsContext(key).default] + ).filter(([key, component]) => !component.virtual) + ) + const componentKeys = [...componentsMap.keys()] + + // const componentValues = componentsMap.values() + + // Initializing selected component and its computed descendants + const selectedComponentKey = ref(componentKeys[0]) + const selectedComponentValue = computed(() => componentsMap.get(selectedComponentKey.value)) + + const selectedComponentName = computed(() => selectedComponentValue.value.name) + const selectedComponentVariants = computed(() => { + return new Set(['normal', ...(Object.keys(selectedComponentValue.value.variants || {}))]) + }) + const selectedComponentStates = computed(() => { + return new Set([...(Object.keys(selectedComponentValue.value.states || {}).filter(x => x !== 'normal'))]) + }) + + const selectedVariant = ref('normal') + const selectedStates = reactive(new Set()) + + const updateSelectedStates = (state, v) => { + if (v) { + selectedStates.add(state) + } else { + selectedStates.delete(state) + } + } + + const simulatePseudoSelectors = css => css + .replace(selectedComponentValue.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 = [] + selectors.push(selectedComponentValue.value.variants[selectedVariant.value]) + if (selectedStates.size > 0) { + selectedStates.forEach(state => { + const original = selectedComponentValue.value.states[state] + selectors.push(simulatePseudoSelectors(original)) + }) + } + console.log(selectors) + return selectors.map(x => x.substring(1)).join('') + }) + + const previewCss = computed(() => { + console.log(previewRules) + const scoped = getCssRules(previewRules).map(simulatePseudoSelectors) + return scoped.join('\n') + }) + + const updateSelectedComponent = () => { + selectedVariant.value = 'normal' + selectedStates.clear() + + previewRules.splice(0, previewRules.length) + previewRules.push(...init({ + inputRuleset: [{ + component: selectedComponentName.value + }], + initialStaticVars: { + ...palette + }, + ultimateBackgroundColor: '#000000', + rootComponentName: selectedComponentName.value, + editMode: true, + debug: true + }).eager) + } + + updateSelectedComponent() + + watch( + selectedComponentName, + updateSelectedComponent + ) + + return { + name, + author, + license, + website, + componentKeys, + componentsMap, + + selectedComponentValue, + selectedComponentName, + selectedComponentKey, + selectedComponentVariants, + selectedComponentStates, + updateSelectedStates, + selectedVariant, + selectedStates, + getFriendlyNamePath, + getStatePath, + getVariantPath, + previewCss, + previewClass, + fallbackI18n (translated, fallback) { + if (translated.startsWith('settings.style.themes3')) { + return fallback + } + return translated + } + } + } +} 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 04b6d984..91c5572b 100644 --- a/src/components/settings_modal/tabs/style_tab/style_tab.scss +++ b/src/components/settings_modal/tabs/style_tab/style_tab.scss @@ -40,11 +40,37 @@ .component-editor { display: grid; - grid-template-columns: 10em, 1fr, 2fr; - grid-template-rows: auto 1fr; + grid-template-columns: 10em 2fr 3fr; + grid-template-rows: auto auto 1fr; + grid-gap: 0.5em; grid-template-areas: - "variant" "preview" "controls", - "state" "preview" "controls"; + "component-label component ." + "variant preview settings" + "state preview settings"; + + .compopnent-selector { + grid-area: component; + align-self: center; + } + + .component-selector-label { + grid-area: component-label; + align-self: center; + text-align: right; + font-weight: bold; + } + + .state-selector, + .variant-selector { + display: grid; + grid-template-rows: auto auto; + grid-auto-flow: rows; + grid-gap: 0.5em; + + > label { + font-weight: bold; + } + } .state-selector { grid-area: state; @@ -52,8 +78,23 @@ .variant-selector { grid-area: variant; - display: flex; - flex-direction: column; + } + + .state-selector-list { + display: grid; + list-style: none; + grid-template-rows: auto; + grid-gap: 0.5em; + padding: 0; + margin: 0; + } + + .preview-container { + grid-area: preview; + } + + .component-settings { + grid-area: settings; } } } 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 1a1180b7..c92c9217 100644 --- a/src/components/settings_modal/tabs/style_tab/style_tab.vue +++ b/src/components/settings_modal/tabs/style_tab/style_tab.vue @@ -1,49 +1,4 @@ -