Compare commits
15 commits
104bc8c86e
...
f8b39faae7
Author | SHA1 | Date | |
---|---|---|---|
|
f8b39faae7 | ||
|
4d472e1d4b | ||
|
4f66731723 | ||
|
f0e5b0be1e | ||
|
f75ea738ca | ||
|
97c058ebda | ||
|
cfe52185f7 | ||
|
756ea63b67 | ||
|
8725de3e91 | ||
|
a6863248bb | ||
|
da2c016ab4 | ||
|
7e684ea3ff | ||
|
0f2bd39db8 | ||
|
c58ed1036f | ||
|
d31da2c300 |
14 changed files with 475 additions and 310 deletions
|
@ -35,11 +35,11 @@ export default {
|
||||||
{
|
{
|
||||||
component: 'Root',
|
component: 'Root',
|
||||||
directives: {
|
directives: {
|
||||||
'--defaultButtonHoverGlow': 'shadow | 0 0 4 --text / 0.5',
|
'--buttonDefaultHoverGlow': 'shadow | 0 0 4 --text / 0.5',
|
||||||
'--defaultButtonFocusGlow': 'shadow | 0 0 4 4 --link / 0.5',
|
'--buttonDefaultFocusGlow': 'shadow | 0 0 4 4 --link / 0.5',
|
||||||
'--defaultButtonShadow': 'shadow | 0 0 2 #000000',
|
'--buttonDefaultShadow': 'shadow | 0 0 2 #000000',
|
||||||
'--defaultButtonBevel': 'shadow | $borderSide(#FFFFFF top 0.2 2), $borderSide(#000000 bottom 0.2 2)',
|
'--buttonDefaultBevel': '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)'
|
'--buttonPressedBevel': 'shadow | $borderSide(#FFFFFF bottom 0.2 2), $borderSide(#000000 top 0.2 2)'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -47,53 +47,53 @@ export default {
|
||||||
// like within it
|
// like within it
|
||||||
directives: {
|
directives: {
|
||||||
background: '--fg',
|
background: '--fg',
|
||||||
shadow: ['--defaultButtonShadow', '--defaultButtonBevel'],
|
shadow: ['--buttonDefaultShadow', '--buttonDefaultBevel'],
|
||||||
roundness: 3
|
roundness: 3
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
state: ['hover'],
|
state: ['hover'],
|
||||||
directives: {
|
directives: {
|
||||||
shadow: ['--defaultButtonHoverGlow', '--defaultButtonBevel']
|
shadow: ['--buttonDefaultHoverGlow', '--buttonDefaultBevel']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
state: ['focused'],
|
state: ['focused'],
|
||||||
directives: {
|
directives: {
|
||||||
shadow: ['--defaultButtonFocusGlow', '--defaultButtonBevel']
|
shadow: ['--buttonDefaultFocusGlow', '--buttonDefaultBevel']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
state: ['pressed'],
|
state: ['pressed'],
|
||||||
directives: {
|
directives: {
|
||||||
shadow: ['--defaultButtonShadow', '--pressedButtonBevel']
|
shadow: ['--buttonDefaultShadow', '--buttonPressedBevel']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
state: ['pressed', 'hover'],
|
state: ['pressed', 'hover'],
|
||||||
directives: {
|
directives: {
|
||||||
shadow: ['--pressedButtonBevel', '--defaultButtonHoverGlow']
|
shadow: ['--buttonPressedBevel', '--buttonDefaultHoverGlow']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
state: ['toggled'],
|
state: ['toggled'],
|
||||||
directives: {
|
directives: {
|
||||||
background: '--inheritedBackground,-14.2',
|
background: '--inheritedBackground,-14.2',
|
||||||
shadow: ['--defaultButtonShadow', '--pressedButtonBevel']
|
shadow: ['--buttonDefaultShadow', '--buttonPressedBevel']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
state: ['toggled', 'hover'],
|
state: ['toggled', 'hover'],
|
||||||
directives: {
|
directives: {
|
||||||
background: '--inheritedBackground,-14.2',
|
background: '--inheritedBackground,-14.2',
|
||||||
shadow: ['--defaultButtonHoverGlow', '--pressedButtonBevel']
|
shadow: ['--buttonDefaultHoverGlow', '--buttonPressedBevel']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
state: ['disabled'],
|
state: ['disabled'],
|
||||||
directives: {
|
directives: {
|
||||||
background: '$blend(--inheritedBackground 0.25 --parent)',
|
background: '$blend(--inheritedBackground 0.25 --parent)',
|
||||||
shadow: ['--defaultButtonBevel']
|
shadow: ['--buttonDefaultBevel']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -111,6 +111,8 @@ export default {
|
||||||
"x-num x-slide . "
|
"x-num x-slide . "
|
||||||
"options options options";
|
"options options options";
|
||||||
grid-gap: 0.5em;
|
grid-gap: 0.5em;
|
||||||
|
max-width: 25em;
|
||||||
|
max-height: 25em;
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
grid-area: header;
|
grid-area: header;
|
||||||
|
|
|
@ -107,7 +107,7 @@ const updatePalette = (paletteKey, value) => {
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
grid-template-rows: repeat(3, 1fr) auto;
|
grid-template-rows: repeat(3, 1fr) auto;
|
||||||
grid-gap: 0.5em;
|
grid-gap: 0.5em;
|
||||||
align-items: space-between;
|
align-items: baseline;
|
||||||
|
|
||||||
.palette-import-button {
|
.palette-import-button {
|
||||||
grid-column: 1 / span 2;
|
grid-column: 1 / span 2;
|
||||||
|
|
|
@ -82,9 +82,7 @@ const moveDn = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const add = () => {
|
const add = () => {
|
||||||
const newModel = [...props.modelValue]
|
const newModel = [...props.modelValue, props.getAddValue()]
|
||||||
newModel.push(props.getAddValue())
|
|
||||||
console.log(newModel)
|
|
||||||
|
|
||||||
emit('update:modelValue', newModel)
|
emit('update:modelValue', newModel)
|
||||||
emit('update:selectedId', Math.max(newModel.length - 1, 0))
|
emit('update:selectedId', Math.max(newModel.length - 1, 0))
|
||||||
|
|
|
@ -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 { init } from 'src/services/theme_data/theme_data_3.service.js'
|
||||||
import { getCssRules } from 'src/services/theme_data/css_utils.js'
|
import { getCssRules } from 'src/services/theme_data/css_utils.js'
|
||||||
import { serialize } from 'src/services/theme_data/iss_serializer.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 {
|
import {
|
||||||
// rgb2hex,
|
// rgb2hex,
|
||||||
hex2rgb,
|
hex2rgb,
|
||||||
getContrastRatio
|
getContrastRatio
|
||||||
} from 'src/services/color_convert/color_convert.js'
|
} 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 { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faFloppyDisk, faFolderOpen, faFile } from '@fortawesome/free-solid-svg-icons'
|
import { faFloppyDisk, faFolderOpen, faFile } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
@ -56,23 +60,40 @@ export default {
|
||||||
ContrastRatio
|
ContrastRatio
|
||||||
},
|
},
|
||||||
setup () {
|
setup () {
|
||||||
// ### Meta stuff
|
// All rules that are made by editor
|
||||||
|
const allEditedRules = reactive({})
|
||||||
|
|
||||||
|
// ## Meta stuff
|
||||||
const name = ref('')
|
const name = ref('')
|
||||||
const author = ref('')
|
const author = ref('')
|
||||||
const license = ref('')
|
const license = ref('')
|
||||||
const website = ref('')
|
const website = ref('')
|
||||||
|
|
||||||
const metaOut = computed(() => {
|
const metaOut = computed(() => {
|
||||||
return `@meta {
|
return [
|
||||||
name: ${name.value};
|
'@meta {',
|
||||||
author: ${author.value};
|
` name: ${name.value};`,
|
||||||
license: ${license.value};
|
` author: ${author.value};`,
|
||||||
website: ${website.value};
|
` license: ${license.value};`,
|
||||||
}`
|
` website: ${website.value};`,
|
||||||
|
'}'
|
||||||
|
].join('\n')
|
||||||
})
|
})
|
||||||
|
|
||||||
// ### Palette stuff
|
// ## Palette stuff
|
||||||
const palettes = reactive([
|
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',
|
name: 'light',
|
||||||
bg: '#f2f6f9',
|
bg: '#f2f6f9',
|
||||||
|
@ -85,44 +106,17 @@ export default {
|
||||||
cGreen: '#0fa00f',
|
cGreen: '#0fa00f',
|
||||||
cOrange: '#ffa500',
|
cOrange: '#ffa500',
|
||||||
border: '#d8e6f9'
|
border: '#d8e6f9'
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'dark',
|
|
||||||
bg: '#121a24',
|
|
||||||
fg: '#182230',
|
|
||||||
text: '#b9b9ba',
|
|
||||||
link: '#d8a070',
|
|
||||||
accent: '#d8a070',
|
|
||||||
cRed: '#FF0000',
|
|
||||||
cBlue: '#0095ff',
|
|
||||||
cGreen: '#0fa00f',
|
|
||||||
cOrange: '#ffa500'
|
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
const selectedPaletteId = ref(0)
|
||||||
const palettesOut = computed(() => {
|
const selectedPalette = 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({
|
|
||||||
get () {
|
get () {
|
||||||
console.log(palettes, editedPalette.value)
|
return palettes[selectedPaletteId.value]
|
||||||
return palettes[editedPalette.value]
|
|
||||||
},
|
},
|
||||||
set (newPalette) {
|
set (newPalette) {
|
||||||
palettes[editedPalette.value] = newPalette
|
palettes[selectedPaletteId.value] = newPalette
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const getNewPalette = () => ({
|
const getNewPalette = () => ({
|
||||||
name: 'new palette',
|
name: 'new palette',
|
||||||
bg: '#121a24',
|
bg: '#121a24',
|
||||||
|
@ -136,34 +130,21 @@ export default {
|
||||||
cOrange: '#ffa500'
|
cOrange: '#ffa500'
|
||||||
})
|
})
|
||||||
|
|
||||||
// ### I18n stuff
|
const palettesOut = computed(() => {
|
||||||
// The paths in i18n are getting ridicously long, this effectively shortens them
|
return palettes.map(({ name, ...palette }) => {
|
||||||
const getI18nPath = (componentName) => `settings.style.themes3.editor.components.${componentName}`
|
const entries = Object
|
||||||
// vue i18n doesn't seem to have (working) mechanic to have a fallback so we have to
|
.entries(palette)
|
||||||
// make do ourselves
|
.map(([slot, data]) => ` ${slot}: ${data};`)
|
||||||
const fallbackI18n = (translated, fallback) => {
|
.join('\n')
|
||||||
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}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### Initialization stuff
|
return `@palette.${name} {\n${entries}\n}`
|
||||||
|
}).join('\n\n')
|
||||||
|
})
|
||||||
|
|
||||||
|
// ## Components stuff
|
||||||
// Getting existing components
|
// Getting existing components
|
||||||
const componentsContext = require.context('src', true, /\.style.js(on)?$/)
|
const componentsContext = require.context('src', true, /\.style.js(on)?$/)
|
||||||
const componentKeysAll = componentsContext.keys()
|
const componentKeysAll = componentsContext.keys()
|
||||||
|
|
||||||
const componentsMap = new Map(
|
const componentsMap = new Map(
|
||||||
componentKeysAll
|
componentKeysAll
|
||||||
.map(
|
.map(
|
||||||
|
@ -172,23 +153,23 @@ export default {
|
||||||
)
|
)
|
||||||
const componentKeys = [...componentsMap.keys()]
|
const componentKeys = [...componentsMap.keys()]
|
||||||
|
|
||||||
// Initializing selected component and its computed descendants
|
// selection basis
|
||||||
const selectedComponentKey = ref(componentsMap.keys().next().value)
|
const selectedComponentKey = ref(componentsMap.keys().next().value)
|
||||||
const selectedComponent = computed(() => componentsMap.get(selectedComponentKey.value))
|
const selectedComponent = computed(() => componentsMap.get(selectedComponentKey.value))
|
||||||
const selectedComponentName = computed(() => selectedComponent.value.name)
|
const selectedComponentName = computed(() => selectedComponent.value.name)
|
||||||
|
const selectedComponentVariants = computed(() => {
|
||||||
const selectedVariant = ref('normal')
|
|
||||||
const selectedComponentVariantsAll = computed(() => {
|
|
||||||
return Object.keys({ normal: null, ...(selectedComponent.value.variants || {}) })
|
return Object.keys({ normal: null, ...(selectedComponent.value.variants || {}) })
|
||||||
})
|
})
|
||||||
|
|
||||||
const selectedState = reactive(new Set())
|
|
||||||
const selectedComponentStatesAll = computed(() => {
|
const selectedComponentStatesAll = computed(() => {
|
||||||
return Object.keys({ normal: null, ...(selectedComponent.value.states || {}) })
|
return Object.keys({ normal: null, ...(selectedComponent.value.states || {}) })
|
||||||
})
|
})
|
||||||
const selectedComponentStates = computed(() => {
|
const selectedComponentStates = computed(() => {
|
||||||
return selectedComponentStatesAll.value.filter(x => x !== 'normal')
|
return selectedComponentStatesAll.value.filter(x => x !== 'normal')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// selection
|
||||||
|
const selectedVariant = ref('normal')
|
||||||
|
const selectedState = reactive(new Set())
|
||||||
const updateSelectedStates = (state, v) => {
|
const updateSelectedStates = (state, v) => {
|
||||||
if (v) {
|
if (v) {
|
||||||
selectedState.add(state)
|
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
|
// ### Rules stuff aka meat and potatoes
|
||||||
// The native structure of separate rules and the child -> parent
|
// The native structure of separate rules and the child -> parent
|
||||||
// relation isn't very convenient for editor, we replace the array
|
// relation isn't very convenient for editor, we replace the array
|
||||||
|
@ -297,9 +228,6 @@ export default {
|
||||||
return root
|
return root
|
||||||
})
|
})
|
||||||
|
|
||||||
// All rules that are made by editor
|
|
||||||
const allEditedRules = reactive({})
|
|
||||||
|
|
||||||
// Checkging whether component can support some "directives" which
|
// Checkging whether component can support some "directives" which
|
||||||
// are actually virtual subcomponents, i.e. Text, Link etc
|
// are actually virtual subcomponents, i.e. Text, Link etc
|
||||||
const componentHas = (subComponent) => {
|
const componentHas = (subComponent) => {
|
||||||
|
@ -331,7 +259,7 @@ export default {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const getEditedElement = (component, directive) => computed({
|
const getEditedElement = (component, directive, postProcess = x => x) => computed({
|
||||||
get () {
|
get () {
|
||||||
let usedRule
|
let usedRule
|
||||||
const fallback = editorFriendlyFallbackStructure.value
|
const fallback = editorFriendlyFallbackStructure.value
|
||||||
|
@ -343,7 +271,11 @@ export default {
|
||||||
usedRule = get(fallback, path)
|
usedRule = get(fallback, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return usedRule
|
if (directive === 'shadow') {
|
||||||
|
console.log('EDITED', usedRule)
|
||||||
|
console.log('PP', postProcess(usedRule))
|
||||||
|
}
|
||||||
|
return postProcess(usedRule)
|
||||||
},
|
},
|
||||||
set (value) {
|
set (value) {
|
||||||
set(allEditedRules, getPath(component, directive), value)
|
set(allEditedRules, getPath(component, directive), value)
|
||||||
|
@ -352,21 +284,71 @@ export default {
|
||||||
|
|
||||||
// All the editable stuff for the component
|
// All the editable stuff for the component
|
||||||
const editedBackgroundColor = getEditedElement(null, 'background')
|
const editedBackgroundColor = getEditedElement(null, 'background')
|
||||||
|
const isBackgroundColorPresent = isElementPresent(null, 'background', '#FFFFFF')
|
||||||
const editedOpacity = getEditedElement(null, 'opacity')
|
const editedOpacity = getEditedElement(null, 'opacity')
|
||||||
|
const isOpacityPresent = isElementPresent(null, 'opacity', 1)
|
||||||
const editedTextColor = getEditedElement('Text', 'textColor')
|
const editedTextColor = getEditedElement('Text', 'textColor')
|
||||||
|
const isTextColorPresent = isElementPresent('Text', 'textColor', '#000000')
|
||||||
const editedTextAuto = getEditedElement('Text', 'textAuto')
|
const editedTextAuto = getEditedElement('Text', 'textAuto')
|
||||||
|
const isTextAutoPresent = isElementPresent('Text', 'textAuto', '#000000')
|
||||||
const editedLinkColor = getEditedElement('Link', 'textColor')
|
const editedLinkColor = getEditedElement('Link', 'textColor')
|
||||||
|
const isLinkColorPresent = isElementPresent('Link', 'textColor', '#000080')
|
||||||
const editedIconColor = getEditedElement('Icon', 'textColor')
|
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 <ContractRatio />
|
||||||
|
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
|
// Shadow is partially edited outside the ShadowControl
|
||||||
// for better space utilization
|
// for better space utilization
|
||||||
|
const editedShadow = getEditedElement(null, 'shadow', normalizeShadows)
|
||||||
const editedSubShadowId = ref(null)
|
const editedSubShadowId = ref(null)
|
||||||
const editedSubShadow = computed(() => {
|
const editedSubShadow = computed(() => {
|
||||||
if (editedShadow.value == null || editedSubShadowId.value == null) return null
|
if (editedShadow.value == null || editedSubShadowId.value == null) return null
|
||||||
return editedShadow.value[editedSubShadowId.value]
|
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) => {
|
const updateSubShadow = (axis, value) => {
|
||||||
if (!editedSubShadow.value || editedSubShadowId.value == null) return
|
if (!editedSubShadow.value || editedSubShadowId.value == null) return
|
||||||
const newEditedShadow = [...editedShadow.value]
|
const newEditedShadow = [...editedShadow.value]
|
||||||
|
@ -378,26 +360,58 @@ export default {
|
||||||
|
|
||||||
editedShadow.value = newEditedShadow
|
editedShadow.value = newEditedShadow
|
||||||
}
|
}
|
||||||
|
const isShadowTabOpen = ref(false)
|
||||||
const onSubShadow = (id) => {
|
const onTabSwitch = (tab) => {
|
||||||
if (id != null) {
|
isShadowTabOpen.value = tab === 'shadow'
|
||||||
editedSubShadowId.value = id
|
|
||||||
} else {
|
|
||||||
editedSubShadow.value = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whether specific directives present in the edited rule or not
|
// component preview
|
||||||
// Somewhat serves double-duty as it creates/removes the directive
|
const editorHintStyle = computed(() => {
|
||||||
// when set
|
const editorHint = selectedComponent.value.editor
|
||||||
const isBackgroundColorPresent = isElementPresent(null, 'background', '#FFFFFF')
|
const styles = []
|
||||||
const isOpacityPresent = isElementPresent(null, 'opacity', 1)
|
if (editorHint && Object.keys(editorHint).length > 0) {
|
||||||
const isTextColorPresent = isElementPresent('Text', 'textColor', '#000000')
|
if (editorHint.aspect != null) {
|
||||||
const isTextAutoPresent = isElementPresent('Text', 'textAuto', '#000000')
|
styles.push(`aspect-ratio: ${editorHint.aspect} !important;`)
|
||||||
const isLinkColorPresent = isElementPresent('Link', 'textColor', '#000080')
|
}
|
||||||
const isIconColorPresent = isElementPresent('Icon', 'textColor', '#909090')
|
if (editorHint.border != null) {
|
||||||
const isShadowPresent = isElementPresent(null, 'shadow', [])
|
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 editorFriendlyToOriginal = computed(() => {
|
||||||
const resultRules = []
|
const resultRules = []
|
||||||
|
|
||||||
|
@ -439,8 +453,11 @@ export default {
|
||||||
|
|
||||||
const updatePreview = () => {
|
const updatePreview = () => {
|
||||||
try {
|
try {
|
||||||
const { name, ...paletteData } = palette.value
|
const { name, ...paletteData } = selectedPalette.value
|
||||||
console.log('WORK', paletteData)
|
// 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({
|
const rules = init({
|
||||||
inputRuleset: editorFriendlyToOriginal.value,
|
inputRuleset: editorFriendlyToOriginal.value,
|
||||||
initialStaticVars: {
|
initialStaticVars: {
|
||||||
|
@ -465,6 +482,7 @@ export default {
|
||||||
}
|
}
|
||||||
updateSelectedComponent()
|
updateSelectedComponent()
|
||||||
|
|
||||||
|
// export and import
|
||||||
watch(
|
watch(
|
||||||
allEditedRules,
|
allEditedRules,
|
||||||
updatePreview
|
updatePreview
|
||||||
|
@ -476,7 +494,7 @@ export default {
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
editedPalette,
|
selectedPalette,
|
||||||
updatePreview
|
updatePreview
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -485,97 +503,139 @@ export default {
|
||||||
updateSelectedComponent
|
updateSelectedComponent
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO this is VERY primitive right now, need to make it
|
// ## Variables
|
||||||
// support variables, fallbacks etc.
|
const allCustomVirtualDirectives = [...componentsMap.values()]
|
||||||
const getContrast = (bg, text) => {
|
.map(c => {
|
||||||
try {
|
return c
|
||||||
const bgRgb = hex2rgb(bg)
|
.defaultRules
|
||||||
const textRgb = hex2rgb(text)
|
.filter(c => c.component === 'Root')
|
||||||
|
.map(x => Object.entries(x.directives))
|
||||||
const ratio = getContrastRatio(bgRgb, textRgb)
|
.flat()
|
||||||
|
})
|
||||||
|
.filter(x => x)
|
||||||
|
.flat()
|
||||||
|
.map(([name, value]) => {
|
||||||
|
const [valType, valVal] = value.split('|')
|
||||||
return {
|
return {
|
||||||
// TODO this ideally should be part of <ContractRatio />
|
name: name.substring(2),
|
||||||
ratio,
|
valType: valType.trim(),
|
||||||
text: ratio.toPrecision(3) + ':1',
|
value: valVal.trim()
|
||||||
// AA level, AAA level
|
}
|
||||||
aa: ratio >= 4.5,
|
})
|
||||||
aaa: ratio >= 7,
|
const virtualDirectives = reactive(allCustomVirtualDirectives)
|
||||||
// same but for 18pt+ texts
|
const selectedVirtualDirectiveId = ref(0)
|
||||||
laa: ratio >= 3,
|
const selectedVirtualDirective = computed(() => virtualDirectives[selectedVirtualDirectiveId.value])
|
||||||
laaa: ratio >= 4.5
|
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 getNewDirective = () => ({
|
||||||
const onTabSwitch = (tab) => {
|
name: 'newDirective',
|
||||||
isShadowTabOpen.value = tab === 'shadow'
|
valType: 'generic',
|
||||||
}
|
value: 'foobar'
|
||||||
|
})
|
||||||
|
|
||||||
const exportStyle = () => {
|
// ## Export and Import
|
||||||
console.log('ORIG', toValue(editorFriendlyToOriginal.value))
|
const styleExporter = newExporter({
|
||||||
console.log('SERI', serialize(editorFriendlyToOriginal.value))
|
filename: name.value || 'pleroma_theme',
|
||||||
|
mime: 'text/plain',
|
||||||
const result = [
|
extension: 'piss',
|
||||||
|
getExportedObject: () => exportStyleData.value
|
||||||
|
})
|
||||||
|
const exportStyleData = computed(() => {
|
||||||
|
return [
|
||||||
metaOut.value,
|
metaOut.value,
|
||||||
palettesOut.value,
|
palettesOut.value,
|
||||||
serialize(editorFriendlyToOriginal.value)
|
serialize(editorFriendlyToOriginal.value)
|
||||||
].join('\n\n')
|
].join('\n\n')
|
||||||
|
})
|
||||||
console.log('RESULT', result)
|
const exportStyle = () => {
|
||||||
console.log('DESERI', deserialize(result))
|
styleExporter.exportData()
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
// ## Meta
|
||||||
name,
|
name,
|
||||||
author,
|
author,
|
||||||
license,
|
license,
|
||||||
website,
|
website,
|
||||||
palette,
|
|
||||||
|
// ## Palette
|
||||||
palettes,
|
palettes,
|
||||||
editedPalette,
|
selectedPalette,
|
||||||
|
selectedPaletteId,
|
||||||
getNewPalette,
|
getNewPalette,
|
||||||
|
|
||||||
|
// ## Components
|
||||||
componentKeys,
|
componentKeys,
|
||||||
componentsMap,
|
componentsMap,
|
||||||
|
|
||||||
|
// selection basis
|
||||||
selectedComponent,
|
selectedComponent,
|
||||||
selectedComponentName,
|
selectedComponentName,
|
||||||
selectedComponentKey,
|
selectedComponentKey,
|
||||||
selectedComponentVariantsAll,
|
selectedComponentVariants,
|
||||||
selectedComponentStates,
|
selectedComponentStates,
|
||||||
|
|
||||||
|
// selection
|
||||||
selectedVariant,
|
selectedVariant,
|
||||||
selectedState,
|
selectedState,
|
||||||
updateSelectedStates,
|
updateSelectedStates,
|
||||||
|
|
||||||
|
// component directives
|
||||||
|
componentHas,
|
||||||
|
|
||||||
|
// component colors
|
||||||
editedBackgroundColor,
|
editedBackgroundColor,
|
||||||
|
isBackgroundColorPresent,
|
||||||
editedOpacity,
|
editedOpacity,
|
||||||
|
isOpacityPresent,
|
||||||
editedTextColor,
|
editedTextColor,
|
||||||
|
isTextColorPresent,
|
||||||
editedTextAuto,
|
editedTextAuto,
|
||||||
|
isTextAutoPresent,
|
||||||
editedLinkColor,
|
editedLinkColor,
|
||||||
|
isLinkColorPresent,
|
||||||
editedIconColor,
|
editedIconColor,
|
||||||
|
isIconColorPresent,
|
||||||
|
getContrast,
|
||||||
|
|
||||||
|
// component shadow
|
||||||
editedShadow,
|
editedShadow,
|
||||||
editedSubShadow,
|
editedSubShadow,
|
||||||
|
isShadowPresent,
|
||||||
onSubShadow,
|
onSubShadow,
|
||||||
updateSubShadow,
|
updateSubShadow,
|
||||||
getContrast,
|
|
||||||
isBackgroundColorPresent,
|
|
||||||
isOpacityPresent,
|
|
||||||
isTextColorPresent,
|
|
||||||
isTextAutoPresent,
|
|
||||||
isLinkColorPresent,
|
|
||||||
isIconColorPresent,
|
|
||||||
isShadowPresent,
|
|
||||||
previewCss,
|
|
||||||
previewClass,
|
|
||||||
editorHintStyle,
|
|
||||||
getFriendlyNamePath,
|
|
||||||
fallbackI18n,
|
|
||||||
getVariantPath,
|
|
||||||
getStatePath,
|
|
||||||
componentHas,
|
|
||||||
isShadowTabOpen,
|
isShadowTabOpen,
|
||||||
onTabSwitch,
|
onTabSwitch,
|
||||||
|
|
||||||
|
// component preview
|
||||||
|
editorHintStyle,
|
||||||
|
previewCss,
|
||||||
|
previewClass,
|
||||||
|
|
||||||
|
// ## Variables
|
||||||
|
virtualDirectives,
|
||||||
|
selectedVirtualDirective,
|
||||||
|
selectedVirtualDirectiveId,
|
||||||
|
selectedVirtualDirectiveParsed,
|
||||||
|
getNewDirective,
|
||||||
|
|
||||||
|
// ## Export and Import
|
||||||
exportStyle
|
exportStyle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,48 +87,72 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.palette-editor {
|
.list-editor {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"label editor"
|
"label editor"
|
||||||
"selector editor"
|
"selector editor"
|
||||||
"motion editor";
|
"movement editor";
|
||||||
grid-template-columns: auto 1fr;
|
grid-template-columns: 10em 1fr;
|
||||||
grid-template-rows: auto 1fr auto;
|
grid-template-rows: auto 1fr auto;
|
||||||
grid-gap: 0.5em;
|
grid-gap: 0.5em;
|
||||||
|
|
||||||
.palette-editor-edit {
|
.list-edit-area {
|
||||||
grid-area: editor;
|
grid-area: editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.palette-selector {
|
.list-select {
|
||||||
|
grid-area: selector;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
&-label {
|
&-label {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
grid-area: label;
|
grid-area: label;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
align-self: baseline;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.palette-list {
|
|
||||||
grid-area: selector;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
&-movement {
|
&-movement {
|
||||||
grid-area: motion;
|
grid-area: movement;
|
||||||
margin: 0;
|
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 {
|
.component-editor {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 6fr 3fr 4fr;
|
grid-template-columns: 6fr 3fr 4fr;
|
||||||
grid-template-rows: auto auto 1fr;
|
grid-template-rows: auto auto 1fr;
|
||||||
grid-gap: 0.5em;
|
grid-gap: 0.5em;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"component component variant"
|
"component component variant"
|
||||||
"state state state"
|
"state state state"
|
||||||
"preview settings settings";
|
"preview settings settings";
|
||||||
|
|
||||||
.component-selector {
|
.component-selector {
|
||||||
grid-area: component;
|
grid-area: component;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="StyleTab">
|
<div class="StyleTab">
|
||||||
<div class="setting-item heading">
|
<div class="setting-item heading">
|
||||||
|
<!-- TODO: This needs to go -->
|
||||||
<h2>{{ $t('settings.style.themes3.editor.title') }}</h2>
|
<h2>{{ $t('settings.style.themes3.editor.title') }}</h2>
|
||||||
<button
|
<button
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
|
@ -71,12 +72,12 @@
|
||||||
:key="'component-' + key"
|
:key="'component-' + key"
|
||||||
:value="key"
|
:value="key"
|
||||||
>
|
>
|
||||||
{{ fallbackI18n($t(getFriendlyNamePath(componentsMap.get(key).name)), componentsMap.get(key).name) }}
|
{{ componentsMap.get(key).name }}
|
||||||
</option>
|
</option>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="selectedComponentVariantsAll.length > 1"
|
v-if="selectedComponentVariants.length > 1"
|
||||||
class="variant-selector"
|
class="variant-selector"
|
||||||
>
|
>
|
||||||
<label for="variant-selector">
|
<label for="variant-selector">
|
||||||
|
@ -86,11 +87,11 @@
|
||||||
v-model="selectedVariant"
|
v-model="selectedVariant"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="variant in selectedComponentVariantsAll"
|
v-for="variant in selectedComponentVariants"
|
||||||
:key="'component-variant-' + variant"
|
:key="'component-variant-' + variant"
|
||||||
:value="variant"
|
:value="variant"
|
||||||
>
|
>
|
||||||
{{ fallbackI18n($t(getVariantPath(selectedComponentName, variant)), variant) }}
|
{{ variant }}
|
||||||
</option>
|
</option>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -112,7 +113,7 @@
|
||||||
:value="selectedState.has(state)"
|
:value="selectedState.has(state)"
|
||||||
@update:modelValue="(v) => updateSelectedStates(state, v)"
|
@update:modelValue="(v) => updateSelectedStates(state, v)"
|
||||||
>
|
>
|
||||||
{{ fallbackI18n($t(getStatePath(selectedComponentName, state)), state) }}
|
{{ state }}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -249,6 +250,7 @@
|
||||||
v-model="editedShadow"
|
v-model="editedShadow"
|
||||||
:disabled="!isShadowPresent"
|
:disabled="!isShadowPresent"
|
||||||
:no-preview="true"
|
:no-preview="true"
|
||||||
|
:compact="true"
|
||||||
:separate-inset="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'"
|
:separate-inset="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'"
|
||||||
@subShadowSelected="onSubShadow"
|
@subShadowSelected="onSubShadow"
|
||||||
/>
|
/>
|
||||||
|
@ -258,10 +260,10 @@
|
||||||
<div
|
<div
|
||||||
key="palette"
|
key="palette"
|
||||||
:label="$t('settings.style.themes3.editor.palette_tab')"
|
:label="$t('settings.style.themes3.editor.palette_tab')"
|
||||||
class="setting-item palette-editor"
|
class="setting-item list-editor palette-editor"
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
class="palette-selector-label"
|
class="list-select-label"
|
||||||
for="palette-selector"
|
for="palette-selector"
|
||||||
>
|
>
|
||||||
{{ $t('settings.style.themes3.palette.label') }}
|
{{ $t('settings.style.themes3.palette.label') }}
|
||||||
|
@ -269,9 +271,9 @@
|
||||||
</label>
|
</label>
|
||||||
<Select
|
<Select
|
||||||
id="palette-selector"
|
id="palette-selector"
|
||||||
v-model="editedPalette"
|
v-model="selectedPaletteId"
|
||||||
class="palette-list"
|
class="list-select"
|
||||||
size="9"
|
size="4"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="(p, index) in palettes"
|
v-for="(p, index) in palettes"
|
||||||
|
@ -282,17 +284,87 @@
|
||||||
</option>
|
</option>
|
||||||
</Select>
|
</Select>
|
||||||
<SelectMotion
|
<SelectMotion
|
||||||
class="palette-list-movement"
|
class="list-select-movement"
|
||||||
v-model="palettes"
|
v-model="palettes"
|
||||||
:selected-id="editedPalette"
|
|
||||||
:get-add-value="getNewPalette"
|
:get-add-value="getNewPalette"
|
||||||
@update:selectedId="e => editedPalette = e"
|
:selected-id="selectedPaletteId"
|
||||||
|
@update:selectedId="e => selectedPaletteId = e"
|
||||||
/>
|
/>
|
||||||
<PaletteEditor
|
<PaletteEditor
|
||||||
class="palette-editor-edit"
|
class="list-edit-area"
|
||||||
v-model="palette"
|
v-model="selectedPalette"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
key="variables"
|
||||||
|
:label="$t('settings.style.themes3.editor.variables_tab')"
|
||||||
|
class="setting-item list-editor variables-editor"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="list-select-label"
|
||||||
|
for="variables-selector"
|
||||||
|
>
|
||||||
|
{{ $t('settings.style.themes3.variables.label') }}
|
||||||
|
{{ ' ' }}
|
||||||
|
</label>
|
||||||
|
<Select
|
||||||
|
id="variables-selector"
|
||||||
|
v-model="selectedVirtualDirectiveId"
|
||||||
|
class="list-select"
|
||||||
|
size="9"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="(p, index) in virtualDirectives"
|
||||||
|
:key="p.name"
|
||||||
|
:value="index"
|
||||||
|
>
|
||||||
|
{{ p.name }}
|
||||||
|
</option>
|
||||||
|
</Select>
|
||||||
|
<SelectMotion
|
||||||
|
class="list-select-movement"
|
||||||
|
v-model="virtualDirectives"
|
||||||
|
:selected-id="selectedVirtualDirectiveId"
|
||||||
|
:get-add-value="getNewVirtualDirective"
|
||||||
|
@update:selectedId="e => selectedVirtualDirectiveId = e"
|
||||||
|
/>
|
||||||
|
<div class="list-edit-area">
|
||||||
|
<div class="variable-selector">
|
||||||
|
<label
|
||||||
|
class="variable-name-label"
|
||||||
|
for="variables-selector"
|
||||||
|
>
|
||||||
|
{{ $t('settings.style.themes3.variables.name_label') }}
|
||||||
|
{{ ' ' }}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
v-model="selectedVirtualDirective.name"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="variable-type-label"
|
||||||
|
for="variables-selector"
|
||||||
|
>
|
||||||
|
{{ $t('settings.style.themes3.variables.type_label') }}
|
||||||
|
{{ ' ' }}
|
||||||
|
</label>
|
||||||
|
<Select
|
||||||
|
v-model="selectedVirtualDirective.valType"
|
||||||
|
>
|
||||||
|
<option value='shadow'>
|
||||||
|
{{ $t('settings.style.themes3.variables.type_label') }}
|
||||||
|
shadow</option>
|
||||||
|
<option value='shadow'>color</option>
|
||||||
|
<option value='shadow'>generic</option>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<ShadowControl
|
||||||
|
v-if="selectedVirtualDirective.valType === 'shadow'"
|
||||||
|
v-model="selectedVirtualDirectiveParsed"
|
||||||
|
:compact="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</tab-switcher>
|
</tab-switcher>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -41,14 +41,17 @@ const toModel = (input) => {
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: [
|
props: [
|
||||||
'modelValue', 'fallback', 'separateInset', 'noPreview', 'disabled'
|
'modelValue',
|
||||||
|
'fallback',
|
||||||
|
'separateInset',
|
||||||
|
'noPreview',
|
||||||
|
'disabled',
|
||||||
|
'compact'
|
||||||
],
|
],
|
||||||
emits: ['update:modelValue', 'subShadowSelected'],
|
emits: ['update:modelValue', 'subShadowSelected'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
selectedId: 0,
|
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)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
@ -61,6 +64,14 @@ export default {
|
||||||
ComponentPreview
|
ComponentPreview
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
cValue: {
|
||||||
|
get () {
|
||||||
|
return (this.modelValue ?? this.fallback ?? []).map(toModel)
|
||||||
|
},
|
||||||
|
set (newVal) {
|
||||||
|
this.$emit('update:modelValue', newVal)
|
||||||
|
}
|
||||||
|
},
|
||||||
selectedType: {
|
selectedType: {
|
||||||
get () {
|
get () {
|
||||||
return typeof this.selected
|
return typeof this.selected
|
||||||
|
@ -115,9 +126,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
modelValue (value) {
|
|
||||||
if (!value) this.cValue = (this.modelValue ?? this.fallback ?? []).map(toModel)
|
|
||||||
},
|
|
||||||
selected (value) {
|
selected (value) {
|
||||||
this.$emit('subShadowSelected', this.selectedId)
|
this.$emit('subShadowSelected', this.selectedId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,31 @@
|
||||||
.settings-modal .settings-modal-panel .shadow-control {
|
.ShadowControl {
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-wrap: wrap;
|
grid-template-columns: 10em 1fr 1fr;
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
grid-template-areas: "selector preview tweak";
|
||||||
|
grid-gap: 0.5em;
|
||||||
justify-content: stretch;
|
justify-content: stretch;
|
||||||
grid-gap: 0.25em;
|
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
width: 100%;
|
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 {
|
.shadow-switcher {
|
||||||
|
grid-area: selector;
|
||||||
order: 1;
|
order: 1;
|
||||||
flex: 1 0 6em;
|
flex: 1 0 6em;
|
||||||
min-width: 6em;
|
min-width: 6em;
|
||||||
|
@ -20,6 +39,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.shadow-tweak {
|
.shadow-tweak {
|
||||||
|
grid-area: tweak;
|
||||||
order: 3;
|
order: 3;
|
||||||
flex: 2 0 10em;
|
flex: 2 0 10em;
|
||||||
min-width: 10em;
|
min-width: 10em;
|
||||||
|
@ -65,6 +85,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.-no-preview {
|
&.-no-preview {
|
||||||
|
grid-template-columns: 10em 1fr;
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
grid-template-areas: "selector tweak";
|
||||||
|
|
||||||
.shadow-tweak {
|
.shadow-tweak {
|
||||||
order: 0;
|
order: 0;
|
||||||
flex: 2 0 8em;
|
flex: 2 0 8em;
|
||||||
|
@ -87,11 +111,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.shadow-preview {
|
.shadow-preview {
|
||||||
order: 2;
|
grid-area: preview;
|
||||||
flex: 3 3 15em;
|
|
||||||
min-width: 10em;
|
min-width: 10em;
|
||||||
margin-left: 0.125em;
|
margin-left: 0.125em;
|
||||||
align-self: start;
|
align-self: start;
|
||||||
|
justify-self: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="label shadow-control"
|
class="ShadowControl label shadow-control"
|
||||||
:class="{ disabled: disabled || !present, '-no-preview': noPreview }"
|
:class="{ disabled: disabled || !present, '-no-preview': noPreview, '-compact': compact }"
|
||||||
>
|
>
|
||||||
<ComponentPreview
|
<ComponentPreview
|
||||||
v-if="!noPreview"
|
v-if="!noPreview"
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
id="shadow-list"
|
id="shadow-list"
|
||||||
v-model="selectedId"
|
v-model="selectedId"
|
||||||
class="shadow-list"
|
class="shadow-list"
|
||||||
size="10"
|
size="4"
|
||||||
:disabled="disabled || shadowsAreNull"
|
:disabled="disabled || shadowsAreNull"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
|
|
|
@ -14,14 +14,14 @@ export default {
|
||||||
{
|
{
|
||||||
directives: {
|
directives: {
|
||||||
background: '--fg',
|
background: '--fg',
|
||||||
shadow: ['--defaultButtonShadow', '--defaultButtonBevel'],
|
shadow: ['--buttonDefaultShadow', '--buttonDefaultBevel'],
|
||||||
roundness: 3
|
roundness: 3
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
state: ['hover'],
|
state: ['hover'],
|
||||||
directives: {
|
directives: {
|
||||||
shadow: ['--defaultButtonHoverGlow', '--defaultButtonBevel']
|
shadow: ['--buttonDefaultHoverGlow', '--buttonDefaultBevel']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -33,14 +33,14 @@ export default {
|
||||||
{
|
{
|
||||||
state: ['hover', 'active'],
|
state: ['hover', 'active'],
|
||||||
directives: {
|
directives: {
|
||||||
shadow: ['--defaultButtonShadow', '--defaultButtonBevel']
|
shadow: ['--buttonDefaultShadow', '--buttonDefaultBevel']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
state: ['disabled'],
|
state: ['disabled'],
|
||||||
directives: {
|
directives: {
|
||||||
background: '$blend(--inheritedBackground 0.25 --parent)',
|
background: '$blend(--inheritedBackground 0.25 --parent)',
|
||||||
shadow: ['--defaultButtonBevel']
|
shadow: ['--buttonDefaultBevel']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -757,7 +757,7 @@
|
||||||
"themes3": {
|
"themes3": {
|
||||||
"define": "Override",
|
"define": "Override",
|
||||||
"palette": {
|
"palette": {
|
||||||
"label": "Palette",
|
"label": "Palettes",
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
"export": "Export",
|
"export": "Export",
|
||||||
"bg": "Panel background",
|
"bg": "Panel background",
|
||||||
|
@ -774,6 +774,11 @@
|
||||||
"extra3": "Extra 3",
|
"extra3": "Extra 3",
|
||||||
"v2_unsupported": "Older v2 themes don't support palettes. Switch to v3 theme to make use of palettes"
|
"v2_unsupported": "Older v2 themes don't support palettes. Switch to v3 theme to make use of palettes"
|
||||||
},
|
},
|
||||||
|
"variables": {
|
||||||
|
"label": "Variables",
|
||||||
|
"name_label": "Name:",
|
||||||
|
"type_label": "Type:"
|
||||||
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
"title": "Style",
|
"title": "Style",
|
||||||
"new_style": "New",
|
"new_style": "New",
|
||||||
|
@ -801,45 +806,7 @@
|
||||||
},
|
},
|
||||||
"component_tab": "Components style",
|
"component_tab": "Components style",
|
||||||
"palette_tab": "Color presets",
|
"palette_tab": "Color presets",
|
||||||
"components": {
|
"variables_tab": "Variables (Advanced)"
|
||||||
"normal": {
|
|
||||||
"state": "Normal",
|
|
||||||
"variant": "Default"
|
|
||||||
},
|
|
||||||
"Alert": {
|
|
||||||
"friendlyName": "Alert",
|
|
||||||
"variants": {
|
|
||||||
"error": "Error",
|
|
||||||
"warning": "Warning",
|
|
||||||
"success": "Success"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Button": {
|
|
||||||
"friendlyName": "Button",
|
|
||||||
"variants": {
|
|
||||||
"danger": "Dangerous"
|
|
||||||
},
|
|
||||||
"states": {
|
|
||||||
"toggled": "Toggled",
|
|
||||||
"pressed": "Pressed",
|
|
||||||
"hover": "Hovered",
|
|
||||||
"focused": "Has focus",
|
|
||||||
"disabled": "Disabled"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Input": {
|
|
||||||
"friendlyName": "Input fields",
|
|
||||||
"variants": {
|
|
||||||
"checkbox": "Checkbox",
|
|
||||||
"radio": "Radio"
|
|
||||||
},
|
|
||||||
"states": {
|
|
||||||
"hover": "Hovered",
|
|
||||||
"focus": "Focused",
|
|
||||||
"disabled": "Disabled"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"hacks": {
|
"hacks": {
|
||||||
"underlay_overrides": "Change underlay",
|
"underlay_overrides": "Change underlay",
|
||||||
|
|
|
@ -627,7 +627,10 @@ export const normalizeThemeData = (input) => {
|
||||||
// We got passed a full theme file
|
// We got passed a full theme file
|
||||||
themeData = input.theme
|
themeData = input.theme
|
||||||
themeSource = input.source
|
themeSource = input.source
|
||||||
} else if (Object.prototype.hasOwnProperty.call(input, 'themeEngineVersion')) {
|
} else if (
|
||||||
|
Object.prototype.hasOwnProperty.call(input, 'themeEngineVersion') ||
|
||||||
|
Object.prototype.hasOwnProperty.call(input, 'bg')
|
||||||
|
) {
|
||||||
// We got passed a source/snapshot
|
// We got passed a source/snapshot
|
||||||
themeData = input
|
themeData = input
|
||||||
themeSource = input
|
themeSource = input
|
||||||
|
|
|
@ -2,15 +2,22 @@ import utf8 from 'utf8'
|
||||||
|
|
||||||
export const newExporter = ({
|
export const newExporter = ({
|
||||||
filename = 'data',
|
filename = 'data',
|
||||||
|
mime = 'application/json',
|
||||||
|
extension = '.json',
|
||||||
getExportedObject
|
getExportedObject
|
||||||
}) => ({
|
}) => ({
|
||||||
exportData () {
|
exportData () {
|
||||||
const stringified = utf8.encode(JSON.stringify(getExportedObject(), null, 2)) // Pretty-print and indent with 2 spaces
|
let stringified
|
||||||
|
if (mime === 'application/json') {
|
||||||
|
stringified = utf8.encode(JSON.stringify(getExportedObject(), null, 2)) // Pretty-print and indent with 2 spaces
|
||||||
|
} else {
|
||||||
|
stringified = utf8.encode(getExportedObject()) // Pretty-print and indent with 2 spaces
|
||||||
|
}
|
||||||
|
|
||||||
// Create an invisible link with a data url and simulate a click
|
// Create an invisible link with a data url and simulate a click
|
||||||
const e = document.createElement('a')
|
const e = document.createElement('a')
|
||||||
e.setAttribute('download', `${filename}.json`)
|
e.setAttribute('download', `${filename}.${extension}`)
|
||||||
e.setAttribute('href', 'data:application/json;base64,' + window.btoa(stringified))
|
e.setAttribute('href', `data:${mime};base64, ${window.btoa(stringified)}`)
|
||||||
e.style.display = 'none'
|
e.style.display = 'none'
|
||||||
|
|
||||||
document.body.appendChild(e)
|
document.body.appendChild(e)
|
||||||
|
|
Loading…
Add table
Reference in a new issue