support transaction tag group
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
import { useI18n } from '@/locales/helpers.ts';
|
||||
|
||||
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||
|
||||
import { values } from '@/core/base.ts';
|
||||
import { DEFAULT_TAG_GROUP_ID } from '@/consts/tag.ts';
|
||||
|
||||
import { TransactionTag } from '@/models/transaction_tag.ts';
|
||||
|
||||
export type TransactionTagWithGroupHeader = TransactionTag | {
|
||||
type: 'subheader';
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface CommonTransactionTagSelectionProps {
|
||||
modelValue: string[];
|
||||
allowAddNewTag?: boolean;
|
||||
}
|
||||
|
||||
export function useTransactionTagSelectionBase(props: CommonTransactionTagSelectionProps, useClonedModelValue?: boolean) {
|
||||
const { tt } = useI18n();
|
||||
|
||||
const transactionTagsStore = useTransactionTagsStore();
|
||||
|
||||
const clonedModelValue = ref<string[]>(useClonedModelValue ? Array.from(props.modelValue) : []);
|
||||
const tagSearchContent = ref<string>('');
|
||||
|
||||
const selectedTagIds = computed<Record<string, boolean>>(() => {
|
||||
const ret: Record<string, boolean> = {};
|
||||
|
||||
if (useClonedModelValue) {
|
||||
for (const tagId of clonedModelValue.value) {
|
||||
ret[tagId] = true;
|
||||
}
|
||||
} else {
|
||||
for (const tagId of props.modelValue) {
|
||||
ret[tagId] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
});
|
||||
|
||||
const lowerCaseTagSearchContent = computed<string>(() => tagSearchContent.value.toLowerCase());
|
||||
|
||||
const allTagsWithGroupHeader = computed<TransactionTagWithGroupHeader[]>(() => getTagsWithGroupHeader(tag => {
|
||||
if (!tag.hidden) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (selectedTagIds.value[tag.id]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lowerCaseTagSearchContent.value && tag.name.toLowerCase().indexOf(lowerCaseTagSearchContent.value) >= 0 && isAllFilteredTagHidden.value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}));
|
||||
|
||||
const filteredTagsWithGroupHeader = computed<TransactionTagWithGroupHeader[]>(() => getTagsWithGroupHeader(tag => {
|
||||
if (lowerCaseTagSearchContent.value) {
|
||||
if (tag.name.toLowerCase().indexOf(lowerCaseTagSearchContent.value) >= 0 && (!tag.hidden || isAllFilteredTagHidden.value)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !tag.hidden || !!selectedTagIds.value[tag.id];
|
||||
}));
|
||||
|
||||
const isAllFilteredTagHidden = computed<boolean>(() => {
|
||||
const lowerCaseTagSearchContent = tagSearchContent.value.toLowerCase();
|
||||
let hiddenCount = 0;
|
||||
|
||||
for (const tag of values(transactionTagsStore.allTransactionTagsMap)) {
|
||||
if (!lowerCaseTagSearchContent || tag.name.toLowerCase().indexOf(lowerCaseTagSearchContent) >= 0) {
|
||||
if (!tag.hidden) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hiddenCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return hiddenCount > 0;
|
||||
});
|
||||
|
||||
function getTagsWithGroupHeader(tagFilterFn: (tag: TransactionTag) => boolean): TransactionTagWithGroupHeader[] {
|
||||
const result: TransactionTagWithGroupHeader[] = [];
|
||||
const tagsInDefaultGroup = transactionTagsStore.allTransactionTagsByGroupMap[DEFAULT_TAG_GROUP_ID];
|
||||
|
||||
if (tagsInDefaultGroup && tagsInDefaultGroup.length > 0) {
|
||||
const visibleTags = tagsInDefaultGroup.filter(tag => tagFilterFn(tag));
|
||||
|
||||
if (visibleTags.length > 0) {
|
||||
result.push({
|
||||
type: 'subheader',
|
||||
title: tt('Default Group')
|
||||
});
|
||||
|
||||
result.push(...visibleTags);
|
||||
}
|
||||
}
|
||||
|
||||
for (const tagGroup of transactionTagsStore.allTransactionTagGroups) {
|
||||
const tags = transactionTagsStore.allTransactionTagsByGroupMap[tagGroup.id];
|
||||
|
||||
if (!tags || tags.length < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const visibleTags = tags.filter(tag => tagFilterFn(tag));
|
||||
|
||||
if (visibleTags.length > 0) {
|
||||
result.push({
|
||||
type: 'subheader',
|
||||
title: tagGroup.name
|
||||
});
|
||||
|
||||
result.push(...visibleTags);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return {
|
||||
// states
|
||||
clonedModelValue,
|
||||
tagSearchContent,
|
||||
// computed states
|
||||
selectedTagIds,
|
||||
allTagsWithGroupHeader,
|
||||
filteredTagsWithGroupHeader
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<v-dialog max-width="500" :persistent="oldName !== newName" v-model="showState">
|
||||
<v-card class="pa-sm-1 pa-md-2">
|
||||
<template #title>
|
||||
<h4 class="text-h4 text-wrap">{{ dialogTitle || defaultTitle }}</h4>
|
||||
</template>
|
||||
<v-card-text class="w-100 d-flex justify-center">
|
||||
<v-text-field persistent-placeholder
|
||||
:autofocus="true"
|
||||
:label="label"
|
||||
:placeholder="placeholder"
|
||||
v-model="newName"
|
||||
@keyup.enter="save" />
|
||||
</v-card-text>
|
||||
<v-card-text>
|
||||
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
|
||||
<v-btn color="primary" :disabled="!newName || oldName === newName" @click="save">
|
||||
{{ tt('Save') }}
|
||||
</v-btn>
|
||||
<v-btn color="secondary" variant="tonal" @click="cancel">
|
||||
{{ tt('Cancel') }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useI18n } from '@/locales/helpers.ts';
|
||||
|
||||
defineProps<{
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
defaultTitle?: string;
|
||||
}>();
|
||||
|
||||
const { tt } = useI18n();
|
||||
|
||||
let resolveFunc: ((name: string) => void) | null = null;
|
||||
let rejectFunc: ((reason?: unknown) => void) | null = null;
|
||||
|
||||
const showState = ref<boolean>(false);
|
||||
const dialogTitle = ref<string | undefined>(undefined);
|
||||
const oldName = ref<string>('');
|
||||
const newName = ref<string>('');
|
||||
|
||||
function open(currentName: string, title?: string): Promise<string> {
|
||||
showState.value = true;
|
||||
dialogTitle.value = title;
|
||||
oldName.value = currentName;
|
||||
newName.value = currentName;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
resolveFunc = resolve;
|
||||
rejectFunc = reject;
|
||||
});
|
||||
}
|
||||
|
||||
function save(): void {
|
||||
if (!newName.value || oldName.value === newName.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
resolveFunc?.(newName.value);
|
||||
showState.value = false;
|
||||
}
|
||||
|
||||
function cancel(): void {
|
||||
rejectFunc?.();
|
||||
showState.value = false;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<v-autocomplete
|
||||
item-title="name"
|
||||
item-value="id"
|
||||
auto-select-first
|
||||
persistent-placeholder
|
||||
multiple
|
||||
chips
|
||||
:density="density"
|
||||
:variant="variant"
|
||||
:closable-chips="!readonly"
|
||||
:readonly="readonly"
|
||||
:disabled="disabled"
|
||||
:label="showLabel ? tt('Tags') : undefined"
|
||||
:placeholder="tt('None')"
|
||||
:items="allTagsWithGroupHeader"
|
||||
:model-value="modelValue"
|
||||
v-model:search="tagSearchContent"
|
||||
@update:modelValue="updateModelValue"
|
||||
>
|
||||
<template #chip="{ props, item }">
|
||||
<v-chip :prepend-icon="mdiPound" :text="item.title" v-bind="props"/>
|
||||
</template>
|
||||
|
||||
<template #subheader="{ props }">
|
||||
<v-list-subheader>{{ props['title'] }}</v-list-subheader>
|
||||
</template>
|
||||
|
||||
<template #item="{ props, item }">
|
||||
<v-list-item :value="item.value" v-bind="props" v-if="item.raw instanceof TransactionTag && !item.raw.hidden">
|
||||
<template #title>
|
||||
<v-list-item-title>
|
||||
<div class="d-flex align-center">
|
||||
<v-icon size="20" start :icon="mdiPound"/>
|
||||
<span>{{ item.title }}</span>
|
||||
</div>
|
||||
</v-list-item-title>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item :disabled="true" v-bind="props" v-else-if="item.raw instanceof TransactionTag && item.raw.hidden">
|
||||
<template #title>
|
||||
<v-list-item-title>
|
||||
<div class="d-flex align-center">
|
||||
<v-icon size="20" start :icon="mdiPound"/>
|
||||
<span>{{ item.title }}</span>
|
||||
</div>
|
||||
</v-list-item-title>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</template>
|
||||
|
||||
<template #no-data>
|
||||
<v-list class="py-0">
|
||||
<v-list-item v-if="tagSearchContent && allowAddNewTag" @click="saveNewTag(tagSearchContent)">{{ tt('format.misc.addNewTag', { tag: tagSearchContent }) }}</v-list-item>
|
||||
<v-list-item v-else-if="!tagSearchContent || !allowAddNewTag">{{ tt('No available tag') }}</v-list-item>
|
||||
</v-list>
|
||||
</template>
|
||||
</v-autocomplete>
|
||||
|
||||
<snack-bar ref="snackbar" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SnackBar from '@/components/desktop/SnackBar.vue';
|
||||
|
||||
import { useTemplateRef } from 'vue';
|
||||
|
||||
import { useI18n } from '@/locales/helpers.ts';
|
||||
import { type CommonTransactionTagSelectionProps, useTransactionTagSelectionBase } from '@/components/base/TransactionTagSelectionBase.ts';
|
||||
|
||||
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||
|
||||
import { TransactionTag } from '@/models/transaction_tag.ts';
|
||||
|
||||
import type { ComponentDensity, InputVariant } from '@/lib/ui/desktop.ts';
|
||||
|
||||
import {
|
||||
mdiPound
|
||||
} from '@mdi/js';
|
||||
|
||||
type SnackBarType = InstanceType<typeof SnackBar>;
|
||||
|
||||
interface DesktopTransactionTagSelectionProps extends CommonTransactionTagSelectionProps {
|
||||
density?: ComponentDensity;
|
||||
variant?: InputVariant;
|
||||
readonly?: boolean;
|
||||
disabled?: boolean;
|
||||
showLabel?: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps<DesktopTransactionTagSelectionProps>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string[]): void;
|
||||
(e: 'tag:saving', state: boolean, tagName: string): void;
|
||||
}>();
|
||||
|
||||
const { tt } = useI18n();
|
||||
|
||||
const {
|
||||
tagSearchContent,
|
||||
allTagsWithGroupHeader
|
||||
} = useTransactionTagSelectionBase(props);
|
||||
|
||||
const transactionTagsStore = useTransactionTagsStore();
|
||||
|
||||
const snackbar = useTemplateRef<SnackBarType>('snackbar');
|
||||
|
||||
function saveNewTag(tagName: string): void {
|
||||
emit('tag:saving', true, tagName);
|
||||
|
||||
transactionTagsStore.saveTag({
|
||||
tag: TransactionTag.createNewTag(tagName)
|
||||
}).then(tag => {
|
||||
emit('tag:saving', false, tagName);
|
||||
|
||||
if (tag && tag.id) {
|
||||
const newValue: string[] = [...props.modelValue];
|
||||
newValue.push(tag.id);
|
||||
updateModelValue(newValue);
|
||||
}
|
||||
}).catch(error => {
|
||||
emit('tag:saving', false, tagName);
|
||||
|
||||
if (!error.processed) {
|
||||
snackbar.value?.showError(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateModelValue(newValue: string[]) {
|
||||
emit('update:modelValue', newValue);
|
||||
}
|
||||
</script>
|
||||
@@ -8,43 +8,55 @@
|
||||
<f7-link sheet-close icon-f7="xmark"></f7-link>
|
||||
</div>
|
||||
<f7-searchbar ref="searchbar" custom-searchs
|
||||
:value="filterContent"
|
||||
:value="tagSearchContent"
|
||||
:placeholder="tt('Find tag')"
|
||||
:disable-button="false"
|
||||
v-if="enableFilter"
|
||||
@input="filterContent = $event.target.value"
|
||||
@input="tagSearchContent = $event.target.value"
|
||||
@focus="onSearchBarFocus">
|
||||
</f7-searchbar>
|
||||
<div class="right">
|
||||
<f7-button round fill icon-f7="checkmark_alt" @click="save"
|
||||
v-if="allTags && allTags.length && !noAvailableTag"></f7-button>
|
||||
v-if="filteredTagsWithGroupHeader && filteredTagsWithGroupHeader.length > 0"></f7-button>
|
||||
<f7-link icon-f7="plus" :class="{'disabled': newTag}" @click="addNewTag"
|
||||
v-if="!allTags || !allTags.length || noAvailableTag"></f7-link>
|
||||
v-if="!filteredTagsWithGroupHeader || filteredTagsWithGroupHeader.length < 1"></f7-link>
|
||||
</div>
|
||||
</f7-toolbar>
|
||||
<f7-page-content :class="'margin-top ' + heightClass">
|
||||
<f7-list class="no-margin-top no-margin-bottom" v-if="(!allTags || !allTags.length || noAvailableTag) && !newTag">
|
||||
<f7-list class="no-margin-top no-margin-bottom" v-if="(!filteredTagsWithGroupHeader || filteredTagsWithGroupHeader.length < 1) && !newTag">
|
||||
<f7-list-item :title="tt('No available tag')"></f7-list-item>
|
||||
</f7-list>
|
||||
<f7-list dividers class="no-margin-top no-margin-bottom tag-selection-list" v-else-if="(allTags && allTags.length && !noAvailableTag) || newTag">
|
||||
<f7-list-item checkbox
|
||||
:class="isChecked(tag.id) ? 'list-item-selected' : ''"
|
||||
:value="tag.id"
|
||||
:checked="isChecked(tag.id)"
|
||||
:key="tag.id"
|
||||
v-for="tag in allTags"
|
||||
@change="changeTagSelection">
|
||||
<template #title>
|
||||
<f7-block class="no-padding no-margin">
|
||||
<f7-list dividers class="no-margin-top no-margin-bottom tag-selection-list" v-else-if="(filteredTagsWithGroupHeader && filteredTagsWithGroupHeader.length > 0) || newTag">
|
||||
<template :key="(tag instanceof TransactionTag) ? tag.id : `${tag.type}-${index}-${tag.title}`"
|
||||
v-for="(tag, index) in filteredTagsWithGroupHeader">
|
||||
<f7-list-item group-title v-if="!(tag instanceof TransactionTag)">
|
||||
<div class="tag-selection-list-item">
|
||||
{{ tag.title }}
|
||||
</div>
|
||||
</f7-list-item>
|
||||
<f7-list-item checkbox
|
||||
:class="{ 'list-item-selected': selectedTagIds[tag.id], 'disabled': tag.hidden && !selectedTagIds[tag.id] }"
|
||||
:value="tag.id"
|
||||
:checked="selectedTagIds[tag.id]"
|
||||
:key="tag.id"
|
||||
v-else-if="tag instanceof TransactionTag"
|
||||
@change="changeTagSelection">
|
||||
<template #media>
|
||||
<f7-icon class="transaction-tag-icon" f7="number">
|
||||
<f7-badge color="gray" class="right-bottom-icon" v-if="tag.hidden">
|
||||
<f7-icon f7="eye_slash_fill"></f7-icon>
|
||||
</f7-badge>
|
||||
</f7-icon>
|
||||
</template>
|
||||
<template #title>
|
||||
<div class="display-flex">
|
||||
<f7-icon class="transaction-tag-icon" f7="number"></f7-icon>
|
||||
<div class="tag-selection-list-item list-item-valign-middle padding-inline-start-half">
|
||||
{{ tag.name }}
|
||||
</div>
|
||||
</div>
|
||||
</f7-block>
|
||||
</template>
|
||||
</f7-list-item>
|
||||
</template>
|
||||
</f7-list-item>
|
||||
</template>
|
||||
<f7-list-item link="#" no-chevron
|
||||
:title="tt('Add new tag')"
|
||||
v-if="allowAddNewTag && !newTag"
|
||||
@@ -85,11 +97,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, useTemplateRef } from 'vue';
|
||||
import { ref, useTemplateRef } from 'vue';
|
||||
import type { Sheet, Searchbar } from 'framework7/types';
|
||||
|
||||
import { useI18n } from '@/locales/helpers.ts';
|
||||
import { useI18nUIComponents, showLoading, hideLoading } from '@/lib/ui/mobile.ts';
|
||||
import { type CommonTransactionTagSelectionProps, useTransactionTagSelectionBase } from '@/components/base/TransactionTagSelectionBase.ts';
|
||||
|
||||
import { TransactionTag } from '@/models/transaction_tag.ts';
|
||||
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||
@@ -97,12 +110,12 @@ import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||
import { scrollToSelectedItem } from '@/lib/ui/common.ts';
|
||||
import { type Framework7Dom, scrollSheetToTop } from '@/lib/ui/mobile.ts';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string[];
|
||||
allowAddNewTag?: boolean;
|
||||
interface MobileransactionTagSelectionProps extends CommonTransactionTagSelectionProps {
|
||||
enableFilter?: boolean;
|
||||
show: boolean;
|
||||
}>();
|
||||
}
|
||||
|
||||
const props = defineProps<MobileransactionTagSelectionProps>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string[]): void;
|
||||
@@ -112,81 +125,49 @@ const emit = defineEmits<{
|
||||
const { tt } = useI18n();
|
||||
const { showToast } = useI18nUIComponents();
|
||||
|
||||
const {
|
||||
clonedModelValue,
|
||||
tagSearchContent,
|
||||
selectedTagIds,
|
||||
filteredTagsWithGroupHeader
|
||||
} = useTransactionTagSelectionBase(props, true);
|
||||
|
||||
const transactionTagsStore = useTransactionTagsStore();
|
||||
|
||||
const sheet = useTemplateRef<Sheet.Sheet>('sheet');
|
||||
const searchbar = useTemplateRef<Searchbar.Searchbar>('searchbar');
|
||||
|
||||
const filterContent = ref<string>('');
|
||||
const selectedItemIds = ref<string[]>(Array.from(props.modelValue));
|
||||
const newTag = ref<TransactionTag | null>(null);
|
||||
const heightClass = ref<string>(getHeightClass());
|
||||
|
||||
const allTags = computed<TransactionTag[]>(() => {
|
||||
const finalTags: TransactionTag[] = [];
|
||||
|
||||
for (const tag of transactionTagsStore.allTransactionTags) {
|
||||
if (tag.hidden && !isChecked(tag.id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!props.enableFilter || !filterContent.value) {
|
||||
finalTags.push(tag);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag.name.toLowerCase().indexOf(filterContent.value.toLowerCase()) >= 0) {
|
||||
finalTags.push(tag);
|
||||
}
|
||||
}
|
||||
|
||||
return finalTags;
|
||||
});
|
||||
|
||||
const noAvailableTag = computed<boolean>(() => {
|
||||
if (transactionTagsStore.allTransactionTags) {
|
||||
for (const transactionTag of transactionTagsStore.allTransactionTags) {
|
||||
if (!transactionTag.hidden) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
function getHeightClass(): string {
|
||||
if (transactionTagsStore.allTransactionTags && transactionTagsStore.allVisibleTagsCount > 6) {
|
||||
if (filteredTagsWithGroupHeader.value.length > 6) {
|
||||
return 'tag-selection-huge-sheet';
|
||||
} else if (transactionTagsStore.allTransactionTags && transactionTagsStore.allVisibleTagsCount > 3) {
|
||||
} else if (filteredTagsWithGroupHeader.value.length > 3) {
|
||||
return 'tag-selection-large-sheet';
|
||||
} else {
|
||||
return 'tag-selection-default-sheet';
|
||||
}
|
||||
}
|
||||
|
||||
function isChecked(itemId: string): boolean {
|
||||
return selectedItemIds.value.indexOf(itemId) >= 0;
|
||||
}
|
||||
|
||||
function changeTagSelection(e: Event): void {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const tagId = target.value;
|
||||
const index = selectedItemIds.value.indexOf(tagId);
|
||||
const index = clonedModelValue.value.indexOf(tagId);
|
||||
|
||||
if (target.checked) {
|
||||
if (index < 0) {
|
||||
selectedItemIds.value.push(tagId);
|
||||
clonedModelValue.value.push(tagId);
|
||||
}
|
||||
} else {
|
||||
if (index >= 0) {
|
||||
selectedItemIds.value.splice(index, 1);
|
||||
clonedModelValue.value.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function save(): void {
|
||||
emit('update:modelValue', selectedItemIds.value);
|
||||
emit('update:modelValue', clonedModelValue.value);
|
||||
emit('update:show', false);
|
||||
}
|
||||
|
||||
@@ -208,7 +189,7 @@ function saveNewTag(): void {
|
||||
newTag.value = null;
|
||||
|
||||
if (tag && tag.id) {
|
||||
selectedItemIds.value.push(tag.id);
|
||||
clonedModelValue.value.push(tag.id);
|
||||
}
|
||||
}).catch(error => {
|
||||
hideLoading();
|
||||
@@ -228,14 +209,14 @@ function onSearchBarFocus(): void {
|
||||
}
|
||||
|
||||
function onSheetOpen(event: { $el: Framework7Dom }): void {
|
||||
selectedItemIds.value = Array.from(props.modelValue);
|
||||
clonedModelValue.value = Array.from(props.modelValue);
|
||||
newTag.value = null;
|
||||
scrollToSelectedItem(event.$el[0], '.sheet-modal-inner', '.page-content', 'li.list-item-selected');
|
||||
}
|
||||
|
||||
function onSheetClosed(): void {
|
||||
emit('update:show', false);
|
||||
filterContent.value = '';
|
||||
tagSearchContent.value = '';
|
||||
searchbar.value?.clear();
|
||||
}
|
||||
</script>
|
||||
@@ -262,6 +243,12 @@ function onSheetClosed(): void {
|
||||
}
|
||||
}
|
||||
|
||||
.tag-selection-list.list.list-dividers li.list-group-title:first-child,
|
||||
.tag-selection-list.list.list-dividers li.list-group-title.actual-first-child {
|
||||
margin-top: 10px;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.tag-selection-list.list .item-media + .item-inner {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user