mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-19 17:24:26 +08:00
tree view selection sheet supports filtering content (#38)
This commit is contained in:
@@ -1,134 +1,39 @@
|
|||||||
import { type Ref, ref, computed } from 'vue';
|
import { type Ref } from 'vue';
|
||||||
|
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import {
|
||||||
|
type TwoLevelItemSelectionBaseProps,
|
||||||
|
useTwoLevelItemSelectionBase
|
||||||
|
} from '@/components/base/TwoLevelItemSelectionBase.ts';
|
||||||
|
|
||||||
import { getItemByKeyValue, getPrimaryValueBySecondaryValue } from '@/lib/common.ts';
|
import { getItemByKeyValue, getPrimaryValueBySecondaryValue } from '@/lib/common.ts';
|
||||||
|
|
||||||
export interface CommonTwoColumnListItemSelectionProps {
|
export interface CommonTwoColumnListItemSelectionProps extends TwoLevelItemSelectionBaseProps {
|
||||||
modelValue: unknown;
|
|
||||||
primaryKeyField?: string;
|
|
||||||
primaryValueField?: string;
|
primaryValueField?: string;
|
||||||
primaryTitleField?: string;
|
|
||||||
primaryTitleI18n?: boolean;
|
|
||||||
primaryHeaderField?: string;
|
primaryHeaderField?: string;
|
||||||
primaryHeaderI18n?: boolean;
|
primaryHeaderI18n?: boolean;
|
||||||
primaryFooterField?: string;
|
primaryFooterField?: string;
|
||||||
primaryFooterI18n?: boolean;
|
primaryFooterI18n?: boolean;
|
||||||
primaryIconField?: string;
|
|
||||||
primaryIconType?: string;
|
|
||||||
primaryColorField?: string;
|
|
||||||
primaryHiddenField?: string;
|
|
||||||
primarySubItemsField: string;
|
|
||||||
secondaryKeyField?: string;
|
|
||||||
secondaryValueField?: string;
|
|
||||||
secondaryTitleField?: string;
|
|
||||||
secondaryTitleI18n?: boolean;
|
|
||||||
secondaryHeaderField?: string;
|
secondaryHeaderField?: string;
|
||||||
secondaryHeaderI18n?: boolean;
|
secondaryHeaderI18n?: boolean;
|
||||||
secondaryFooterField?: string;
|
secondaryFooterField?: string;
|
||||||
secondaryFooterI18n?: boolean;
|
secondaryFooterI18n?: boolean;
|
||||||
secondaryIconField?: string;
|
|
||||||
secondaryIconType?: string;
|
|
||||||
secondaryColorField?: string;
|
|
||||||
secondaryHiddenField?: string;
|
|
||||||
enableFilter?: boolean;
|
|
||||||
filterPlaceholder?: string;
|
|
||||||
filterNoItemsText?: string;
|
|
||||||
items: Record<string, unknown>[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useTwoColumnListItemSelectionBase(props: CommonTwoColumnListItemSelectionProps) {
|
export function useTwoColumnListItemSelectionBase(props: CommonTwoColumnListItemSelectionProps) {
|
||||||
const { ti } = useI18n();
|
const {
|
||||||
|
filterContent,
|
||||||
const filterContent = ref<string>('');
|
visibleItemsCount,
|
||||||
|
filteredItems,
|
||||||
const filteredItems = computed<Record<string, unknown>[]>(() => {
|
getFilteredSubItems,
|
||||||
const finalItems: Record<string, unknown>[] = [];
|
isSecondaryValueSelected,
|
||||||
const items = props.items;
|
getSelectedSecondaryItem,
|
||||||
|
updateCurrentSecondaryValue
|
||||||
for (const item of items) {
|
} = useTwoLevelItemSelectionBase(props);
|
||||||
if (props.primaryHiddenField && item[props.primaryHiddenField]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!props.enableFilter || !filterContent.value) {
|
|
||||||
finalItems.push(item);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.primaryTitleField) {
|
|
||||||
const title = ti(item[props.primaryTitleField] as string, !!props.primaryTitleI18n);
|
|
||||||
|
|
||||||
if (title.toLowerCase().indexOf(filterContent.value.toLowerCase()) >= 0) {
|
|
||||||
finalItems.push(item);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.primarySubItemsField) {
|
|
||||||
if (getFilteredSubItems(item).length > 0) {
|
|
||||||
finalItems.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return finalItems;
|
|
||||||
});
|
|
||||||
|
|
||||||
function getFilteredSubItems(selectedPrimaryItem: unknown): Record<string, unknown>[] {
|
|
||||||
const finalItems: Record<string, unknown>[] = [];
|
|
||||||
|
|
||||||
if (!selectedPrimaryItem || !props.primarySubItemsField) {
|
|
||||||
return finalItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
const subItems = (selectedPrimaryItem as Record<string, unknown>)[props.primarySubItemsField] as Record<string, unknown>[];
|
|
||||||
let primaryTitleHasFilterContent = false;
|
|
||||||
|
|
||||||
if (props.primaryTitleField) {
|
|
||||||
const title = ti((selectedPrimaryItem as Record<string, unknown>)[props.primaryTitleField] as string, !!props.primaryTitleI18n);
|
|
||||||
primaryTitleHasFilterContent = title.toLowerCase().indexOf(filterContent.value.toLowerCase()) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const subItem of subItems) {
|
|
||||||
if (props.secondaryHiddenField && subItem[props.secondaryHiddenField]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!props.enableFilter || !filterContent.value) {
|
|
||||||
finalItems.push(subItem);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (primaryTitleHasFilterContent) {
|
|
||||||
finalItems.push(subItem);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.secondaryTitleField && filterContent.value) {
|
|
||||||
const title = ti(subItem[props.secondaryTitleField] as string, !!props.secondaryTitleI18n);
|
|
||||||
|
|
||||||
if (title.toLowerCase().indexOf(filterContent.value.toLowerCase()) >= 0) {
|
|
||||||
finalItems.push(subItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return finalItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCurrentPrimaryValueBySecondaryValue(secondaryValue: unknown): unknown {
|
function getCurrentPrimaryValueBySecondaryValue(secondaryValue: unknown): unknown {
|
||||||
return getPrimaryValueBySecondaryValue(props.items as Record<string, Record<string, unknown>[]>[], props.primarySubItemsField, props.primaryValueField, props.primaryHiddenField, props.secondaryValueField, props.secondaryHiddenField, secondaryValue);
|
return getPrimaryValueBySecondaryValue(props.items as Record<string, Record<string, unknown>[]>[], props.primarySubItemsField, props.primaryValueField, props.primaryHiddenField, props.secondaryValueField, props.secondaryHiddenField, secondaryValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSecondaryValueSelected(currentSecondaryValue: unknown, subItem: unknown): boolean {
|
|
||||||
if (props.secondaryValueField) {
|
|
||||||
return currentSecondaryValue === (subItem as Record<string, unknown>)[props.secondaryValueField];
|
|
||||||
} else {
|
|
||||||
return currentSecondaryValue === subItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSelectedPrimaryItem(currentPrimaryValue: unknown): unknown {
|
function getSelectedPrimaryItem(currentPrimaryValue: unknown): unknown {
|
||||||
if (props.primaryValueField) {
|
if (props.primaryValueField) {
|
||||||
return getItemByKeyValue(props.items, currentPrimaryValue, props.primaryValueField);
|
return getItemByKeyValue(props.items, currentPrimaryValue, props.primaryValueField);
|
||||||
@@ -137,14 +42,6 @@ export function useTwoColumnListItemSelectionBase(props: CommonTwoColumnListItem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSelectedSecondaryItem(currentSecondaryValue: unknown, selectedPrimaryItem: unknown): unknown {
|
|
||||||
if (currentSecondaryValue && selectedPrimaryItem && (selectedPrimaryItem as Record<string, unknown>)[props.primarySubItemsField]) {
|
|
||||||
return getItemByKeyValue((selectedPrimaryItem as Record<string, unknown>)[props.primarySubItemsField] as Record<string, unknown>[], currentSecondaryValue, props.secondaryValueField as string);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateCurrentPrimaryValue(currentPrimaryValue: Ref<unknown>, item: unknown): void {
|
function updateCurrentPrimaryValue(currentPrimaryValue: Ref<unknown>, item: unknown): void {
|
||||||
if (props.primaryValueField) {
|
if (props.primaryValueField) {
|
||||||
currentPrimaryValue.value = (item as Record<string, unknown>)[props.primaryValueField];
|
currentPrimaryValue.value = (item as Record<string, unknown>)[props.primaryValueField];
|
||||||
@@ -153,18 +50,11 @@ export function useTwoColumnListItemSelectionBase(props: CommonTwoColumnListItem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCurrentSecondaryValue(currentSecondaryValue: Ref<unknown>, subItem: unknown): void {
|
|
||||||
if (props.secondaryValueField) {
|
|
||||||
currentSecondaryValue.value = (subItem as Record<string, unknown>)[props.secondaryValueField];
|
|
||||||
} else {
|
|
||||||
currentSecondaryValue.value = subItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// states
|
// states
|
||||||
filterContent,
|
filterContent,
|
||||||
// computed states
|
// computed states
|
||||||
|
visibleItemsCount,
|
||||||
filteredItems,
|
filteredItems,
|
||||||
// functions
|
// functions
|
||||||
getFilteredSubItems,
|
getFilteredSubItems,
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
import { type Ref, ref, computed } from 'vue';
|
||||||
|
|
||||||
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
|
import { getItemByKeyValue } from '@/lib/common.ts';
|
||||||
|
|
||||||
|
export interface TwoLevelItemSelectionBaseProps {
|
||||||
|
modelValue: unknown;
|
||||||
|
primaryKeyField?: string;
|
||||||
|
primaryTitleField?: string;
|
||||||
|
primaryTitleI18n?: boolean;
|
||||||
|
primaryIconField?: string;
|
||||||
|
primaryIconType?: string;
|
||||||
|
primaryColorField?: string;
|
||||||
|
primaryHiddenField?: string;
|
||||||
|
primarySubItemsField: string;
|
||||||
|
secondaryKeyField?: string;
|
||||||
|
secondaryValueField?: string;
|
||||||
|
secondaryTitleField?: string;
|
||||||
|
secondaryTitleI18n?: boolean;
|
||||||
|
secondaryIconField?: string;
|
||||||
|
secondaryIconType?: string;
|
||||||
|
secondaryColorField?: string;
|
||||||
|
secondaryHiddenField?: string;
|
||||||
|
enableFilter?: boolean;
|
||||||
|
filterPlaceholder?: string;
|
||||||
|
filterNoItemsText?: string;
|
||||||
|
items: Record<string, unknown>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useTwoLevelItemSelectionBase(props: TwoLevelItemSelectionBaseProps) {
|
||||||
|
const { ti } = useI18n();
|
||||||
|
|
||||||
|
const filterContent = ref<string>('');
|
||||||
|
|
||||||
|
const visibleItemsCount = computed<number>(() => {
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
for (const item of props.items) {
|
||||||
|
if (props.primaryHiddenField && item[props.primaryHiddenField]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
});
|
||||||
|
|
||||||
|
const filteredItems = computed<Record<string, unknown>[]>(() => {
|
||||||
|
const finalItems: Record<string, unknown>[] = [];
|
||||||
|
const items = props.items;
|
||||||
|
const lowerCaseFilterContent = filterContent.value?.toLowerCase() ?? '';
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
if (props.primaryHiddenField && item[props.primaryHiddenField]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!props.enableFilter || !lowerCaseFilterContent) {
|
||||||
|
finalItems.push(item);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.primaryTitleField) {
|
||||||
|
const title = ti(item[props.primaryTitleField] as string, !!props.primaryTitleI18n);
|
||||||
|
|
||||||
|
if (title.toLowerCase().indexOf(lowerCaseFilterContent) >= 0) {
|
||||||
|
finalItems.push(item);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.primarySubItemsField) {
|
||||||
|
if (getFilteredSubItems(item).length > 0) {
|
||||||
|
finalItems.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalItems;
|
||||||
|
});
|
||||||
|
|
||||||
|
function getFilteredSubItems(selectedPrimaryItem: unknown): Record<string, unknown>[] {
|
||||||
|
const finalItems: Record<string, unknown>[] = [];
|
||||||
|
|
||||||
|
if (!selectedPrimaryItem || !props.primarySubItemsField) {
|
||||||
|
return finalItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
const subItems = (selectedPrimaryItem as Record<string, unknown>)[props.primarySubItemsField] as Record<string, unknown>[];
|
||||||
|
let primaryTitleHasFilterContent = false;
|
||||||
|
|
||||||
|
if (props.primaryTitleField) {
|
||||||
|
const title = ti((selectedPrimaryItem as Record<string, unknown>)[props.primaryTitleField] as string, !!props.primaryTitleI18n);
|
||||||
|
primaryTitleHasFilterContent = title.toLowerCase().indexOf(filterContent.value.toLowerCase()) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const subItem of subItems) {
|
||||||
|
if (props.secondaryHiddenField && subItem[props.secondaryHiddenField]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!props.enableFilter || !filterContent.value) {
|
||||||
|
finalItems.push(subItem);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (primaryTitleHasFilterContent) {
|
||||||
|
finalItems.push(subItem);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.secondaryTitleField && filterContent.value) {
|
||||||
|
const title = ti(subItem[props.secondaryTitleField] as string, !!props.secondaryTitleI18n);
|
||||||
|
|
||||||
|
if (title.toLowerCase().indexOf(filterContent.value.toLowerCase()) >= 0) {
|
||||||
|
finalItems.push(subItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSecondaryValueSelected(currentSecondaryValue: unknown, subItem: unknown): boolean {
|
||||||
|
if (props.secondaryValueField) {
|
||||||
|
return currentSecondaryValue === (subItem as Record<string, unknown>)[props.secondaryValueField];
|
||||||
|
} else {
|
||||||
|
return currentSecondaryValue === subItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSelectedSecondaryItem(currentSecondaryValue: unknown, selectedPrimaryItem: unknown): unknown {
|
||||||
|
if (currentSecondaryValue && selectedPrimaryItem && (selectedPrimaryItem as Record<string, unknown>)[props.primarySubItemsField]) {
|
||||||
|
return getItemByKeyValue((selectedPrimaryItem as Record<string, unknown>)[props.primarySubItemsField] as Record<string, unknown>[], currentSecondaryValue, props.secondaryValueField as string);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCurrentSecondaryValue(currentSecondaryValue: Ref<unknown>, subItem: unknown): void {
|
||||||
|
if (props.secondaryValueField) {
|
||||||
|
currentSecondaryValue.value = (subItem as Record<string, unknown>)[props.secondaryValueField];
|
||||||
|
} else {
|
||||||
|
currentSecondaryValue.value = subItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// states
|
||||||
|
filterContent,
|
||||||
|
// computed states
|
||||||
|
visibleItemsCount,
|
||||||
|
filteredItems,
|
||||||
|
// functions
|
||||||
|
getFilteredSubItems,
|
||||||
|
isSecondaryValueSelected,
|
||||||
|
getSelectedSecondaryItem,
|
||||||
|
updateCurrentSecondaryValue
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<f7-sheet swipe-to-close swipe-handler=".swipe-handler"
|
<f7-sheet swipe-to-close swipe-handler=".swipe-handler"
|
||||||
:class="heightClass"
|
style="height: auto" :opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
|
||||||
:opened="show" @sheet:open="onSheetOpen" @sheet:closed="onSheetClosed">
|
|
||||||
<f7-toolbar>
|
<f7-toolbar>
|
||||||
<div class="swipe-handler"></div>
|
<div class="swipe-handler"></div>
|
||||||
<div class="left"></div>
|
<div class="left"></div>
|
||||||
@@ -9,25 +8,33 @@
|
|||||||
<f7-link sheet-close :text="tt('Done')"></f7-link>
|
<f7-link sheet-close :text="tt('Done')"></f7-link>
|
||||||
</div>
|
</div>
|
||||||
</f7-toolbar>
|
</f7-toolbar>
|
||||||
<f7-page-content>
|
<f7-searchbar ref="searchbar" custom-searchs
|
||||||
|
:value="filterContent"
|
||||||
|
:placeholder="filterPlaceholder"
|
||||||
|
:disable-button="false"
|
||||||
|
v-if="enableFilter"
|
||||||
|
@input="filterContent = $event.target.value">
|
||||||
|
</f7-searchbar>
|
||||||
|
<f7-page-content :class="'no-padding-top ' + heightClass">
|
||||||
|
<f7-list class="no-margin-top no-margin-bottom" v-if="!filteredItems || !filteredItems.length">
|
||||||
|
<f7-list-item :title="filterNoItemsText"></f7-list-item>
|
||||||
|
</f7-list>
|
||||||
<f7-treeview>
|
<f7-treeview>
|
||||||
<f7-treeview-item item-toggle
|
<f7-treeview-item item-toggle
|
||||||
:opened="isPrimaryItemHasSecondaryValue(item)"
|
:opened="isPrimaryItemHasSecondaryValue(item)"
|
||||||
:label="ti((primaryTitleField ? item[primaryTitleField] : item) as string, !!primaryTitleI18n)"
|
:label="ti((primaryTitleField ? item[primaryTitleField] : item) as string, !!primaryTitleI18n)"
|
||||||
:key="primaryKeyField ? item[primaryKeyField] : item"
|
:key="primaryKeyField ? item[primaryKeyField] : item"
|
||||||
v-for="item in items"
|
v-for="item in filteredItems">
|
||||||
v-show="item && (!primaryHiddenField || !item[primaryHiddenField])">
|
|
||||||
<template #media>
|
<template #media>
|
||||||
<ItemIcon :icon-type="primaryIconType" :icon-id="item[primaryIconField]"
|
<ItemIcon :icon-type="primaryIconType" :icon-id="item[primaryIconField]"
|
||||||
:color="primaryColorField ? item[primaryColorField] : undefined" v-if="primaryIconField"></ItemIcon>
|
:color="primaryColorField ? item[primaryColorField] : undefined" v-if="primaryIconField"></ItemIcon>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<f7-treeview-item selectable
|
<f7-treeview-item selectable
|
||||||
:selected="isSecondarySelected(subItem)"
|
:selected="isSecondaryValueSelected(currentValue, subItem)"
|
||||||
:label="ti((secondaryTitleField ? (subItem as Record<string, unknown>)[secondaryTitleField] : subItem) as string, !!secondaryTitleI18n)"
|
:label="ti((secondaryTitleField ? (subItem as Record<string, unknown>)[secondaryTitleField] : subItem) as string, !!secondaryTitleI18n)"
|
||||||
:key="secondaryKeyField ? (subItem as Record<string, unknown>)[secondaryKeyField] : subItem"
|
:key="secondaryKeyField ? (subItem as Record<string, unknown>)[secondaryKeyField] : subItem"
|
||||||
v-for="subItem in item[primarySubItemsField]"
|
v-for="subItem in getFilteredSubItems(item)"
|
||||||
v-show="subItem && (!secondaryHiddenField || !(subItem as Record<string, unknown>)[secondaryHiddenField])"
|
|
||||||
@click="onSecondaryItemClicked(subItem)">
|
@click="onSecondaryItemClicked(subItem)">
|
||||||
<template #media>
|
<template #media>
|
||||||
<ItemIcon :icon-type="secondaryIconType" :icon-id="(subItem as Record<string, unknown>)[secondaryIconField]"
|
<ItemIcon :icon-type="secondaryIconType" :icon-id="(subItem as Record<string, unknown>)[secondaryIconField]"
|
||||||
@@ -41,34 +48,19 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed, useTemplateRef } from 'vue';
|
||||||
|
import type { Searchbar } from 'framework7/types';
|
||||||
|
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
import { type TwoLevelItemSelectionBaseProps, useTwoLevelItemSelectionBase } from '@/components/base/TwoLevelItemSelectionBase.ts';
|
||||||
|
|
||||||
import { isArray } from '@/lib/common.ts';
|
|
||||||
import { type Framework7Dom, scrollToSelectedItem } from '@/lib/ui/mobile.ts';
|
import { type Framework7Dom, scrollToSelectedItem } from '@/lib/ui/mobile.ts';
|
||||||
|
|
||||||
const props = defineProps<{
|
interface MobileTwoLevelItemSelectionBaseProps extends TwoLevelItemSelectionBaseProps {
|
||||||
modelValue: unknown;
|
|
||||||
primaryKeyField?: string;
|
|
||||||
primaryTitleField?: string;
|
|
||||||
primaryTitleI18n?: boolean;
|
|
||||||
primaryIconField?: string;
|
|
||||||
primaryIconType?: string;
|
|
||||||
primaryColorField?: string;
|
|
||||||
primaryHiddenField?: string;
|
|
||||||
primarySubItemsField: string;
|
|
||||||
secondaryKeyField?: string;
|
|
||||||
secondaryValueField?: string;
|
|
||||||
secondaryTitleField?: string;
|
|
||||||
secondaryTitleI18n?: boolean;
|
|
||||||
secondaryIconField?: string;
|
|
||||||
secondaryIconType?: string;
|
|
||||||
secondaryColorField?: string;
|
|
||||||
secondaryHiddenField?: string;
|
|
||||||
items: Record<string, unknown>[];
|
|
||||||
show: boolean;
|
show: boolean;
|
||||||
}>();
|
}
|
||||||
|
|
||||||
|
const props = defineProps<MobileTwoLevelItemSelectionBaseProps>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update:modelValue', value: unknown): void;
|
(e: 'update:modelValue', value: unknown): void;
|
||||||
@@ -77,29 +69,26 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const { tt, ti } = useI18n();
|
const { tt, ti } = useI18n();
|
||||||
|
|
||||||
|
const {
|
||||||
|
filterContent,
|
||||||
|
visibleItemsCount,
|
||||||
|
filteredItems,
|
||||||
|
getFilteredSubItems,
|
||||||
|
isSecondaryValueSelected,
|
||||||
|
updateCurrentSecondaryValue
|
||||||
|
} = useTwoLevelItemSelectionBase(props);
|
||||||
|
|
||||||
|
const searchbar = useTemplateRef<Searchbar.Searchbar>('searchbar');
|
||||||
|
|
||||||
const currentValue = ref<unknown>(props.modelValue);
|
const currentValue = ref<unknown>(props.modelValue);
|
||||||
|
|
||||||
const heightClass = computed<string>(() => {
|
const heightClass = computed<string>(() => {
|
||||||
let count = 0;
|
if (visibleItemsCount.value > 6) {
|
||||||
|
|
||||||
if (isArray(props.items)) {
|
|
||||||
count = props.items.length;
|
|
||||||
} else {
|
|
||||||
for (const field in props.items) {
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(props.items, field)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 6) {
|
|
||||||
return 'tree-view-selection-huge-sheet';
|
return 'tree-view-selection-huge-sheet';
|
||||||
} else if (count > 2) {
|
} else if (visibleItemsCount.value > 2) {
|
||||||
return 'tree-view-selection-large-sheet';
|
return 'tree-view-selection-large-sheet';
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return 'tree-view-selection-default-sheet';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -110,6 +99,8 @@ function isPrimaryItemHasSecondaryValue(primaryItem: Record<string, unknown>): b
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lowerCaseFilterContent = filterContent.value?.toLowerCase() ?? '';
|
||||||
|
|
||||||
for (let i = 0; i < subItems.length; i++) {
|
for (let i = 0; i < subItems.length; i++) {
|
||||||
const secondaryItem = subItems[i];
|
const secondaryItem = subItems[i];
|
||||||
|
|
||||||
@@ -117,6 +108,14 @@ function isPrimaryItemHasSecondaryValue(primaryItem: Record<string, unknown>): b
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (props.primaryTitleField && lowerCaseFilterContent) {
|
||||||
|
const title = ti((secondaryItem as Record<string, unknown>)[props.primaryTitleField] as string, !!props.primaryTitleI18n);
|
||||||
|
|
||||||
|
if (title.toLowerCase().indexOf(lowerCaseFilterContent) >= 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (props.secondaryValueField && (secondaryItem as Record<string, unknown>)[props.secondaryValueField] === currentValue.value) {
|
if (props.secondaryValueField && (secondaryItem as Record<string, unknown>)[props.secondaryValueField] === currentValue.value) {
|
||||||
return true;
|
return true;
|
||||||
} else if (!props.secondaryValueField && secondaryItem === currentValue.value) {
|
} else if (!props.secondaryValueField && secondaryItem === currentValue.value) {
|
||||||
@@ -127,21 +126,8 @@ function isPrimaryItemHasSecondaryValue(primaryItem: Record<string, unknown>): b
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSecondarySelected(subItem: unknown): boolean {
|
|
||||||
if (props.secondaryValueField) {
|
|
||||||
return currentValue.value === (subItem as Record<string, unknown>)[props.secondaryValueField];
|
|
||||||
} else {
|
|
||||||
return currentValue.value === subItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSecondaryItemClicked(subItem: unknown): void {
|
function onSecondaryItemClicked(subItem: unknown): void {
|
||||||
if (props.secondaryValueField) {
|
updateCurrentSecondaryValue(currentValue, subItem);
|
||||||
currentValue.value = (subItem as Record<string, unknown>)[props.secondaryValueField];
|
|
||||||
} else {
|
|
||||||
currentValue.value = subItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit('update:modelValue', currentValue.value);
|
emit('update:modelValue', currentValue.value);
|
||||||
emit('update:show', false);
|
emit('update:show', false);
|
||||||
}
|
}
|
||||||
@@ -153,17 +139,23 @@ function onSheetOpen(event: { $el: Framework7Dom }): void {
|
|||||||
|
|
||||||
function onSheetClosed(): void {
|
function onSheetClosed(): void {
|
||||||
emit('update:show', false);
|
emit('update:show', false);
|
||||||
|
filterContent.value = '';
|
||||||
|
searchbar.value?.clear();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@media (min-height: 630px) {
|
@media (min-height: 630px) {
|
||||||
|
.tree-view-selection-default-sheet {
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
.tree-view-selection-large-sheet {
|
.tree-view-selection-large-sheet {
|
||||||
height: 310px;
|
height: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tree-view-selection-huge-sheet {
|
.tree-view-selection-huge-sheet {
|
||||||
height: 400px;
|
height: 340px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -118,6 +118,7 @@
|
|||||||
secondary-key-field="id" secondary-value-field="id" secondary-title-field="name"
|
secondary-key-field="id" secondary-value-field="id" secondary-title-field="name"
|
||||||
secondary-icon-field="icon" secondary-icon-type="category" secondary-color-field="color"
|
secondary-icon-field="icon" secondary-icon-type="category" secondary-color-field="color"
|
||||||
secondary-hidden-field="hidden"
|
secondary-hidden-field="hidden"
|
||||||
|
:enable-filter="true" :filter-placeholder="tt('Find category')" :filter-no-items-text="tt('No available category')"
|
||||||
:items="allCategories[CategoryType.Expense]"
|
:items="allCategories[CategoryType.Expense]"
|
||||||
v-model:show="showCategorySheet"
|
v-model:show="showCategorySheet"
|
||||||
v-model="transaction.expenseCategoryId">
|
v-model="transaction.expenseCategoryId">
|
||||||
@@ -149,6 +150,7 @@
|
|||||||
secondary-key-field="id" secondary-value-field="id" secondary-title-field="name"
|
secondary-key-field="id" secondary-value-field="id" secondary-title-field="name"
|
||||||
secondary-icon-field="icon" secondary-icon-type="category" secondary-color-field="color"
|
secondary-icon-field="icon" secondary-icon-type="category" secondary-color-field="color"
|
||||||
secondary-hidden-field="hidden"
|
secondary-hidden-field="hidden"
|
||||||
|
:enable-filter="true" :filter-placeholder="tt('Find category')" :filter-no-items-text="tt('No available category')"
|
||||||
:items="allCategories[CategoryType.Income]"
|
:items="allCategories[CategoryType.Income]"
|
||||||
v-model:show="showCategorySheet"
|
v-model:show="showCategorySheet"
|
||||||
v-model="transaction.incomeCategoryId">
|
v-model="transaction.incomeCategoryId">
|
||||||
@@ -180,6 +182,7 @@
|
|||||||
secondary-key-field="id" secondary-value-field="id" secondary-title-field="name"
|
secondary-key-field="id" secondary-value-field="id" secondary-title-field="name"
|
||||||
secondary-icon-field="icon" secondary-icon-type="category" secondary-color-field="color"
|
secondary-icon-field="icon" secondary-icon-type="category" secondary-color-field="color"
|
||||||
secondary-hidden-field="hidden"
|
secondary-hidden-field="hidden"
|
||||||
|
:enable-filter="true" :filter-placeholder="tt('Find category')" :filter-no-items-text="tt('No available category')"
|
||||||
:items="allCategories[CategoryType.Transfer]"
|
:items="allCategories[CategoryType.Transfer]"
|
||||||
v-model:show="showCategorySheet"
|
v-model:show="showCategorySheet"
|
||||||
v-model="transaction.transferCategoryId">
|
v-model="transaction.transferCategoryId">
|
||||||
|
|||||||
Reference in New Issue
Block a user