Skip to content

Commit

Permalink
feat: add DataSelector component for improved menu selection function…
Browse files Browse the repository at this point in the history
…ality

Signed-off-by: Wanjin Noh <wanjin@megazone.com>
  • Loading branch information
WANZARGEN committed Jan 2, 2025
1 parent 3a5e7d2 commit 0cdac71
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 58 deletions.
87 changes: 87 additions & 0 deletions apps/web/src/common/components/select/DataSelector.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<script setup lang="ts">
import {
ref, onMounted, toRef, watch,
} from 'vue';
import { debounce } from 'lodash';
import { PFieldTitle, PContextMenu, useContextMenuItems } from '@cloudforet/mirinae';
import type { MenuAttachHandler } from '@cloudforet/mirinae/types/hooks/use-context-menu-attach/use-context-menu-attach';
import type { DataSelectorItem } from '@/common/components/select/type';
const props = defineProps<{
label?: string;
menu?: DataSelectorItem[];
handler?: MenuAttachHandler<DataSelectorItem>;
}>();
const emit = defineEmits<{(e: 'update:selected', value: DataSelectorItem[]): void;
}>();
const searchText = ref('');
const selected = ref<DataSelectorItem[]>([]);
const {
refinedMenu,
loading,
initiateMenu,
showMoreMenu,
reloadMenu,
} = useContextMenuItems<DataSelectorItem>({
menu: toRef(props, 'menu'),
handler: toRef(props, 'handler'),
selected,
useMenuFiltering: true,
searchText,
pageSize: 10,
hideHeaderWithoutItems: true,
});
const handleUpdateSearchText = debounce((text: string) => {
searchText.value = text;
reloadMenu();
}, 200);
const handleUpdateSelected = (items: DataSelectorItem[]) => {
selected.value = items;
emit('update:selected', selected.value);
};
onMounted(() => {
selected.value = [];
emit('update:selected', selected.value);
initiateMenu();
});
watch([() => props.menu, () => props.handler], () => {
selected.value = [];
emit('update:selected', selected.value);
initiateMenu();
});
</script>

<template>
<div>
<div class="flex flex-col gap-2">
<p-field-title class="py-0 px-3"
:label="props.label"
required
/>
<p-context-menu :menu="refinedMenu"
class="data-selector-context-menu"
:loading="loading"
:search-text="searchText"
searchable
:selected="selected"
@click-show-more="showMoreMenu()"
@update:search-text="handleUpdateSearchText"
@update:selected="handleUpdateSelected"
/>
</div>
</div>
</template>

<style lang="postcss">
.data-selector-context-menu {
border: none;
}
</style>
6 changes: 6 additions & 0 deletions apps/web/src/common/components/select/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { MenuItem } from '@cloudforet/mirinae/types/controls/context-menu/type';

export interface DataSelectorItem extends MenuItem {
name: string;
label: string;
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
<script lang="ts" setup>
import {
computed, onMounted, reactive, toRef, watch,
computed, reactive, watch,
} from 'vue';
import { debounce } from 'lodash';
import {
PFieldTitle, PContextMenu, useContextMenuController,
} from '@cloudforet/mirinae';
import type { MenuItem } from '@cloudforet/mirinae/types/controls/context-menu/type';
import type { AutocompleteHandler } from '@cloudforet/mirinae/types/controls/dropdown/select-dropdown/type';
Expand All @@ -24,6 +19,7 @@ import {
getVariableModelMenuHandler,
} from '@/lib/variable-models/variable-model-menu-handler';
import DataSelector from '@/common/components/select/DataSelector.vue';
import { useProxyValue } from '@/common/composables/proxy-state';
Expand Down Expand Up @@ -52,7 +48,6 @@ const state = reactive({
};
return getVariableModelMenuHandler([variableModelInfo]);
}),
dataSourceSearchText: '',
// data type
dataTypeMenuItems: computed<MenuItem[]>(() => {
if (!state.selectedDataSource.length) return [];
Expand All @@ -69,38 +64,17 @@ const state = reactive({
{ type: 'item', name: 'usage_quantity', label: 'Usage' },
...(additionalMenuItems || []),
];
return dataTypeItems.filter((d) => d.label.toLowerCase().includes(state.dataTypeSearchText.toLowerCase()));
return dataTypeItems;
}),
dataTypeSearchText: '',
selectedDataType: [] as MenuItem[],
});
const {
refinedMenu,
initiateMenu,
reloadMenu,
} = useContextMenuController({
targetRef: toRef(state, 'targetRef'),
searchText: toRef(state, 'dataSourceSearchText'),
handler: toRef(state, 'dataSourceMenuHandler'),
selected: toRef(state, 'selectedDataSource'),
pageSize: 10,
});
/* Event */
const handleUpdateCostDataSourceSearchText = debounce((text: string) => {
state.dataSourceSearchText = text;
reloadMenu();
}, 200);
const handleSelectDataSource = () => {
const handleSelectDataSource = (items: MenuItem[]) => {
state.selectedDataSource = items;
state.selectedDataType = [];
};
onMounted(() => {
state.selectedDataSource = [];
state.selectedDataType = [];
initiateMenu();
});
/* Watcher */
watch(() => state.selectedDataSource, (val) => {
state.proxySelectedCostDataSourceId = val[0]?.name;
Expand All @@ -113,27 +87,15 @@ watch(() => state.selectedDataType, (val) => {
<template>
<div class="widget-form-cost-data-source-popper">
<div class="data-source-select-col">
<p-field-title class="field-title"
:label="i18n.t('Data Source')"
required
/>
<p-context-menu :menu="refinedMenu"
:search-text="state.dataSourceSearchText"
searchable
:selected.sync="state.selectedDataSource"
@update:search-text="handleUpdateCostDataSourceSearchText"
@select="handleSelectDataSource"
<data-selector :label="i18n.t('Data Source')"
:handler="state.dataSourceMenuHandler"
@update:selected="handleSelectDataSource"
/>
</div>
<div class="data-source-select-col">
<p-field-title class="field-title"
:label="i18n.t('Data Type')"
required
/>
<p-context-menu :menu="state.dataTypeMenuItems"
:search-text.sync="state.dataTypeSearchText"
searchable
:selected.sync="state.selectedDataType"
<data-selector :label="i18n.t('Data Type')"
:menu="state.dataTypeMenuItems"
@update:selected="state.selectedDataType = $event"
/>
</div>
</div>
Expand All @@ -151,17 +113,9 @@ watch(() => state.selectedDataType, (val) => {
gap: 0.5rem;
width: 16rem;
padding: 0.75rem 0;
.field-title {
padding: 0 0.75rem;
}
&:last-child {
@apply border-r-0;
}
}
}
/* custom design-system component - p-context-menu */
:deep(.p-context-menu) {
border: none;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface MenuAttachHandler<Item extends MenuItem = MenuItem> {

export interface UseContextMenuAttachOptions<Item extends MenuItem = MenuItem> {
attachHandler?: Ref<MenuAttachHandler<Item>|undefined>; // custom handler
menu?: Ref<Item[]>; // required when to use default attach handler. one of menu or attachHandler is required.
menu?: Ref<Item[]|undefined>; // required when to use default attach handler. one of menu or attachHandler is required.
searchText?: Ref<string>; // it will be passed to the attach handler as the argument, so the handler can filter the items based on this text.
pageSize?: Ref<number|undefined>|number; // required when to use show more button to attach items
filterItems?: Ref<Item[]>; // items to be filtered out from the attached menu
Expand Down

0 comments on commit 0cdac71

Please sign in to comment.