2019-06-06 16:33:14 -04:00
|
|
|
<template lang="html">
|
2019-06-09 19:48:26 -04:00
|
|
|
<div v-on-clickaway="close" class="ffz--icon-picker tw-relative">
|
|
|
|
<div class="tw-search-input tw-full-width">
|
|
|
|
<label v-if="open" :for="'icon-search$' + id" class="tw-hide-accessible">{{ t('setting.icon.search', 'Search for Icon') }}</label>
|
|
|
|
<div class="tw-relative">
|
|
|
|
<div class="tw-absolute tw-align-items-center tw-c-text-alt-2 tw-flex tw-full-height tw-input__icon tw-justify-content-center tw-left-0 tw-top-0 tw-z-default">
|
|
|
|
<figure :class="[(isOpen || ! val || ! val.length) ? 'ffz-i-search' : val]" />
|
2019-06-06 16:33:14 -04:00
|
|
|
</div>
|
2019-06-09 19:48:26 -04:00
|
|
|
<input
|
|
|
|
:id="'icon-search$' + id"
|
2019-06-19 20:57:14 -04:00
|
|
|
ref="input"
|
2019-06-09 19:48:26 -04:00
|
|
|
:placeholder="('setting.icon.search', 'Search for Icon')"
|
|
|
|
:value="isOpen ? search : val"
|
|
|
|
:class="[clearable ? 'tw-pd-r-5' : 'tw-pd-r-1']"
|
|
|
|
type="text"
|
|
|
|
class="tw-block tw-border-radius-medium tw-font-size-6 tw-full-width tw-input tw-pd-l-3 tw-pd-y-05"
|
|
|
|
autocapitalize="off"
|
|
|
|
autocorrect="off"
|
|
|
|
autocomplete="off"
|
|
|
|
spellcheck="false"
|
|
|
|
@input="update"
|
|
|
|
@focus="onFocus"
|
|
|
|
@blur="onBlur"
|
|
|
|
@keydown.escape="open = false"
|
|
|
|
>
|
|
|
|
<button
|
|
|
|
v-if="clearable"
|
|
|
|
class="tw-absolute tw-right-0 tw-top-0 tw-button tw-button--text tw-tooltip-wrapper"
|
|
|
|
@click="change('', false)"
|
|
|
|
@keydown.escape="open = false"
|
|
|
|
@focus="onFocus(false)"
|
|
|
|
@blur="onBlur"
|
|
|
|
>
|
|
|
|
<span class="tw-button__text ffz-i-trash" />
|
|
|
|
<div class="tw-tooltip tw-tooltip--up tw-tooltip--align-right">
|
|
|
|
{{ t('setting.icon.clear', 'Clear') }}
|
2019-06-06 16:33:14 -04:00
|
|
|
</div>
|
2019-06-09 19:48:26 -04:00
|
|
|
</button>
|
|
|
|
</div>
|
2019-06-06 16:33:14 -04:00
|
|
|
</div>
|
2019-06-09 19:48:26 -04:00
|
|
|
<balloon v-if="open" :dir="direction" color="background-base">
|
|
|
|
<div ref="list">
|
|
|
|
<simplebar classes="scrollable-area--suppress-scroll-x ffz--icon-picker__list">
|
2019-06-19 20:57:14 -04:00
|
|
|
<div v-if="visible.length" role="radiogroup" class="tw-pd-1 tw-flex tw-flex-wrap tw-justify-content-between">
|
2019-06-09 19:48:26 -04:00
|
|
|
<div
|
|
|
|
v-for="i of visible"
|
|
|
|
:key="i[0]"
|
|
|
|
:aria-checked="val === i[0]"
|
|
|
|
:class="{'tw-interactable--selected': val === i[0]}"
|
|
|
|
:data-title="i[1]"
|
2019-08-09 14:24:26 -04:00
|
|
|
class="ffz-tooltip ffz-icon tw-interactable tw-interactable--hover-enabled tw-interactable--inverted tw-interactive"
|
2019-06-09 19:48:26 -04:00
|
|
|
role="radio"
|
|
|
|
tabindex="0"
|
|
|
|
@keydown.space.stop.prevent=""
|
|
|
|
@keyup.space="change(i[0])"
|
|
|
|
@keyup.enter="change(i[0])"
|
|
|
|
@click="change(i[0])"
|
|
|
|
@focus="onFocus(false)"
|
|
|
|
@blur="onBlur"
|
|
|
|
>
|
|
|
|
<figure :class="`tw-mg-y-05 tw-mg-x-1 ${i[0]}`" />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div v-else class="tw-align-center tw-pd-1 tw-c-text-alt-2">
|
|
|
|
{{ t('setting.actions.empty-search', 'no results') }}
|
|
|
|
</div>
|
|
|
|
</simplebar>
|
|
|
|
</div>
|
|
|
|
</balloon>
|
2019-06-06 16:33:14 -04:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
2019-06-08 17:35:48 -04:00
|
|
|
let id = 0;
|
|
|
|
|
2019-06-09 19:48:26 -04:00
|
|
|
import {escape_regex, deep_copy, debounce} from 'utilities/object';
|
2019-06-06 16:33:14 -04:00
|
|
|
import {load, ICONS as FA_ICONS, ALIASES as FA_ALIASES} from 'utilities/font-awesome';
|
2019-06-09 19:48:26 -04:00
|
|
|
import { maybeLoad } from '../utilities/font-awesome';
|
2019-06-06 16:33:14 -04:00
|
|
|
|
|
|
|
const FFZ_ICONS = [
|
2019-06-08 17:35:48 -04:00
|
|
|
'cancel',
|
2019-06-06 16:33:14 -04:00
|
|
|
'zreknarf',
|
2019-06-08 17:35:48 -04:00
|
|
|
'search',
|
2019-06-06 16:33:14 -04:00
|
|
|
'clock',
|
|
|
|
'star',
|
|
|
|
'star-empty',
|
2019-06-08 17:35:48 -04:00
|
|
|
'down-dir',
|
|
|
|
'right-dir',
|
|
|
|
'attention',
|
|
|
|
'ok',
|
|
|
|
'cog',
|
|
|
|
'plus',
|
|
|
|
'folder-open',
|
2019-06-06 16:33:14 -04:00
|
|
|
'download',
|
|
|
|
'upload',
|
2019-06-08 17:35:48 -04:00
|
|
|
'floppy',
|
|
|
|
'crown',
|
|
|
|
'verified',
|
|
|
|
'heart',
|
|
|
|
'heart-empty',
|
2019-06-06 16:33:14 -04:00
|
|
|
'tag',
|
|
|
|
'tags',
|
|
|
|
'retweet',
|
|
|
|
'thumbs-up',
|
|
|
|
'thumbs-down',
|
|
|
|
'bell',
|
|
|
|
'pencil',
|
|
|
|
'info',
|
|
|
|
'help',
|
|
|
|
'calendar',
|
2019-06-08 17:35:48 -04:00
|
|
|
'left-dir',
|
|
|
|
'inventory',
|
2019-06-06 16:33:14 -04:00
|
|
|
'lock',
|
|
|
|
'lock-open',
|
|
|
|
'arrows-cw',
|
2019-06-08 17:35:48 -04:00
|
|
|
'ignore',
|
|
|
|
'block',
|
|
|
|
'pin',
|
|
|
|
'pin-outline',
|
2019-06-06 16:33:14 -04:00
|
|
|
'gift',
|
2019-06-08 17:35:48 -04:00
|
|
|
'discord',
|
|
|
|
'eye',
|
|
|
|
'eye-off',
|
|
|
|
'views',
|
|
|
|
'conversations',
|
|
|
|
'channels',
|
|
|
|
'camera',
|
|
|
|
'cw',
|
|
|
|
'up-dir',
|
|
|
|
'up-big',
|
2019-06-09 19:48:26 -04:00
|
|
|
'play',
|
2019-06-17 15:32:38 -04:00
|
|
|
'user',
|
|
|
|
'clip',
|
2019-06-08 17:35:48 -04:00
|
|
|
'link-ext',
|
|
|
|
'twitter',
|
2019-06-06 16:33:14 -04:00
|
|
|
'github',
|
2019-06-17 15:32:38 -04:00
|
|
|
'sort-down',
|
|
|
|
'sort-up',
|
2019-06-08 17:35:48 -04:00
|
|
|
'gauge',
|
|
|
|
'download-cloud',
|
|
|
|
'upload-cloud',
|
|
|
|
'smile',
|
|
|
|
'keyboard',
|
|
|
|
'calendar-empty',
|
|
|
|
'ellipsis-vert',
|
2019-06-17 15:32:38 -04:00
|
|
|
'sort-alt-up',
|
|
|
|
'sort-alt-down',
|
|
|
|
'language',
|
2019-06-08 17:35:48 -04:00
|
|
|
'twitch',
|
|
|
|
'bell-off',
|
|
|
|
'trash',
|
|
|
|
'eyedropper',
|
|
|
|
'user-secret',
|
|
|
|
'window-maximize',
|
|
|
|
'window-minimize',
|
|
|
|
'window-restore',
|
|
|
|
'window-close'
|
2019-06-06 16:33:14 -04:00
|
|
|
];
|
|
|
|
|
|
|
|
const FFZ_ALIASES = {
|
|
|
|
'block': ['ban', 'block'],
|
|
|
|
'ok': ['ok', 'unban', 'untimeout', 'checkmark'],
|
|
|
|
'clock': ['clock', 'clock-o', 'timeout']
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const ICONS = FFZ_ICONS
|
|
|
|
.map(x => [`ffz-i-${x}`, FFZ_ALIASES[x] ? FFZ_ALIASES[x].join(' ') : x])
|
|
|
|
.concat(FA_ICONS.filter(x => ! FFZ_ICONS.includes(x)).map(x => [`ffz-fa fa-${x}`, FA_ALIASES[x] ? FA_ALIASES[x].join(' ') : x]));
|
|
|
|
|
|
|
|
export default {
|
2019-06-09 19:48:26 -04:00
|
|
|
props: {
|
|
|
|
value: String,
|
|
|
|
alwaysOpen: {
|
|
|
|
type: Boolean,
|
|
|
|
required: false,
|
|
|
|
default: false
|
|
|
|
},
|
|
|
|
clearable: {
|
|
|
|
type: Boolean,
|
|
|
|
required: false,
|
|
|
|
default: false
|
|
|
|
},
|
|
|
|
direction: {
|
|
|
|
type: String,
|
|
|
|
required: false,
|
|
|
|
default: 'down'
|
|
|
|
}
|
|
|
|
},
|
2019-06-06 16:33:14 -04:00
|
|
|
|
|
|
|
data() {
|
|
|
|
return {
|
2019-06-08 17:35:48 -04:00
|
|
|
id: id++,
|
2019-06-09 19:48:26 -04:00
|
|
|
open: false,
|
|
|
|
val: this.value,
|
2019-06-06 16:33:14 -04:00
|
|
|
search: '',
|
|
|
|
icons: deep_copy(ICONS)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
computed: {
|
|
|
|
visible() {
|
|
|
|
if ( ! this.search || ! this.search.length )
|
|
|
|
return this.icons;
|
|
|
|
|
|
|
|
const search = this.search.toLowerCase().replace(' ', '-'),
|
2019-06-19 20:57:14 -04:00
|
|
|
reg = new RegExp(`(?:^|-| )${escape_regex(search)}`, 'i');
|
2019-06-06 16:33:14 -04:00
|
|
|
|
|
|
|
return this.icons.filter(x => reg.test(x[1]));
|
2019-06-09 19:48:26 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
isOpen() {
|
|
|
|
return this.alwaysOpen || this.open
|
2019-06-06 16:33:14 -04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2019-06-09 19:48:26 -04:00
|
|
|
watch: {
|
|
|
|
value() {
|
|
|
|
this.val = this.value;
|
|
|
|
},
|
2019-06-08 17:35:48 -04:00
|
|
|
|
2019-06-09 19:48:26 -04:00
|
|
|
isOpen() {
|
|
|
|
if ( ! this.isOpen ) {
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
const ffz = FrankerFaceZ.get();
|
|
|
|
if ( ffz )
|
|
|
|
ffz.emit('tooltips:cleanup');
|
|
|
|
});
|
|
|
|
return;
|
2019-06-08 17:35:48 -04:00
|
|
|
}
|
2019-06-09 19:48:26 -04:00
|
|
|
|
|
|
|
load();
|
|
|
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
if ( this.val ) {
|
|
|
|
const root = this.$refs.list,
|
|
|
|
el = root && root.querySelector('.tw-interactable--selected');
|
|
|
|
|
|
|
|
if ( el )
|
|
|
|
el.scrollIntoViewIfNeeded();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
created() {
|
|
|
|
this.maybeClose = debounce(this.maybeClose, 10);
|
|
|
|
},
|
|
|
|
|
|
|
|
mounted() {
|
|
|
|
maybeLoad(this.val);
|
2019-06-06 16:33:14 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
methods: {
|
2019-06-09 19:48:26 -04:00
|
|
|
update() {
|
|
|
|
if ( this.open )
|
|
|
|
this.search = this.$refs.input.value;
|
|
|
|
},
|
|
|
|
|
|
|
|
close() {
|
|
|
|
this.open = false;
|
|
|
|
},
|
|
|
|
|
|
|
|
change(val, close = true) {
|
|
|
|
this.val = val;
|
|
|
|
this.$emit('input', this.val);
|
|
|
|
if ( close )
|
|
|
|
this.open = false;
|
|
|
|
},
|
|
|
|
|
|
|
|
onFocus(open = true) {
|
|
|
|
this.focused = true;
|
|
|
|
if ( open )
|
|
|
|
this.open = true;
|
|
|
|
},
|
|
|
|
|
|
|
|
onBlur() {
|
|
|
|
this.focused = false;
|
|
|
|
this.maybeClose();
|
|
|
|
},
|
|
|
|
|
|
|
|
maybeClose() {
|
|
|
|
if ( ! this.focused )
|
|
|
|
this.open = false;
|
2019-06-06 16:33:14 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
</script>
|