variables + consistency in code

This commit is contained in:
Henry Jameson 2024-10-06 15:00:52 +03:00
parent da2c016ab4
commit a6863248bb
2 changed files with 228 additions and 188 deletions

View file

@ -60,7 +60,7 @@ export default {
ContrastRatio ContrastRatio
}, },
setup () { setup () {
// ### Meta stuff // ## Meta stuff
const name = ref('') const name = ref('')
const author = ref('') const author = ref('')
const license = ref('') const license = ref('')
@ -77,7 +77,7 @@ export default {
].join('\n') ].join('\n')
}) })
// ### Palette stuff // ## Palette stuff
const palettes = reactive([ const palettes = reactive([
{ {
name: 'light', name: 'light',
@ -105,29 +105,15 @@ export default {
cOrange: '#ffa500' cOrange: '#ffa500'
} }
]) ])
const selectedPaletteId = ref(0)
const palettesOut = computed(() => { const selectedPalette = computed({
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',
@ -141,11 +127,21 @@ export default {
cOrange: '#ffa500' cOrange: '#ffa500'
}) })
// ### Initialization stuff const palettesOut = computed(() => {
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')
})
// ## 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(
@ -154,23 +150,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)
@ -179,56 +175,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
@ -334,21 +280,58 @@ 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 }
}
}
// 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')
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]
@ -360,26 +343,105 @@ 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 updatePreview = () => {
try {
const { name, ...paletteData } = selectedPalette.value
const rules = init({
inputRuleset: editorFriendlyToOriginal.value,
initialStaticVars: {
...paletteData
},
ultimateBackgroundColor: '#000000',
rootComponentName: selectedComponentName.value,
editMode: true,
debug: true
}).eager
previewRules.splice(0, previewRules.length)
previewRules.push(...rules)
} catch (e) {
console.error('Could not compile preview theme', e)
}
}
const updateSelectedComponent = () => {
selectedVariant.value = 'normal'
selectedState.clear()
updatePreview()
}
updateSelectedComponent()
watch(
allEditedRules,
updatePreview
)
watch(
palettes,
updatePreview
)
watch(
selectedPalette,
updatePreview
)
watch(
selectedComponentName,
updateSelectedComponent
)
// export and import
const editorFriendlyToOriginal = computed(() => { const editorFriendlyToOriginal = computed(() => {
const resultRules = [] const resultRules = []
@ -419,83 +481,36 @@ export default {
return resultRules return resultRules
}) })
const updatePreview = () => { // ## Variables
try { const allCustomVirtualDirectives = [...componentsMap.values()]
const { name, ...paletteData } = palette.value .map(c => {
const rules = init({ console.log(c.defaultRules.filter(c => c.component === 'Root'))
inputRuleset: editorFriendlyToOriginal.value, return c
initialStaticVars: { .defaultRules
...paletteData .filter(c => c.component === 'Root')
}, .map(x => Object.entries(x.directives))
ultimateBackgroundColor: '#000000', .flat()
rootComponentName: selectedComponentName.value, })
editMode: true, .filter(x => x)
debug: true .flat()
}).eager .map(([name, value]) => {
previewRules.splice(0, previewRules.length) const [valType, valVal] = value.split('|')
previewRules.push(...rules)
} catch (e) {
console.error('Could not compile preview theme', e)
}
}
const updateSelectedComponent = () => {
selectedVariant.value = 'normal'
selectedState.clear()
updatePreview()
}
updateSelectedComponent()
watch(
allEditedRules,
updatePreview
)
watch(
palettes,
updatePreview
)
watch(
editedPalette,
updatePreview
)
watch(
selectedComponentName,
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)
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,
// same but for 18pt+ texts
laa: ratio >= 3,
laaa: ratio >= 4.5
} }
} catch (e) { })
console.warn('Failure computing contrast', e) const virtualDirectives = reactive(allCustomVirtualDirectives)
return { error: e } const selectedVirtualDirectiveId = ref(0)
} const selectedVirtualDirective = computed(() => virtualDirectives[selectedVirtualDirectiveId.value])
} const getNewDirective = () => ({
name: 'newDirective',
const isShadowTabOpen = ref(false) valType: 'generic',
const onTabSwitch = (tab) => { value: 'foobar'
isShadowTabOpen.value = tab === 'shadow' })
}
// ## Export and Import
const styleExporter = newExporter({ const styleExporter = newExporter({
filename: 'pleroma.palette.json', filename: 'pleroma.palette.json',
getExportedObject: () => exportStyleData getExportedObject: () => exportStyleData
@ -512,48 +527,73 @@ export default {
} }
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,
componentHas,
isShadowTabOpen, isShadowTabOpen,
onTabSwitch, onTabSwitch,
// component preview
editorHintStyle,
previewCss,
previewClass,
// ## Variables
virtualDirectives,
selectedVirtualDirective,
selectedVirtualDirectiveId,
getNewDirective,
// ## Export and Import
exportStyle exportStyle
} }
} }

View file

@ -76,7 +76,7 @@
</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,7 +86,7 @@
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"
> >