commit: ef795becf6b2d6b0fa0aeeb5c1ad80c74e63e85b
parent b4a1bcd0707ba26c72411289b0a2e78a4396d142
Author: Henry Jameson <me@hjkos.com>
Date:   Thu, 26 Sep 2024 01:06:14 +0300
shadow editor now can display shadow information
Diffstat:
4 files changed, 193 insertions(+), 62 deletions(-)
diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.js b/src/components/settings_modal/tabs/style_tab/style_tab.js
@@ -4,6 +4,8 @@ import Select from 'src/components/select/select.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 ShadowControl from 'src/components/shadow_control/shadow_control.vue'
+import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
 
 import { init } from 'src/services/theme_data/theme_data_3.service.js'
 import { getCssRules } from 'src/services/theme_data/css_utils.js'
@@ -11,6 +13,8 @@ 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'
 
+const toValue = (x) => JSON.parse(JSON.stringify(x === undefined ? '"lol"' : x))
+
 library.add(
   faFile,
   faFloppyDisk,
@@ -22,14 +26,18 @@ export default {
     Select,
     Checkbox,
     StringSetting,
-    ComponentPreview
+    ComponentPreview,
+    TabSwitcher,
+    ShadowControl
   },
   setup () {
+    // Meta stuff
     const name = ref('')
     const author = ref('')
     const license = ref('')
     const website = ref('')
 
+    // Initialization stuff
     const palette = {
       // These are here just to establish order,
       // themes should override those
@@ -61,43 +69,54 @@ export default {
 
     // Getting existing components
     const componentsContext = require.context('src', true, /\.style.js(on)?$/)
-    const componentKeysRaw = componentsContext.keys()
+    const componentKeysAll = componentsContext.keys()
 
     const componentsMap = new Map(
-      componentKeysRaw
+      componentKeysAll
         .map(
           key => [key, componentsContext(key).default]
         ).filter(([key, component]) => !component.virtual && !component.notEditable)
     )
     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 selectedComponentKey = ref(componentsMap.keys().next().value)
+    const selectedComponent = computed(() => componentsMap.get(selectedComponentKey.value))
+    const selectedComponentName = computed(() => selectedComponent.value.name)
 
-    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 selectedComponentVariantsAll = computed(() => {
+      return Object.keys({ normal: null, ...(selectedComponent.value.variants || {}) })
     })
+    // const selectedComponentVariants = computed(() => {
+    //   return selectedComponentVariantsAll.value.filter(x => x !== 'normal')
+    // })
 
-    const selectedVariant = ref('normal')
     const selectedStates = reactive(new Set())
+    const selectedComponentStatesAll = computed(() => {
+      return Object.keys({ normal: null, ...(selectedComponent.value.states || {}) })
+    })
+    const selectedComponentStates = computed(() => {
+      return selectedComponentStatesAll.value.filter(x => x !== 'normal')
+    })
 
-    const updateSelectedStates = (state, v) => {
-      if (v) {
-        selectedStates.add(state)
-      } else {
-        selectedStates.delete(state)
+    // 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('; ')
+    })
 
     const simulatePseudoSelectors = css => css
-      .replace(selectedComponentValue.value.selector, '.ComponentPreview .preview-block')
+      .replace(selectedComponent.value.selector, '.ComponentPreview .preview-block')
       .replace(':active', '.preview-active')
       .replace(':hover', '.preview-hover')
       .replace(':active', '.preview-active')
@@ -108,44 +127,70 @@ export default {
     const previewRules = reactive([])
     const previewClass = computed(() => {
       const selectors = []
-      if (!!selectedComponentValue.value.variants?.normal || selectedVariant.value !== 'normal') {
-        selectors.push(selectedComponentValue.value.variants[selectedVariant.value])
+      if (!!selectedComponent.value.variants?.normal || selectedVariant.value !== 'normal') {
+        selectors.push(selectedComponent.value.variants[selectedVariant.value])
       }
       if (selectedStates.size > 0) {
         selectedStates.forEach(state => {
-          const original = selectedComponentValue.value.states[state]
+          const original = selectedComponent.value.states[state]
           selectors.push(simulatePseudoSelectors(original))
         })
       }
       return selectors.map(x => x.substring(1)).join('')
     })
-
-    const editorHintStyle = computed(() => {
-      const editorHint = selectedComponentValue.value.editor
-      const styles = []
-      if (editorHint && Object.keys(editorHint).length > 0) {
-        console.log('EH', editorHint)
-        if (editorHint.aspect != null) {
-          styles.push(`aspect-ratio: ${editorHint.aspect} !important;`)
-        }
-        if (editorHint.border != null) {
-          styles.push(`border-width: ${editorHint.border}px !important;`)
-        }
-      }
-      console.log('EH', styles)
-      return styles.join('; ')
-    })
-
     const previewCss = computed(() => {
       const scoped = getCssRules(previewRules)
         .map(simulatePseudoSelectors)
       return scoped.join('\n')
     })
 
+    // Rules stuff
+    const selectedComponentRulesList = reactive([])
+    const selectedComponentRulesObject = computed(() => {
+      const result = {}
+      selectedComponentRulesList.forEach(
+        (rule) => {
+          const component = rule.component
+          const { variant = 'normal', state = [] } = rule
+          result[component] = result[component] || {}
+          result[component][variant] = result[component][variant] || {}
+          result[component][variant][state.join(':')] = rule
+        }
+      )
+      return result
+    })
+
+    // Edited rule
+    const editedRuleFallback = computed(() => {
+      const component = selectedComponentName.value
+      const variant = selectedVariant.value
+      const states = ['normal', ...selectedStates.keys()].join(':')
+      return selectedComponentRulesObject.value[component]?.[variant]?.[states]
+    })
+
+    const editedShadow = computed(() => {
+      return editedRuleFallback.value?.directives.shadow
+    })
+
     const updateSelectedComponent = () => {
       selectedVariant.value = 'normal'
       selectedStates.clear()
 
+      const processedRulesList = selectedComponent.value.defaultRules.map(r => {
+        const rule = {
+          component: selectedComponentName.value,
+          variant: 'normal',
+          ...r,
+          state: ['normal', ...(r.state || []).filter(x => x !== 'normal')]
+        }
+        return rule
+      })
+
+      selectedComponentRulesList.splice(0, selectedComponentRulesList.length)
+      selectedComponentRulesList.push(...processedRulesList)
+
+      console.log('FALLBACK', toValue(editedRuleFallback.value))
+
       previewRules.splice(0, previewRules.length)
       previewRules.push(...init({
         inputRuleset: [{
@@ -175,21 +220,29 @@ export default {
       website,
       componentKeys,
       componentsMap,
-
-      selectedComponentValue,
+      selectedComponent,
       selectedComponentName,
       selectedComponentKey,
-      selectedComponentVariants,
+      selectedComponentVariantsAll,
       selectedComponentStates,
-      updateSelectedStates,
+      selectedComponentRulesObject,
       selectedVariant,
       selectedStates,
-      getFriendlyNamePath,
-      getStatePath,
-      getVariantPath,
-      editorHintStyle,
+      updateSelectedStates (state, v) {
+        if (v) {
+          selectedStates.add(state)
+        } else {
+          selectedStates.delete(state)
+        }
+      },
+      editedRuleFallback,
+      editedShadow,
       previewCss,
       previewClass,
+      editorHintStyle,
+      getFriendlyNamePath,
+      getVariantPath,
+      getStatePath,
       fallbackI18n (translated, fallback) {
         if (translated.startsWith('settings.style.themes3')) {
           return fallback
diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.scss b/src/components/settings_modal/tabs/style_tab/style_tab.scss
@@ -1,4 +1,48 @@
 .StyleTab {
+  .style-control {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: baseline;
+    margin-bottom: 0.5em;
+
+    .label {
+      margin-right: 1em;
+      flex: 1 1 10em;
+      line-height: 2;
+    }
+
+    .opt {
+      margin: 0.5em;
+    }
+
+    .color-input {
+      flex: 0 0 0;
+    }
+
+    input,
+    select {
+      min-width: 3em;
+      margin: 0;
+      flex: 0;
+
+      &[type="number"] {
+        min-width: 5em;
+      }
+
+      &[type="range"] {
+        flex: 1;
+        min-width: 2em;
+        align-self: center;
+        margin: 0 0.5em;
+      }
+
+      &[type="checkbox"] + i {
+        height: 1.1em;
+        align-self: center;
+      }
+    }
+  }
+
   .setting-item {
     padding-bottom: 0;
 
@@ -74,6 +118,8 @@
 
     .state-selector {
       grid-area: state;
+      align-content: flex-start;
+      align-items: flex-start;
     }
 
     .variant-selector {
@@ -96,5 +142,12 @@
     .component-settings {
       grid-area: settings;
     }
+
+    .editor-tab {
+      border-left: 1px solid var(--border);
+      border-right: 1px solid var(--border);
+      border-bottom: 1px solid var(--border);
+      padding: 0.5em;
+    }
   }
 }
diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.vue b/src/components/settings_modal/tabs/style_tab/style_tab.vue
@@ -83,44 +83,44 @@
           {{ $t('settings.style.themes3.editor.variant_selector') }}
         </label>
         <Select
-          v-if="selectedComponentVariants.size > 1"
+          v-if="selectedComponentVariantsAll.length > 1"
           v-model="selectedVariant"
         >
           <option
-            v-for="variant in selectedComponentVariants"
+            v-for="variant in selectedComponentVariantsAll"
             :value="variant"
             :key="'component-variant-' + variant"
           >
             {{ fallbackI18n($t(getVariantPath(selectedComponentName, variant)), variant)  }}
           </option>
         </Select>
-        <p v-else>
+        <div v-else>
           {{ $t('settings.style.themes3.editor.only_variant') }}
-        </p>
+        </div>
       </div>
       <div class="state-selector">
         <label for="variant-selector">
           {{ $t('settings.style.themes3.editor.states_selector') }}
         </label>
         <ul
-          v-if="selectedComponentStates.size > 0"
+          v-if="selectedComponentStates.length > 0"
           class="state-selector-list"
-        >
+          >
           <li
             v-for="state in selectedComponentStates"
             :key="'component-variant-' + state"
-          >
+            >
             <Checkbox
               :value="selectedStates.has(state)"
-              @update:modelValue="(v) => updateSelectedStates(state, v)"
-            >
+                @update:modelValue="(v) => updateSelectedStates(state, v)"
+              >
               {{ fallbackI18n($t(getStatePath(selectedComponentName, state)), state)  }}
             </Checkbox>
           </li>
         </ul>
-        <p v-else>
+        <div v-else>
           {{ $t('settings.style.themes3.editor.only_state') }}
-        </p>
+        </div>
       </div>
       <div class="preview-container">
         <!-- eslint-disable vue/no-v-html vue/no-v-text-v-html-on-component -->
@@ -136,8 +136,31 @@
           @update:shadow="({ axis, value }) => updateProperty(axis, value)"
         />
       </div>
-      <div class="component-setting">
-      </div>
+      <tab-switcher
+        ref="tabSwitcher"
+        class="component-settings"
+        :on-switch="onTabSwitch"
+      >
+        <div
+          class="editor-tab"
+          :label="$t('settings.style.themes3.editor.main_tab')"
+          :data-tab-name="main"
+        >
+          lol
+        </div>
+        <div
+          class="editor-tab"
+          :label="$t('settings.style.themes3.editor.shadows_tab')"
+          :data-tab-name="shadow"
+        >
+          <ShadowControl
+            v-model="editedShadow"
+            :no-preview="true"
+            :separate-inset="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'"
+            :fallback="currentShadowFallback"
+          />
+        </div>
+      </tab-switcher>
     </div>
   </div>
 </template>
diff --git a/src/i18n/en.json b/src/i18n/en.json
@@ -767,6 +767,8 @@
           "states_selector": "States",
           "only_variant": "Component doesn't have any variants",
           "only_state": "Component only has default state",
+          "main_tab": "Main",
+          "shadows_tab": "Shadows",
           "components": {
             "normal": {
               "state": "Normal",