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: 9753db1c6798c0a581e9eebcdb3f46c4e075b780
parent 2a98ea6ddc5e3c602b4aad768e9452666438fb95
Author: Henry Jameson <me@hjkos.com>
Date:   Sun, 29 Sep 2024 03:20:14 +0300

it works!

Diffstat:

Msrc/components/settings_modal/tabs/style_tab/style_tab.js115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/components/settings_modal/tabs/style_tab/style_tab.scss4++++
Msrc/components/settings_modal/tabs/style_tab/style_tab.vue41+++++++++++++++++++++++++++++++++++++++++
Msrc/i18n/en.json6++++++
4 files changed, 152 insertions(+), 14 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 @@ -10,9 +10,15 @@ import ColorInput from 'src/components/color_input/color_input.vue' import OpacityInput from 'src/components/opacity_input/opacity_input.vue' import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' import Popover from 'src/components/popover/popover.vue' +import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue' import { init } from 'src/services/theme_data/theme_data_3.service.js' import { getCssRules } from 'src/services/theme_data/css_utils.js' +import { + // rgb2hex, + hex2rgb, + getContrastRatio +} from 'src/services/color_convert/color_convert.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faFloppyDisk, faFolderOpen, faFile } from '@fortawesome/free-solid-svg-icons' @@ -40,7 +46,8 @@ export default { TabSwitcher, ShadowControl, ColorInput, - OpacityInput + OpacityInput, + ContrastRatio }, setup () { // Meta stuff @@ -153,7 +160,10 @@ export default { return scoped.join('\n') }) - // Rules stuff aka meat and potatoes + // ### Rules stuff aka meat and potatoes + // The native structure of separate rules and the child -> parent + // relation isn't very convenient for editor, we replace the array + // and child -> parent structure with map and parent -> child structure const editorFriendlyFallbackStructure = computed(() => { const root = {} @@ -251,27 +261,66 @@ export default { const editedBackgroundColor = getEditedElement(null, 'background') const editedOpacity = getEditedElement(null, 'opacity') - const editedTextColor = getEditedElement('Text', 'color') - const editedLinkColor = getEditedElement('Link', 'color') - const editedIconColor = getEditedElement('Icon', 'color') + const editedTextColor = getEditedElement('Text', 'textColor') + const editedTextAuto = getEditedElement('Text', 'textAuto') + const editedLinkColor = getEditedElement('Link', 'textColor') + const editedIconColor = getEditedElement('Icon', 'textColor') const editedShadow = getEditedElement(null, 'shadow') const isBackgroundColorPresent = isElementPresent(null, 'background', '#FFFFFF') const isOpacityPresent = isElementPresent(null, 'opacity', 1) - const isTextColorPresent = isElementPresent('Text', 'color', '#000000') - const isLinkColorPresent = isElementPresent('Link', 'color', '#000080') - const isIconColorPresent = isElementPresent('Icon', 'color', '#909090') + const isTextColorPresent = isElementPresent('Text', 'textColor', '#000000') + const isTextAutoPresent = isElementPresent('Text', 'textAuto', '#000000') + const isLinkColorPresent = isElementPresent('Link', 'textColor', '#000080') + const isIconColorPresent = isElementPresent('Icon', 'textColor', '#909090') const isShadowPresent = isElementPresent(null, 'shadow', []) - const updateSelectedComponent = () => { - selectedVariant.value = 'normal' - selectedState.clear() + const editorFriendlyToOriginal = computed(() => { + const resultRules = [] + + const convert = (component, data = {}, parent) => { + const variants = Object.entries(data || {}) + + variants.forEach(([variant, variantData]) => { + const states = Object.entries(variantData) + + states.forEach(([jointState, stateData]) => { + const state = jointState.split(/:/g) + const result = { + component, + variant, + state, + directives: stateData.directives || {} + } + + console.log('PARENT', parent) + if (parent) { + result.parent = { + component: parent + } + } + + resultRules.push(result) + + // Currently we only support single depth for simplicity's sake + if (!parent) { + Object.entries(stateData._children || {}).forEach(([cName, child]) => convert(cName, child, component)) + } + }) + }) + } + convert(selectedComponentName.value, allEditedRules[selectedComponentName.value]) + console.log(toValue(allEditedRules)) + console.log(toValue(resultRules)) + + return resultRules + }) + + const updatePreview = () => { previewRules.splice(0, previewRules.length) previewRules.push(...init({ - inputRuleset: [{ - component: selectedComponentName.value - }], + inputRuleset: editorFriendlyToOriginal.value, initialStaticVars: { ...palette }, @@ -282,13 +331,48 @@ export default { }).eager) } + const updateSelectedComponent = () => { + selectedVariant.value = 'normal' + selectedState.clear() + updatePreview() + } updateSelectedComponent() watch( + allEditedRules, + updatePreview + ) + + watch( selectedComponentName, updateSelectedComponent ) + // TODO this is VERY primitive right now, need to make it + // support variables, fallbacks etc. + const getContrast = (bg, text) => { + console.log('CONTRAST', bg, text) + try { + const bgRgb = hex2rgb(bg) + const textRgb = hex2rgb(text) + + const ratio = getContrastRatio(bgRgb, textRgb) + return { + 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 isShadowTabOpen = ref(false) const onTabSwitch = (tab) => { isShadowTabOpen.value = tab === 'shadow' @@ -318,12 +402,15 @@ export default { editedBackgroundColor, editedOpacity, editedTextColor, + editedTextAuto, editedLinkColor, editedIconColor, editedShadow, + getContrast, isBackgroundColorPresent, isOpacityPresent, isTextColorPresent, + isTextAutoPresent, isLinkColorPresent, isIconColorPresent, isShadowPresent, diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.scss b/src/components/settings_modal/tabs/style_tab/style_tab.scss @@ -11,6 +11,10 @@ line-height: 2; } + &.suboption { + margin-left: 1em; + } + .opt { margin: 0.5em; } diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.vue b/src/components/settings_modal/tabs/style_tab/style_tab.vue @@ -183,6 +183,47 @@ {{ $t('settings.style.themes3.editor.include_in_rule') }} </template> </Popover> + <div class="style-control suboption"> + <label + for="textAuto" + class="label" + :class="{ faint: disabled || !present }" + > + {{ $t('settings.style.themes3.editor.text_auto.label') }} + </label> + <Select + id="textAuto" + v-model="editedTextAuto" + :disabled="!isTextAutoPresent" + > + <option value="no-preserve"> + {{ $t('settings.style.themes3.editor.text_auto.no-preserve') }} + </option> + <option value="no-auto"> + {{ $t('settings.style.themes3.editor.text_auto.no-auto') }} + </option> + <option value="preserve"> + {{ $t('settings.style.themes3.editor.text_auto.preserve') }} + </option> + </Select> + </div> + <Popover + trigger="hover" + v-if="componentHas('Text')" + > + <template #trigger> + <Checkbox v-model="isTextAutoPresent" /> + </template> + <template #content> + {{ $t('settings.style.themes3.editor.include_in_rule') }} + </template> + </Popover> + <div> + <ContrastRatio :contrast="getContrast(editedBackgroundColor, editedTextColor)" /> + </div> + <div> + <!-- spacer for missing checkbox --> + </div> <ColorInput v-model="editedLinkColor" :label="$t('settings.style.themes3.editor.link_color')" diff --git a/src/i18n/en.json b/src/i18n/en.json @@ -772,6 +772,12 @@ "icon_color": "Icon color", "link_color": "Link color", "include_in_rule": "Add to rule", + "text_auto": { + "label": "Auto-contrast", + "no-preserve": "Black or White", + "preserve": "Keep color", + "no-auto": "Disabled" + }, "components": { "normal": { "state": "Normal",