Loading client/src/components/Form/Elements/FormSelect.vue +33 −5 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ type ValueWithTags = SelectValue & { tags: string[] }; interface SelectOption { label: string; value: SelectValue; id?: string; } const props = defineProps({ Loading Loading @@ -64,22 +65,29 @@ const filteredOptions = useFilterObjectArray(() => props.options, filter, ["labe const optionReorderThreshold = 8; const reorderedOptions = computed(() => { let result; if (!props.multiple || filteredOptions.value.length <= optionReorderThreshold) { return filteredOptions.value; result = filteredOptions.value; } else { const selectedOptions: SelectOption[] = []; const unselectedOptions: SelectOption[] = []; filteredOptions.value.forEach((option) => { if (selectedValues.value.includes(option.value)) { if (isSelected(option.value)) { selectedOptions.push(option); } else { unselectedOptions.push(option); } }); return [...unselectedOptions, ...selectedOptions]; result = [...unselectedOptions, ...selectedOptions]; } return result.map((option) => { return { ...option, id: trackBy.value === "id" && isValueWithId(option.value) ? option.value.id : undefined, }; }); }); /** Loading Loading @@ -114,15 +122,25 @@ const selectedLabel: ComputedRef<string> = computed(() => { */ const selectedValues = computed(() => (Array.isArray(props.value) ? props.value : [props.value])); /** * Tracks selected ids in case of objects with string ids */ const selectedIds = computed(() => { return selectedValues.value.map((v) => (isValueWithId(v) ? v.id : undefined)).filter((v) => v !== undefined); }); /** * Whether current value(s) will be tracked by id or value */ const trackBy = computed(() => { return selectedIds.value.length > 0 ? "id" : "value"; }); /** * Tracks current value and emits changes */ const currentValue = computed({ get: () => props.options.filter((option: SelectOption) => selectedValues.value.includes(option.value)), get: () => props.options.filter((option: SelectOption) => isSelected(option.value)).map(getSelectOption), set: (val: Array<SelectOption> | SelectOption): void => { if (Array.isArray(val)) { if (val.length > 0) { Loading @@ -146,6 +164,16 @@ function setInitialValue(): void { } } function getSelectOption(option: SelectOption): SelectOption { if (isValueWithId(option.value)) { return { ...option, id: option.value.id, }; } return option; } /** * Watches changes in select options and adjusts initial value if necessary */ Loading Loading @@ -200,7 +228,7 @@ function isSelected(item: SelectValue): boolean { :placeholder="placeholder" :selected-label="selectedLabel" :select-label="null" track-by="value" :track-by="trackBy" :internal-search="false" @search-change="onSearchChange" @open="onOpen" Loading Loading
client/src/components/Form/Elements/FormSelect.vue +33 −5 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ type ValueWithTags = SelectValue & { tags: string[] }; interface SelectOption { label: string; value: SelectValue; id?: string; } const props = defineProps({ Loading Loading @@ -64,22 +65,29 @@ const filteredOptions = useFilterObjectArray(() => props.options, filter, ["labe const optionReorderThreshold = 8; const reorderedOptions = computed(() => { let result; if (!props.multiple || filteredOptions.value.length <= optionReorderThreshold) { return filteredOptions.value; result = filteredOptions.value; } else { const selectedOptions: SelectOption[] = []; const unselectedOptions: SelectOption[] = []; filteredOptions.value.forEach((option) => { if (selectedValues.value.includes(option.value)) { if (isSelected(option.value)) { selectedOptions.push(option); } else { unselectedOptions.push(option); } }); return [...unselectedOptions, ...selectedOptions]; result = [...unselectedOptions, ...selectedOptions]; } return result.map((option) => { return { ...option, id: trackBy.value === "id" && isValueWithId(option.value) ? option.value.id : undefined, }; }); }); /** Loading Loading @@ -114,15 +122,25 @@ const selectedLabel: ComputedRef<string> = computed(() => { */ const selectedValues = computed(() => (Array.isArray(props.value) ? props.value : [props.value])); /** * Tracks selected ids in case of objects with string ids */ const selectedIds = computed(() => { return selectedValues.value.map((v) => (isValueWithId(v) ? v.id : undefined)).filter((v) => v !== undefined); }); /** * Whether current value(s) will be tracked by id or value */ const trackBy = computed(() => { return selectedIds.value.length > 0 ? "id" : "value"; }); /** * Tracks current value and emits changes */ const currentValue = computed({ get: () => props.options.filter((option: SelectOption) => selectedValues.value.includes(option.value)), get: () => props.options.filter((option: SelectOption) => isSelected(option.value)).map(getSelectOption), set: (val: Array<SelectOption> | SelectOption): void => { if (Array.isArray(val)) { if (val.length > 0) { Loading @@ -146,6 +164,16 @@ function setInitialValue(): void { } } function getSelectOption(option: SelectOption): SelectOption { if (isValueWithId(option.value)) { return { ...option, id: option.value.id, }; } return option; } /** * Watches changes in select options and adjusts initial value if necessary */ Loading Loading @@ -200,7 +228,7 @@ function isSelected(item: SelectValue): boolean { :placeholder="placeholder" :selected-label="selectedLabel" :select-label="null" track-by="value" :track-by="trackBy" :internal-search="false" @search-change="onSearchChange" @open="onOpen" Loading