Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release-2.19] fix: formkit select component not emitting event or updating selection on value change #6610

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions ui/src/formkit/inputs/select/SelectContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@
import { IconArrowDownLine, IconCloseCircle } from "@halo-dev/components";
import {
computed,
defineEmits,
nextTick,
onMounted,
onUnmounted,
ref,
defineEmits,
type PropType,
onUnmounted,
} from "vue";

import SelectSelector from "./SelectSelector.vue";
import { Dropdown } from "floating-vue";
import MultipleSelectSelector from "./MultipleSelectSelector.vue";
import SelectDropdownContainer from "./SelectDropdownContainer.vue";
import { Dropdown } from "floating-vue";
import SelectSelector from "./SelectSelector.vue";

const props = defineProps({
multiple: {
Expand Down Expand Up @@ -95,7 +95,7 @@ const inputRef = ref<HTMLInputElement | null>();
const searchKeyword = ref<string>("");
const isDropdownVisible = ref<boolean>(false);
const selectedOptions = computed({
get: () => props.selected,
get: () => [...props.selected],
set: (value) => {
emit("update", value);
},
Expand Down
62 changes: 43 additions & 19 deletions ui/src/formkit/inputs/select/SelectMain.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export interface SelectProps {
remoteOptimize?: boolean;

/**
* Allows the creation of new options, only available in local mode.
* Allows the creation of new options, only available in local mode. Default is false.
*/
allowCreate?: boolean;

Expand Down Expand Up @@ -209,7 +209,7 @@ const initSelectProps = () => {
selectProps.maxCount = nodeProps.maxCount ?? NaN;
selectProps.placeholder = nodeProps.placeholder ?? "";
selectProps.action = nodeProps.action ?? "";
selectProps.remoteOptimize = nodeProps.remoteOptimize ?? true;
selectProps.remoteOptimize = !isFalse(nodeProps.remoteOptimize, true);
selectProps.requestOption = {
...{
method: "GET",
Expand All @@ -225,12 +225,12 @@ const initSelectProps = () => {
...(nodeProps.requestOption ?? {}),
};
selectProps.multiple = !isFalse(nodeProps.multiple);
selectProps.sortable = !isFalse(nodeProps.sortable);
selectProps.sortable = !isFalse(nodeProps.sortable, true);
selectProps.remote = !isFalse(nodeProps.remote);
selectProps.allowCreate = !isFalse(nodeProps.allowCreate);
selectProps.clearable = !isFalse(nodeProps.clearable);
selectProps.searchable = !isFalse(nodeProps.searchable);
selectProps.autoSelect = !isFalse(nodeProps.autoSelect) || true;
selectProps.autoSelect = !isFalse(nodeProps.autoSelect, true);
if (selectProps.remote) {
if (!nodeProps.remoteOption) {
throw new Error("remoteOption is required when remote is true.");
Expand Down Expand Up @@ -543,14 +543,25 @@ const getAutoSelectedOption = ():
});
};

const stopSelectedWatch = watch(
() => [options.value, props.context.value],
async () => {
if (options.value) {
watch(
() => props.context.value,
async (newValue) => {
const selectedValues = selectOptions.value?.map((item) => item.value) || [];
if (selectedValues.length > 0 && selectedValues.includes(newValue)) {
return;
}
const selectedOption = await fetchSelectedOptions();
selectOptions.value = selectedOption;
}
);

watch(
() => options.value,
async (newOptions) => {
if (newOptions && newOptions.length > 0) {
const selectedOption = await fetchSelectedOptions();
if (selectedOption) {
selectOptions.value = selectedOption;
return;
}
const isAutoSelect =
selectProps.autoSelect &&
Expand All @@ -562,12 +573,12 @@ const stopSelectedWatch = watch(
// Automatically select the first option when the selected value is empty.
const autoSelectedOption = getAutoSelectedOption();
if (autoSelectedOption) {
selectOptions.value = [autoSelectedOption];
handleUpdate(selectOptions.value);
handleUpdate([autoSelectedOption]);
}
}
}
}
},
{ once: true }
);

// When attr options are processed asynchronously, it is necessary to monitor
Expand All @@ -581,14 +592,27 @@ watch(
}
);

const handleSelectedUpdate = (
value: Array<{ label: string; value: string }>
) => {
stopSelectedWatch();
handleUpdate(value);
const handleUpdate = async (value: Array<{ label: string; value: string }>) => {
const oldSelectValue = selectOptions.value;
if (
oldSelectValue &&
value.length === oldSelectValue.length &&
value.every((item, index) => item.value === oldSelectValue[index].value)
) {
return;
}
const newValue = value.map((item) => {
return {
label: item.label,
value: item.value,
};
});
handleSetNodeValue(newValue);
await props.context.node.settled;
props.context.attrs.onChange?.(newValue);
};

const handleUpdate = (value: Array<{ label: string; value: string }>) => {
const handleSetNodeValue = (value: Array<{ label: string; value: string }>) => {
const values = value.map((item) => item.value);
selectOptions.value = value;
if (selectProps.multiple) {
Expand Down Expand Up @@ -725,7 +749,7 @@ const handleNextPage = async () => {
:clearable="selectProps.clearable"
:searchable="selectProps.searchable"
:auto-select="selectProps.autoSelect"
@update="handleSelectedUpdate"
@update="handleUpdate"
@search="handleSearch"
@load-more="handleNextPage"
/>
Expand Down
6 changes: 5 additions & 1 deletion ui/src/formkit/inputs/select/isFalse.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export const isFalse = (value: string | boolean | undefined | null) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isFalse = (value: any, onlyBoolean = false) => {
if (onlyBoolean) {
return [false, "false"].includes(value);
}
return [undefined, null, "false", false].includes(value);
};
Loading