diff options
Diffstat (limited to 'src/components/shadow_control')
| -rw-r--r-- | src/components/shadow_control/shadow_control.js | 136 | ||||
| -rw-r--r-- | src/components/shadow_control/shadow_control.scss | 105 | ||||
| -rw-r--r-- | src/components/shadow_control/shadow_control.vue | 356 |
3 files changed, 321 insertions, 276 deletions
diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js index f8e12dbf..4521305e 100644 --- a/src/components/shadow_control/shadow_control.js +++ b/src/components/shadow_control/shadow_control.js @@ -1,9 +1,12 @@ -import ColorInput from '../color_input/color_input.vue' -import OpacityInput from '../opacity_input/opacity_input.vue' -import Select from '../select/select.vue' -import { getCssShadow } from '../../services/theme_data/theme_data.service.js' -import { hex2rgb } from '../../services/color_convert/color_convert.js' +import ColorInput from 'src/components/color_input/color_input.vue' +import OpacityInput from 'src/components/opacity_input/opacity_input.vue' +import Select from 'src/components/select/select.vue' +import Checkbox from 'src/components/checkbox/checkbox.vue' +import Popover from 'src/components/popover/popover.vue' +import ComponentPreview from 'src/components/component_preview/component_preview.vue' +import { getCssShadow, getCssShadowFilter } from '../../services/theme_data/theme_data.service.js' import { library } from '@fortawesome/fontawesome-svg-core' +import { throttle } from 'lodash' import { faTimes, faChevronDown, @@ -30,93 +33,100 @@ const toModel = (object = {}) => ({ }) export default { - // 'modelValue' and 'Fallback' can be undefined, but if they are - // initially vue won't detect it when they become something else - // therefore i'm using "ready" which should be passed as true when - // data becomes available props: [ - 'modelValue', 'fallback', 'ready' + 'modelValue', 'fallback', 'separateInset', 'noPreview', 'disabled' ], - emits: ['update:modelValue'], + emits: ['update:modelValue', 'subShadowSelected'], data () { return { 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) + cValue: (this.modelValue ?? this.fallback ?? []).map(toModel) } }, components: { ColorInput, OpacityInput, - Select + Select, + Checkbox, + Popover, + ComponentPreview + }, + beforeUpdate () { + this.cValue = (this.modelValue ?? this.fallback ?? []).map(toModel) + }, + computed: { + selected () { + const selected = this.cValue[this.selectedId] + if (selected) { + return { ...selected } + } + return null + }, + present () { + return this.selected != null && !this.usingFallback + }, + shadowsAreNull () { + return this.modelValue == null + }, + currentFallback () { + return this.fallback?.[this.selectedId] + }, + moveUpValid () { + return this.selectedId > 0 + }, + moveDnValid () { + return this.selectedId < this.cValue.length - 1 + }, + usingFallback () { + return this.modelValue == null + }, + style () { + if (this.separateInset) { + return { + filter: getCssShadowFilter(this.cValue), + boxShadow: getCssShadow(this.cValue, true) + } + } + return { + boxShadow: getCssShadow(this.cValue) + } + } + }, + watch: { + selected (value) { + this.$emit('subShadowSelected', this.selectedId) + } }, methods: { + updateProperty: throttle(function (prop, value) { + this.cValue[this.selectedId][prop] = value + if (prop === 'inset' && value === false && this.separateInset) { + this.cValue[this.selectedId].spread = 0 + } + this.$emit('update:modelValue', this.cValue) + }, 100), add () { this.cValue.push(toModel(this.selected)) - this.selectedId = this.cValue.length - 1 + this.selectedId = Math.max(this.cValue.length - 1, 0) + this.$emit('update:modelValue', this.cValue) }, del () { this.cValue.splice(this.selectedId, 1) this.selectedId = this.cValue.length === 0 ? undefined : Math.max(this.selectedId - 1, 0) + this.$emit('update:modelValue', this.cValue) }, moveUp () { const movable = this.cValue.splice(this.selectedId, 1)[0] this.cValue.splice(this.selectedId - 1, 0, movable) this.selectedId -= 1 + this.$emit('update:modelValue', this.cValue) }, moveDn () { const movable = this.cValue.splice(this.selectedId, 1)[0] this.cValue.splice(this.selectedId + 1, 0, movable) this.selectedId += 1 - } - }, - beforeUpdate () { - this.cValue = this.modelValue || this.fallback - }, - computed: { - anyShadows () { - return this.cValue.length > 0 - }, - anyShadowsFallback () { - return this.fallback.length > 0 - }, - selected () { - if (this.ready && this.anyShadows) { - return this.cValue[this.selectedId] - } else { - return toModel({}) - } - }, - currentFallback () { - if (this.ready && this.anyShadowsFallback) { - return this.fallback[this.selectedId] - } else { - return toModel({}) - } - }, - moveUpValid () { - return this.ready && this.selectedId > 0 - }, - moveDnValid () { - return this.ready && this.selectedId < this.cValue.length - 1 - }, - present () { - return this.ready && - typeof this.cValue[this.selectedId] !== 'undefined' && - !this.usingFallback - }, - usingFallback () { - return typeof this.modelValue === 'undefined' - }, - rgb () { - return hex2rgb(this.selected.color) - }, - style () { - return this.ready - ? { - boxShadow: getCssShadow(this.fallback) - } - : {} + this.$emit('update:modelValue', this.cValue) } } } diff --git a/src/components/shadow_control/shadow_control.scss b/src/components/shadow_control/shadow_control.scss new file mode 100644 index 00000000..dd049023 --- /dev/null +++ b/src/components/shadow_control/shadow_control.scss @@ -0,0 +1,105 @@ +.settings-modal .settings-modal-panel .shadow-control { + display: flex; + flex-wrap: wrap; + justify-content: stretch; + grid-gap: 0.25em; + margin-bottom: 1em; + + .shadow-switcher { + order: 1; + flex: 1 0 6em; + min-width: 6em; + margin-right: 0.125em; + display: flex; + flex-direction: column; + + .shadow-list { + flex: 1 0 auto; + } + + .arrange-buttons { + flex: 0 0 auto; + display: grid; + grid-auto-columns: 1fr; + grid-auto-flow: column; + margin-top: 0.25em; + + .button-default { + margin: 0; + padding: 0; + } + } + } + + .shadow-tweak { + order: 3; + flex: 2 0 10em; + min-width: 10em; + margin-left: 0.125em; + margin-right: 0.125em; + + /* hack */ + .input-boolean { + flex: 1; + display: flex; + + .label { + flex: 1; + } + } + + .input-string { + flex: 1 0 5em; + } + + .id-control { + align-items: stretch; + + .shadow-switcher, + .btn { + min-width: 1px; + margin-right: 5px; + } + + .btn { + padding: 0 0.4em; + margin: 0 0.1em; + } + } + } + + &.-no-preview { + .shadow-tweak { + order: 0; + flex: 2 0 8em; + max-width: 100%; + } + + .input-range { + min-width: 5em; + } + } + + .inset-alert { + padding: 0.25em 0.5em; + } + + &.disabled { + .inset-alert { + opacity: 0.2; + } + } + + .shadow-preview { + order: 2; + flex: 3 3 15em; + min-width: 10em; + margin-left: 0.125em; + align-self: start; + } +} + +.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 index c3b956cd..e1d20191 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -1,91 +1,51 @@ <template> <div - class="shadow-control" - :class="{ disabled: !present }" + class="label shadow-control" + :class="{ disabled: disabled || !present, '-no-preview': noPreview }" > - <div class="shadow-preview-container"> - <div - :disabled="!present" - class="y-shift-control" - > - <input - v-model="selected.y" - :disabled="!present" - class="input input-number" - type="number" - > - <div class="wrap"> - <input - v-model="selected.y" - :disabled="!present" - class="input input-range" - type="range" - max="20" - min="-20" - > - </div> - </div> - <div class="preview-window"> - <div - class="preview-block" - :style="style" - /> - </div> - <div - :disabled="!present" - class="x-shift-control" + <ComponentPreview + v-if="!noPreview" + class="shadow-preview" + :shadow-control="true" + :shadow="selected" + :preview-style="style" + :disabled="disabled || !present" + @update:shadow="({ axis, value }) => updateProperty(axis, value)" + /> + <div class="shadow-switcher"> + <Select + id="shadow-list" + v-model="selectedId" + class="shadow-list" + size="10" + :disabled="shadowsAreNull" > - <input - v-model="selected.x" - :disabled="!present" - class="input input-number" - type="number" + <option + v-for="(shadow, index) in cValue" + :key="index" + :value="index" + :class="{ '-active': index === Number(selectedId) }" > - <div class="wrap"> - <input - v-model="selected.x" - :disabled="!present" - class="input input-range" - type="range" - max="20" - min="-20" - > - </div> - </div> - </div> - - <div class="shadow-tweak"> + {{ shadow?.name ?? $t('settings.style.shadows.shadow_id', { value: index }) }} + </option> + </Select> <div - :disabled="usingFallback" - class="id-control style-control" + class="id-control btn-group arrange-buttons" > - <Select - id="shadow-switcher" - v-model="selectedId" - class="shadow-switcher" - :disabled="!ready || usingFallback" - > - <option - v-for="(shadow, index) in cValue" - :key="index" - :value="index" - > - {{ $t('settings.style.shadows.shadow_id', { value: index }) }} - </option> - </Select> <button class="btn button-default" - :disabled="!ready || !present" - @click="del" + :disabled="disabled || shadowsAreNull" + @click="add" > <FAIcon fixed-width - icon="times" + icon="plus" /> </button> <button class="btn button-default" - :disabled="!moveUpValid" + :disabled="disabled || !moveUpValid" + :class="{ disabled: disabled || !moveUpValid }" @click="moveUp" > <FAIcon @@ -95,7 +55,8 @@ </button> <button class="btn button-default" - :disabled="!moveDnValid" + :disabled="disabled || !moveDnValid" + :class="{ disabled: disabled || !moveDnValid }" @click="moveDn" > <FAIcon @@ -105,222 +66,191 @@ </button> <button class="btn button-default" - :disabled="usingFallback" - @click="add" + :disabled="disabled || !present" + :class="{ disabled: disabled || !present }" + @click="del" > <FAIcon fixed-width - icon="plus" + icon="times" /> </button> </div> + </div> + <div class="shadow-tweak"> <div - :disabled="!present" - class="inset-control style-control" + :class="{ disabled: disabled || !present }" + class="name-control style-control" > <label - for="inset" + for="name" class="label" + :class="{ faint: disabled || !present }" > - {{ $t('settings.style.shadows.inset') }} + {{ $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="inset-control style-control" + > + <Checkbox id="inset" - v-model="selected.inset" - :disabled="!present" + :value="selected?.inset" + :disabled="disabled || !present" name="inset" - class="input -checkbox input-inset visible-for-screenreader-only" - type="checkbox" + class="input-inset input-boolean" + @input="e => updateProperty('inset', e.target.checked)" > - <label - class="checkbox-label" - for="inset" - :aria-hidden="true" - /> + <template #before> + {{ $t('settings.style.shadows.inset') }} + </template> + </Checkbox> </div> <div - :disabled="!present" + :disabled="disabled || !present" + :class="{ disabled: disabled || !present }" class="blur-control style-control" > <label - for="spread" + for="blur" class="label" + :class="{ faint: disabled || !present }" > {{ $t('settings.style.shadows.blur') }} </label> <input id="blur" - v-model="selected.blur" - :disabled="!present" + :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 - v-model="selected.blur" - :disabled="!present" - class="input input-number" + :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 - :disabled="!present" 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" - v-model="selected.spread" - :disabled="!present" + :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 - v-model="selected.spread" - :disabled="!present" - class="input input-number" + :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 - v-model="selected.color" - :disabled="!present" + :model-value="selected?.color" + :disabled="disabled || !present" :label="$t('settings.style.common.color')" - :fallback="currentFallback.color" + :fallback="currentFallback?.color" :show-optional-tickbox="false" name="shadow" + @update:modelValue="e => updateProperty('color', e)" /> <OpacityInput - v-model="selected.alpha" - :disabled="!present" + :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" + > + <FAIcon icon="exclamation-triangle" /> + + {{ $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" + > + <code>box-shadow</code> + </i18n-t> + <p>{{ $t('settings.style.shadows.filter_hint.spread_zero') }}</p> + </div> + </template> + </Popover> </div> </div> </template> <script src="./shadow_control.js"></script> -<style lang="scss"> -.shadow-control { - display: flex; - flex-wrap: wrap; - justify-content: center; - margin-bottom: 1em; - - .shadow-preview-container, - .shadow-tweak { - margin: 5px 6px 0 0; - } - - .shadow-preview-container { - flex: 0; - display: flex; - flex-wrap: wrap; - - input[type="number"] { - width: 5em; - min-width: 2em; - } - - .x-shift-control, - .y-shift-control { - display: flex; - flex: 0; - - &[disabled="disabled"] * { - opacity: 0.5; - } - } - - .x-shift-control { - align-items: flex-start; - } - - .x-shift-control .wrap, - input[type="range"] { - margin: 0; - width: 15em; - height: 2em; - } - - .y-shift-control { - flex-direction: column; - align-items: flex-end; - - .wrap { - width: 2em; - height: 15em; - } - - input[type="range"] { - transform-origin: 1em 1em; - transform: rotate(90deg); - } - } - - .preview-window { - flex: 1; - background-color: #999; - display: flex; - align-items: center; - justify-content: center; - background-image: - linear-gradient(45deg, #666 25%, transparent 25%), - linear-gradient(-45deg, #666 25%, transparent 25%), - linear-gradient(45deg, transparent 75%, #666 75%), - linear-gradient(-45deg, transparent 75%, #666 75%); - background-size: 20px 20px; - background-position: 0 0, 0 10px, 10px -10px, -10px 0; - border-radius: var(--roundness); - - .preview-block { - width: 33%; - height: 33%; - border-radius: var(--roundness); - } - } - } - - .shadow-tweak { - flex: 1; - min-width: 280px; - - .id-control { - align-items: stretch; - - .shadow-switcher { - flex: 1; - } - - .shadow-switcher, - .btn { - min-width: 1px; - margin-right: 5px; - } - - .btn { - padding: 0 0.4em; - margin: 0 0.1em; - } - } - } -} -</style> +<style src="./shadow_control.scss" lang="scss"></style> |
