logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://anongit.hacktivis.me/git/pleroma-fe.git/
commit: c937736feab589fb123b8208792d5f12e3a056de
parent 24663b2f042e1e384fc3ecd1e5f4759ca986bdc7
Author: Henry Jameson <me@hjkos.com>
Date:   Fri,  4 Oct 2024 00:27:53 +0300

shadow editor now can handle expressions (functions and variables)

Diffstat:

Msrc/components/settings_modal/tabs/style_tab/style_tab.js37+++++++++++++++++++++++--------------
Msrc/components/settings_modal/tabs/style_tab/style_tab.vue2+-
Msrc/components/shadow_control/shadow_control.js78+++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/components/shadow_control/shadow_control.scss10+++++++++-
Msrc/components/shadow_control/shadow_control.vue339++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/i18n/en.json3+++
Msrc/services/theme_data/theme_data_3.service.js7++++---
7 files changed, 277 insertions(+), 199 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 @@ -219,9 +219,13 @@ export default { return selectors.map(x => x.substring(1)).join('') }) const previewCss = computed(() => { - const scoped = getCssRules(previewRules) - .map(simulatePseudoSelectors) - return scoped.join('\n') + 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 @@ -415,17 +419,22 @@ export default { }) const updatePreview = () => { - previewRules.splice(0, previewRules.length) - previewRules.push(...init({ - inputRuleset: editorFriendlyToOriginal.value, - initialStaticVars: { - ...palette.value - }, - ultimateBackgroundColor: '#000000', - rootComponentName: selectedComponentName.value, - editMode: true, - debug: true - }).eager) + try { + const rules = init({ + inputRuleset: editorFriendlyToOriginal.value, + initialStaticVars: { + ...palette.value + }, + 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 = () => { diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.vue b/src/components/settings_modal/tabs/style_tab/style_tab.vue @@ -125,7 +125,7 @@ :shadow-control="isShadowTabOpen" :preview-class="previewClass" :preview-style="editorHintStyle" - :disabled="!editedSubShadow" + :disabled="!editedSubShadow && typeof editedShadow !== 'string'" :shadow="editedSubShadow" @update:shadow="({ axis, value }) => updateSubShadow(axis, value)" /> diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js @@ -21,16 +21,22 @@ library.add( faPlus ) -const toModel = (object = {}) => ({ - x: 0, - y: 0, - blur: 0, - spread: 0, - inset: false, - color: '#000000', - alpha: 1, - ...object -}) +const toModel = (input) => { + if (typeof input === 'object') { + return { + x: 0, + y: 0, + blur: 0, + spread: 0, + inset: false, + color: '#000000', + alpha: 1, + ...input + } + } else if (typeof input === 'string') { + return input + } +} export default { props: [ @@ -56,12 +62,29 @@ export default { this.cValue = (this.modelValue ?? this.fallback ?? []).map(toModel) }, computed: { - selected () { - const selected = this.cValue[this.selectedId] - if (selected) { - return { ...selected } + selectedType: { + get () { + return typeof this.selected + }, + set (newType) { + this.selected = toModel(newType === 'object' ? {} : '') + } + }, + selected: { + get () { + const selected = this.cValue[this.selectedId] + console.log('SELECTED', selected) + if (selected && typeof selected === 'object') { + return { ...selected } + } else if (typeof selected === 'string') { + return selected + } + return null + }, + set (value) { + this.cValue[this.selectedId] = toModel(value) + this.$emit('update:modelValue', this.cValue) } - return null }, present () { return this.selected != null && !this.usingFallback @@ -82,14 +105,20 @@ export default { return this.modelValue == null }, style () { - if (this.separateInset) { + try { + if (this.separateInset) { + return { + filter: getCssShadowFilter(this.cValue), + boxShadow: getCssShadow(this.cValue, true) + } + } return { - filter: getCssShadowFilter(this.cValue), - boxShadow: getCssShadow(this.cValue, true) + boxShadow: getCssShadow(this.cValue) + } + } catch (e) { + return { + border: '1px solid red' } - } - return { - boxShadow: getCssShadow(this.cValue) } } }, @@ -99,6 +128,13 @@ export default { } }, methods: { + getSubshadowLabel (shadow, index) { + if (typeof shadow === 'object') { + return shadow?.name ?? this.$t('settings.style.shadows.shadow_id', { value: index }) + } else if (typeof shadow === 'string') { + return shadow || this.$t('settings.style.shadows.empty_expression') + } + }, updateProperty: throttle(function (prop, value) { this.cValue[this.selectedId][prop] = value if (prop === 'inset' && value === false && this.separateInset) { diff --git a/src/components/shadow_control/shadow_control.scss b/src/components/shadow_control/shadow_control.scss @@ -4,6 +4,7 @@ justify-content: stretch; grid-gap: 0.25em; margin-bottom: 1em; + width: 100%; .shadow-switcher { order: 1; @@ -37,6 +38,9 @@ min-width: 10em; margin-left: 0.125em; margin-right: 0.125em; + display: grid; + grid-template-rows: auto 1fr; + grid-gap: 0.25em; /* hack */ .input-boolean { @@ -52,6 +56,11 @@ flex: 1 0 5em; } + .shadow-expression { + width: 100%; + height: 100%; + } + .id-control { align-items: stretch; @@ -100,6 +109,5 @@ } .inset-tooltip { - padding: 0.5em; max-width: 30em; } diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue @@ -8,7 +8,6 @@ class="shadow-preview" :shadow-control="true" :shadow="selected" - :preview-style="style" :disabled="disabled || !present" @update:shadow="({ axis, value }) => updateProperty(axis, value)" /> @@ -18,7 +17,7 @@ v-model="selectedId" class="shadow-list" size="10" - :disabled="shadowsAreNull" + :disabled="disabled || shadowsAreNull" > <option v-for="(shadow, index) in cValue" @@ -26,7 +25,7 @@ :value="index" :class="{ '-active': index === Number(selectedId) }" > - {{ shadow?.name ?? $t('settings.style.shadows.shadow_id', { value: index }) }} + {{ getSubshadowLabel(shadow, index) }} </option> </Select> <div @@ -78,175 +77,197 @@ </div> </div> <div class="shadow-tweak"> - <div - :class="{ disabled: disabled || !present }" - class="name-control style-control" - > - <label - for="name" - class="label" - :class="{ faint: disabled || !present }" - > - {{ $t('settings.style.shadows.name') }} - </label> - <input - id="name" - :value="selected?.name" - :disabled="disabled || !present" - :class="{ disabled: disabled || !present }" - name="name" - class="input input-string" - @input="e => updateProperty('name', e.target.value)" - > - </div> - <div + <Select + v-model="selectedType" :disabled="disabled || !present" - class="inset-control style-control" > - <Checkbox - id="inset" - :value="selected?.inset" - :disabled="disabled || !present" - name="inset" - class="input-inset input-boolean" - @input="e => updateProperty('inset', e.target.checked)" + <option value="object"> + {{ $t('settings.style.shadows.raw') }} + </option> + <option value="string"> + {{ $t('settings.style.shadows.expression') }} + </option> + </Select> + <template v-if="selectedType === 'string'"> + <textarea + v-model="selected" + class="input shadow-expression" + :disabled="disabled || shadowsAreNull" + :class="{disabled: disabled || shadowsAreNull}" > - <template #before> - {{ $t('settings.style.shadows.inset') }} - </template> - </Checkbox> - </div> - <div - :disabled="disabled || !present" - :class="{ disabled: disabled || !present }" - class="blur-control style-control" - > - <label - for="blur" - class="label" - :class="{ faint: disabled || !present }" + </textarea> + </template> + <template v-else-if="selectedType === 'object'"> + <div + :class="{ disabled: disabled || !present }" + class="name-control style-control" > - {{ $t('settings.style.shadows.blur') }} - </label> - <input - id="blur" - :value="selected?.blur" + <label + for="name" + class="label" + :class="{ faint: disabled || !present }" + > + {{ $t('settings.style.shadows.name') }} + </label> + <input + id="name" + :value="selected?.name" + :disabled="disabled || !present" + :class="{ disabled: disabled || !present }" + name="name" + class="input input-string" + @input="e => updateProperty('name', e.target.value)" + > + </div> + <div :disabled="disabled || !present" - :class="{ disabled: disabled || !present }" - name="blur" - class="input input-range" - type="range" - max="20" - min="0" - @input="e => updateProperty('blur', e.target.value)" + class="inset-control style-control" > - <input - :value="selected?.blur" - class="input input-number -small" + <Checkbox + id="inset" + :value="selected?.inset" + :disabled="disabled || !present" + name="inset" + class="input-inset input-boolean" + @input="e => updateProperty('inset', e.target.checked)" + > + <template #before> + {{ $t('settings.style.shadows.inset') }} + </template> + </Checkbox> + </div> + <div :disabled="disabled || !present" :class="{ disabled: disabled || !present }" - type="number" - min="0" - @input="e => updateProperty('blur', e.target.value)" + class="blur-control style-control" > - </div> - <div - class="spread-control style-control" - :class="{ disabled: disabled || !present || (separateInset && !selected?.inset) }" - > - <label - for="spread" - class="label" - :class="{ faint: disabled || !present || (separateInset && !selected?.inset) }" - > - {{ $t('settings.style.shadows.spread') }} - </label> - <input - id="spread" - :value="selected?.spread" - :disabled="disabled || !present || (separateInset && !selected?.inset)" - :class="{ disabled: disabled || !present || (separateInset && !selected?.inset) }" - name="spread" - class="input input-range" - type="range" - max="20" - min="-20" - @input="e => updateProperty('spread', e.target.value)" - > - <input - :value="selected?.spread" - class="input input-number -small" + <label + for="blur" + class="label" + :class="{ faint: disabled || !present }" + > + {{ $t('settings.style.shadows.blur') }} + </label> + <input + id="blur" + :value="selected?.blur" + :disabled="disabled || !present" + :class="{ disabled: disabled || !present }" + name="blur" + class="input input-range" + type="range" + max="20" + min="0" + @input="e => updateProperty('blur', e.target.value)" + > + <input + :value="selected?.blur" + class="input input-number -small" + :disabled="disabled || !present" + :class="{ disabled: disabled || !present }" + type="number" + min="0" + @input="e => updateProperty('blur', e.target.value)" + > + </div> + <div + class="spread-control style-control" :class="{ disabled: disabled || !present || (separateInset && !selected?.inset) }" - :disabled="{ disabled: disabled || !present || (separateInset && !selected?.inset) }" - type="number" - @input="e => updateProperty('spread', e.target.value)" > - </div> - <ColorInput - :model-value="selected?.color" - :disabled="disabled || !present" - :label="$t('settings.style.common.color')" - :fallback="currentFallback?.color" - :show-optional-tickbox="false" - name="shadow" - @update:modelValue="e => updateProperty('color', e)" - /> - <OpacityInput - :model-value="selected?.alpha" - :disabled="disabled || !present" - @update:modelValue="e => updateProperty('alpha', e)" - /> - <i18n-t - scope="global" - keypath="settings.style.shadows.hintV3" - :class="{ faint: disabled || !present }" - tag="p" - > - <code>--variable,mod</code> - </i18n-t> - <Popover - v-if="separateInset" - trigger="hover" - > - <template #trigger> - <div - class="inset-alert alert warning" + <label + for="spread" + class="label" + :class="{ faint: disabled || !present || (separateInset && !selected?.inset) }" > - <FAIcon icon="exclamation-triangle" /> - &nbsp; - {{ $t('settings.style.shadows.filter_hint.avatar_inset_short') }} - </div> - </template> - <template #content> - <div class="inset-tooltip"> - <i18n-t - scope="global" - keypath="settings.style.shadows.filter_hint.always_drop_shadow" - tag="p" - > - <code>filter: drop-shadow()</code> - </i18n-t> - <p>{{ $t('settings.style.shadows.filter_hint.avatar_inset') }}</p> - <i18n-t - scope="global" - keypath="settings.style.shadows.filter_hint.drop_shadow_syntax" - tag="p" - > - <code>drop-shadow</code> - <code>spread-radius</code> - <code>inset</code> - </i18n-t> - <i18n-t - scope="global" - keypath="settings.style.shadows.filter_hint.inset_classic" - tag="p" + {{ $t('settings.style.shadows.spread') }} + </label> + <input + id="spread" + :value="selected?.spread" + :disabled="disabled || !present || (separateInset && !selected?.inset)" + :class="{ disabled: disabled || !present || (separateInset && !selected?.inset) }" + name="spread" + class="input input-range" + type="range" + max="20" + min="-20" + @input="e => updateProperty('spread', e.target.value)" + > + <input + :value="selected?.spread" + class="input input-number -small" + :class="{ disabled: disabled || !present || (separateInset && !selected?.inset) }" + :disabled="{ disabled: disabled || !present || (separateInset && !selected?.inset) }" + type="number" + @input="e => updateProperty('spread', e.target.value)" + > + </div> + <ColorInput + :model-value="selected?.color" + :disabled="disabled || !present" + :label="$t('settings.style.common.color')" + :fallback="currentFallback?.color" + :show-optional-tickbox="false" + name="shadow" + @update:modelValue="e => updateProperty('color', e)" + /> + <OpacityInput + :model-value="selected?.alpha" + :disabled="disabled || !present" + @update:modelValue="e => updateProperty('alpha', e)" + /> + <i18n-t + scope="global" + keypath="settings.style.shadows.hintV3" + :class="{ faint: disabled || !present }" + tag="p" + > + <code>--variable,mod</code> + </i18n-t> + <Popover + v-if="separateInset" + trigger="hover" + > + <template #trigger> + <div + class="inset-alert alert warning" > - <code>box-shadow</code> - </i18n-t> - <p>{{ $t('settings.style.shadows.filter_hint.spread_zero') }}</p> - </div> - </template> - </Popover> + <FAIcon icon="exclamation-triangle" /> + &nbsp; + {{ $t('settings.style.shadows.filter_hint.avatar_inset_short') }} + </div> + </template> + <template #content> + <div class="inset-tooltip tooltip"> + <i18n-t + scope="global" + keypath="settings.style.shadows.filter_hint.always_drop_shadow" + tag="p" + > + <code>filter: drop-shadow()</code> + </i18n-t> + <p>{{ $t('settings.style.shadows.filter_hint.avatar_inset') }}</p> + <i18n-t + scope="global" + keypath="settings.style.shadows.filter_hint.drop_shadow_syntax" + tag="p" + > + <code>drop-shadow</code> + <code>spread-radius</code> + <code>inset</code> + </i18n-t> + <i18n-t + scope="global" + keypath="settings.style.shadows.filter_hint.inset_classic" + tag="p" + > + <code>box-shadow</code> + </i18n-t> + <p>{{ $t('settings.style.shadows.filter_hint.spread_zero') }}</p> + </div> + </template> + </Popover> + </template> </div> </div> </template> diff --git a/src/i18n/en.json b/src/i18n/en.json @@ -966,6 +966,9 @@ "blur": "Blur", "spread": "Spread", "inset": "Inset", + "raw": "Plain shadow", + "expression": "Expression (advanced)", + "empty_expression": "Empty expression", "hintV3": "For shadows you can also use the {0} notation to use other color slot.", "filter_hint": { "always_drop_shadow": "Warning, this shadow always uses {0} when browser supports it.", diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js @@ -44,8 +44,8 @@ const findShadow = (shadows, { dynamicVars, staticVars }) => { if (shadow.startsWith('$')) { targetShadow = process(shadow, shadowFunctions, { findColor, findShadow }, { dynamicVars, staticVars }) } else if (shadow.startsWith('--')) { - const [variable] = shadow.split(/,/g).map(str => str.trim()) // discarding modifier since it's not supported - const variableSlot = variable.substring(2) + // modifiers are completely unsupported here + const variableSlot = shadow.substring(2) return findShadow(staticVars[variableSlot], { dynamicVars, staticVars }) } else { targetShadow = parseShadow(shadow) @@ -66,6 +66,7 @@ const findColor = (color, { dynamicVars, staticVars }) => { if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color let targetColor = null if (color.startsWith('--')) { + // Modifier support is pretty much for v2 themes only const [variable, modifier] = color.split(/,/g).map(str => str.trim()) const variableSlot = variable.substring(2) if (variableSlot === 'stack') { @@ -421,7 +422,7 @@ export const init = ({ break } case 'shadow': { - const shadow = value.split(/,/g).map(s => s.trim()) + const shadow = value.split(/,/g).map(s => s.trim()).filter(x => x) dynamicVars[k] = shadow if (combination.component === rootComponentName) { staticVars[k.substring(2)] = shadow