commit: 144d426864402606029157d8d0ac02c0e7e1912a
parent a8092de63808ff1445636f07e11f3602774f1438
Author: Henry Jameson <me@hjkos.com>
Date: Tue, 24 Sep 2024 03:07:27 +0300
some initial work on theme editor
Diffstat:
9 files changed, 283 insertions(+), 8 deletions(-)
diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js
@@ -10,9 +10,13 @@ export default {
ProfileSettingIndicator
},
props: {
+ modelValue: {
+ type: String,
+ default: null
+ },
path: {
type: [String, Array],
- required: true
+ required: false
},
disabled: {
type: Boolean,
@@ -68,7 +72,7 @@ export default {
}
},
created () {
- if (this.realDraftMode && this.realSource !== 'admin') {
+ if (this.realDraftMode && (this.realSource !== 'admin' || this.path == null)) {
this.draft = this.state
}
},
@@ -76,14 +80,14 @@ export default {
draft: {
// TODO allow passing shared draft object?
get () {
- if (this.realSource === 'admin') {
+ if (this.realSource === 'admin' || this.path == null) {
return get(this.$store.state.adminSettings.draft, this.canonPath)
} else {
return this.localDraft
}
},
set (value) {
- if (this.realSource === 'admin') {
+ if (this.realSource === 'admin' || this.path == null) {
this.$store.commit('updateAdminDraft', { path: this.canonPath, value })
} else {
this.localDraft = value
@@ -91,6 +95,9 @@ export default {
}
},
state () {
+ if (this.path == null) {
+ return this.modelValue
+ }
const value = get(this.configSource, this.canonPath)
if (value === undefined) {
return this.defaultState
@@ -145,6 +152,9 @@ export default {
return this.backendDescription?.suggestions
},
shouldBeDisabled () {
+ if (this.path == null) {
+ return this.disabled
+ }
const parentValue = this.parentPath !== undefined ? get(this.configSource, this.parentPath) : null
return this.disabled || (parentValue !== null ? (this.parentInvert ? parentValue : !parentValue) : false)
},
@@ -159,6 +169,9 @@ export default {
}
},
configSink () {
+ if (this.path == null) {
+ return (k, v) => this.$emit('modelValue:update', v)
+ }
switch (this.realSource) {
case 'profile':
return (k, v) => this.$store.dispatch('setProfileOption', { name: k, value: v })
@@ -184,6 +197,7 @@ export default {
return this.realSource === 'profile'
},
isChanged () {
+ if (this.path == null) return false
switch (this.realSource) {
case 'profile':
case 'admin':
@@ -193,9 +207,11 @@ export default {
}
},
canonPath () {
+ if (this.path == null) return null
return Array.isArray(this.path) ? this.path : this.path.split('.')
},
isDirty () {
+ if (this.path == null) return false
if (this.realSource === 'admin' && this.canonPath.length > 3) {
return false // should not show draft buttons for "grouped" values
} else {
diff --git a/src/components/settings_modal/helpers/string_setting.vue b/src/components/settings_modal/helpers/string_setting.vue
@@ -15,6 +15,7 @@
</template>
<slot v-else />
</label>
+ {{ ' ' }}
<input
:id="path"
class="input string-input"
diff --git a/src/components/settings_modal/settings_modal.scss b/src/components/settings_modal/settings_modal.scss
@@ -10,6 +10,10 @@
list-style-type: none;
padding-left: 2em;
+ .btn:not(.dropdown-button) {
+ padding: 0 2em;
+ }
+
li {
margin-bottom: 0.5em;
}
@@ -54,10 +58,6 @@
.btn {
min-height: 2em;
}
-
- .btn:not(.dropdown-button) {
- padding: 0 2em;
- }
}
}
diff --git a/src/components/settings_modal/settings_modal_user_content.js b/src/components/settings_modal/settings_modal_user_content.js
@@ -10,6 +10,7 @@ import GeneralTab from './tabs/general_tab.vue'
import AppearanceTab from './tabs/appearance_tab.vue'
import VersionTab from './tabs/version_tab.vue'
import ThemeTab from './tabs/theme_tab/theme_tab.vue'
+import StyleTab from './tabs/style_tab/style_tab.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
@@ -17,6 +18,7 @@ import {
faUser,
faFilter,
faPaintBrush,
+ faPalette,
faBell,
faDownload,
faEyeSlash,
@@ -29,6 +31,7 @@ library.add(
faUser,
faFilter,
faPaintBrush,
+ faPalette,
faBell,
faDownload,
faEyeSlash,
@@ -48,6 +51,7 @@ const SettingsModalContent = {
ProfileTab,
GeneralTab,
AppearanceTab,
+ StyleTab,
VersionTab,
ThemeTab
},
diff --git a/src/components/settings_modal/settings_modal_user_content.vue b/src/components/settings_modal/settings_modal_user_content.vue
@@ -21,6 +21,13 @@
<AppearanceTab />
</div>
<div
+ :label="$t('settings.style.themes3.editor.title')"
+ icon="palette"
+ data-tab-name="style"
+ >
+ <StyleTab />
+ </div>
+ <div
:label="$t('settings.theme')"
icon="paint-brush"
data-tab-name="theme"
diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.js b/src/components/settings_modal/tabs/style_tab/style_tab.js
@@ -0,0 +1,40 @@
+// 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 Select from 'src/components/select/select.vue'
+import Preview from './theme_preview.vue'
+
+import { defineOptions, ref } from 'vue'
+const componentsContext = require.context('src', true, /\.style.js(on)?$/)
+const componentNames = componentsContext.keys()
+const componentName = ref(componentNames[0])
diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.scss b/src/components/settings_modal/tabs/style_tab/style_tab.scss
@@ -0,0 +1,59 @@
+.StyleTab {
+ .setting-item {
+ padding-bottom: 0;
+
+ .btn {
+ padding: 0 0.5em;
+ }
+
+ &:not(:first-child) {
+ margin-top: 0.5em;
+ }
+
+ &:not(:last-child) {
+ margin-bottom: 0.5em;
+ }
+
+ &.heading {
+ display: grid;
+ align-items: baseline;
+ grid-template-columns: 1fr auto auto auto;
+ grid-gap: 0.5em;
+
+ h2 {
+ flex: 1 0 auto;
+ }
+ }
+
+ &.metadata {
+ display: flex;
+
+ .setting-item {
+ flex: 2 0 auto;
+ }
+
+ li {
+ text-align: right;
+ }
+ }
+ }
+
+ .component-editor {
+ display: grid;
+ grid-template-columns: 10em, 1fr, 2fr;
+ grid-template-rows: auto 1fr;
+ grid-template-areas:
+ "variant" "preview" "controls",
+ "state" "preview" "controls";
+
+ .state-selector {
+ grid-area: state;
+ }
+
+ .variant-selector {
+ grid-area: variant;
+ display: flex;
+ flex-direction: column;
+ }
+ }
+}
diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.vue b/src/components/settings_modal/tabs/style_tab/style_tab.vue
@@ -0,0 +1,138 @@
+<script setup>
+import { ref, computed } from 'vue'
+
+import Select from 'src/components/select/select.vue'
+import Checkbox from 'src/components/checkbox/checkbox.vue'
+import StringSetting from '../../helpers/string_setting.vue'
+// import Preview from '../theme_tab/theme_preview.vue'
+
+import { library } from '@fortawesome/fontawesome-svg-core'
+import { faFloppyDisk, faFolderOpen, faFile } from '@fortawesome/free-solid-svg-icons'
+
+library.add(
+ faFile,
+ faFloppyDisk,
+ faFolderOpen
+)
+
+const name = ref('')
+const author = ref('')
+const license = ref('')
+const website = ref('')
+
+// Getting existing components
+const componentsContext = require.context('src', true, /\.style.js(on)?$/)
+const componentKeys = componentsContext.keys()
+
+const componentsMap = new Map(
+ componentKeys
+ .map(
+ key => [key, componentsContext(key).default]
+ )
+)
+// 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(() => selectedComponent.value.name)
+const selectedComponentVariants = computed(() => {
+ return new Set([...(Object.keys(selectedComponentValue.value.variants || {})), 'normal'])
+})
+const selectedComponentStates = computed(() => {
+ return new Set([...(Object.keys(selectedComponentValue.value.states || {})), 'normal'])
+})
+
+</script>
+
+<template>
+ <div class="StyleTab">
+ <div class="setting-item heading">
+ <h2>{{ $t('settings.style.themes3.editor.title') }}</h2>
+ <button
+ class="btn button-default"
+ @click="clearTheme"
+ >
+ <FAIcon icon="file" />
+ {{ $t('settings.style.themes3.editor.new_style') }}
+ </button>
+ <button
+ class="btn button-default"
+ @click="importStyle"
+ >
+ <FAIcon icon="folder-open" />
+ {{ $t('settings.style.themes3.editor.load_style') }}
+ </button>
+ <button
+ class="btn button-default"
+ @click="exportTheme"
+ >
+ <FAIcon icon="floppy-disk" />
+ {{ $t('settings.style.themes3.editor.save_style') }}
+ </button>
+ </div>
+ <div class="setting-item metadata">
+ <ul class="setting-list">
+ <li>
+ <StringSetting v-model="name">
+ {{ $t('settings.style.themes3.editor.style_name') }}
+ </StringSetting>
+ </li>
+ <li>
+ <StringSetting v-model="author">
+ {{ $t('settings.style.themes3.editor.style_author') }}
+ </StringSetting>
+ </li>
+ <li>
+ <StringSetting v-model="license">
+ {{ $t('settings.style.themes3.editor.style_license') }}
+ </StringSetting>
+ </li>
+ <li>
+ <StringSetting v-model="website">
+ {{ $t('settings.style.themes3.editor.style_website') }}
+ </StringSetting>
+ </li>
+ </ul>
+ </div>
+ <div class="setting-item">
+ <Select v-model="selectedComponentKey">
+ <option
+ v-for="key in componentKeys"
+ :key="'component-' + key"
+ :value="key"
+ >
+ {{ componentsMap.get(key).name }}
+ </option>
+ </Select>
+ <div class="component-editor">
+ <Select
+ v-model="selectedComponentVariant"
+ class="variant-selector"
+ >
+ <option
+ v-for="variant in selectedComponentVariants"
+ :key="'component-variant-' + variant"
+ >
+ {{ variant }}
+ </option>
+ </Select>
+ <ul class="state-selector">
+ <li
+ v-for="state in selectedComponentStates"
+ :key="'component-variant-' + state"
+ >
+ <Checkbox
+ v-model="selectedComponentStates"
+ >
+ {{ state }}
+ </Checkbox>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+</template>
+
+<style src="./style_tab.scss" lang="scss"></style>
diff --git a/src/i18n/en.json b/src/i18n/en.json
@@ -753,6 +753,16 @@
"update_preview": "Update preview",
"themes3": {
"define": "Override",
+ "editor": {
+ "title": "Style",
+ "new_style": "New",
+ "load_style": "Open",
+ "save_style": "Save",
+ "style_name": "Stylesheet name",
+ "style_author": "Made by",
+ "style_license": "License",
+ "style_website": "Website"
+ },
"hacks": {
"underlay_overrides": "Change underlay",
"underlay_override_mode_none": "Theme default",