mirror of https://github.com/halo-dev/halo
pref: implement business selector using new selector component (#6525)
#### What type of PR is this? /kind improvement /area ui /milestone 2.19.x #### What this PR does / why we need it: 使用 #6473 中重构的 Formkit Select 组件来实现用户、文章、页面等各种业务搜索组件。 Fixes https://github.com/halo-dev/halo/issues/4931 #### How to test it? 测试各类搜索组件是否正常可用。 测试从旧版本升级后,原有数据是否可以正常显示。 #### Does this PR introduce a user-facing change? ```release-note 使用重构的 Formkit Select 组件来实现业务选择器。 ```pull/6536/head v2.19.0-rc.2
parent
a5c6d6672f
commit
281567877a
|
@ -1,6 +1,6 @@
|
||||||
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
||||||
import { defaultIcon, select, selects } from "@formkit/inputs";
|
|
||||||
import { coreApiClient } from "@halo-dev/api-client";
|
import { coreApiClient } from "@halo-dev/api-client";
|
||||||
|
import { select } from "./select";
|
||||||
|
|
||||||
function optionsHandler(node: FormKitNode) {
|
function optionsHandler(node: FormKitNode) {
|
||||||
node.on("created", async () => {
|
node.on("created", async () => {
|
||||||
|
@ -9,18 +9,20 @@ function optionsHandler(node: FormKitNode) {
|
||||||
sort: ["metadata.creationTimestamp,desc"],
|
sort: ["metadata.creationTimestamp,desc"],
|
||||||
});
|
});
|
||||||
|
|
||||||
node.props.options = data.items.map((group) => {
|
if (node.context) {
|
||||||
|
node.context.attrs.options = data.items.map((group) => {
|
||||||
return {
|
return {
|
||||||
value: group.metadata.name,
|
value: group.metadata.name,
|
||||||
label: group.spec.displayName,
|
label: group.spec.displayName,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const attachmentGroupSelect: FormKitTypeDefinition = {
|
export const attachmentGroupSelect: FormKitTypeDefinition = {
|
||||||
...select,
|
...select,
|
||||||
props: ["placeholder"],
|
props: ["placeholder"],
|
||||||
forceTypeProp: "nativeSelect",
|
forceTypeProp: "select",
|
||||||
features: [optionsHandler, selects, defaultIcon("select", "select")],
|
features: [optionsHandler],
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,23 +1,25 @@
|
||||||
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
||||||
import { defaultIcon, select, selects } from "@formkit/inputs";
|
|
||||||
import { coreApiClient } from "@halo-dev/api-client";
|
import { coreApiClient } from "@halo-dev/api-client";
|
||||||
|
import { select } from "./select";
|
||||||
|
|
||||||
function optionsHandler(node: FormKitNode) {
|
function optionsHandler(node: FormKitNode) {
|
||||||
node.on("created", async () => {
|
node.on("created", async () => {
|
||||||
const { data } = await coreApiClient.storage.policy.listPolicy();
|
const { data } = await coreApiClient.storage.policy.listPolicy();
|
||||||
|
|
||||||
node.props.options = data.items.map((policy) => {
|
if (node.context) {
|
||||||
|
node.context.attrs.options = data.items.map((policy) => {
|
||||||
return {
|
return {
|
||||||
value: policy.metadata.name,
|
value: policy.metadata.name,
|
||||||
label: policy.spec.displayName,
|
label: policy.spec.displayName,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const attachmentPolicySelect: FormKitTypeDefinition = {
|
export const attachmentPolicySelect: FormKitTypeDefinition = {
|
||||||
...select,
|
...select,
|
||||||
props: ["placeholder"],
|
props: ["placeholder"],
|
||||||
forceTypeProp: "nativeSelect",
|
forceTypeProp: "select",
|
||||||
features: [optionsHandler, selects, defaultIcon("select", "select")],
|
features: [optionsHandler],
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
||||||
import { defaultIcon, select, selects } from "@formkit/inputs";
|
|
||||||
import { coreApiClient } from "@halo-dev/api-client";
|
import { coreApiClient } from "@halo-dev/api-client";
|
||||||
|
import { select } from "./select";
|
||||||
|
|
||||||
function optionsHandler(node: FormKitNode) {
|
function optionsHandler(node: FormKitNode) {
|
||||||
node.on("created", async () => {
|
node.on("created", async () => {
|
||||||
|
@ -8,18 +8,20 @@ function optionsHandler(node: FormKitNode) {
|
||||||
fieldSelector: [`name=(${node.props.menuItems.join(",")})`],
|
fieldSelector: [`name=(${node.props.menuItems.join(",")})`],
|
||||||
});
|
});
|
||||||
|
|
||||||
node.props.options = data.items.map((menuItem) => {
|
if (node.context) {
|
||||||
|
node.context.attrs.options = data.items.map((menuItem) => {
|
||||||
return {
|
return {
|
||||||
value: menuItem.metadata.name,
|
value: menuItem.metadata.name,
|
||||||
label: menuItem.status?.displayName,
|
label: menuItem.status?.displayName,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const menuItemSelect: FormKitTypeDefinition = {
|
export const menuItemSelect: FormKitTypeDefinition = {
|
||||||
...select,
|
...select,
|
||||||
props: ["placeholder", "menuItems"],
|
props: ["placeholder", "menuItems"],
|
||||||
forceTypeProp: "nativeSelect",
|
forceTypeProp: "select",
|
||||||
features: [optionsHandler, selects, defaultIcon("select", "select")],
|
features: [optionsHandler],
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,29 +1,65 @@
|
||||||
import { postLabels } from "@/constants/labels";
|
import { postLabels } from "@/constants/labels";
|
||||||
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
||||||
import { defaultIcon, select, selects } from "@formkit/inputs";
|
|
||||||
import { consoleApiClient } from "@halo-dev/api-client";
|
import { consoleApiClient } from "@halo-dev/api-client";
|
||||||
|
import { select } from "./select";
|
||||||
|
|
||||||
function optionsHandler(node: FormKitNode) {
|
async function search({ page, size, keyword }) {
|
||||||
node.on("created", async () => {
|
|
||||||
const { data } = await consoleApiClient.content.post.listPosts({
|
const { data } = await consoleApiClient.content.post.listPosts({
|
||||||
|
page,
|
||||||
|
size,
|
||||||
|
keyword,
|
||||||
labelSelector: [
|
labelSelector: [
|
||||||
`${postLabels.DELETED}=false`,
|
`${postLabels.DELETED}=false`,
|
||||||
`${postLabels.PUBLISHED}=true`,
|
`${postLabels.PUBLISHED}=true`,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
node.props.options = data.items.map((post) => {
|
return {
|
||||||
|
options: data.items.map((post) => {
|
||||||
|
return {
|
||||||
|
value: post.post.metadata.name,
|
||||||
|
label: post.post.spec.title,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
total: data.total,
|
||||||
|
size: data.size,
|
||||||
|
page: data.page,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findOptionsByValues(values: string[]) {
|
||||||
|
if (values.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await consoleApiClient.content.post.listPosts({
|
||||||
|
fieldSelector: [`metadata.name=(${values.join(",")})`],
|
||||||
|
});
|
||||||
|
|
||||||
|
return data.items.map((post) => {
|
||||||
return {
|
return {
|
||||||
value: post.post.metadata.name,
|
value: post.post.metadata.name,
|
||||||
label: post.post.spec.title,
|
label: post.post.spec.title,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function optionsHandler(node: FormKitNode) {
|
||||||
|
node.on("created", async () => {
|
||||||
|
node.props = {
|
||||||
|
...node.props,
|
||||||
|
remote: true,
|
||||||
|
remoteOption: {
|
||||||
|
search,
|
||||||
|
findOptionsByValues,
|
||||||
|
},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const postSelect: FormKitTypeDefinition = {
|
export const postSelect: FormKitTypeDefinition = {
|
||||||
...select,
|
...select,
|
||||||
props: ["placeholder"],
|
props: ["placeholder"],
|
||||||
forceTypeProp: "nativeSelect",
|
forceTypeProp: "select",
|
||||||
features: [optionsHandler, selects, defaultIcon("select", "select")],
|
features: [optionsHandler],
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { rbacAnnotations } from "@/constants/annotations";
|
||||||
import { roleLabels } from "@/constants/labels";
|
import { roleLabels } from "@/constants/labels";
|
||||||
import { i18n } from "@/locales";
|
import { i18n } from "@/locales";
|
||||||
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
||||||
import { defaultIcon, select, selects } from "@formkit/inputs";
|
|
||||||
import { coreApiClient } from "@halo-dev/api-client";
|
import { coreApiClient } from "@halo-dev/api-client";
|
||||||
|
import { select } from "./select";
|
||||||
|
|
||||||
function optionsHandler(node: FormKitNode) {
|
function optionsHandler(node: FormKitNode) {
|
||||||
node.on("created", async () => {
|
node.on("created", async () => {
|
||||||
|
@ -13,7 +13,7 @@ function optionsHandler(node: FormKitNode) {
|
||||||
labelSelector: [`!${roleLabels.TEMPLATE}`],
|
labelSelector: [`!${roleLabels.TEMPLATE}`],
|
||||||
});
|
});
|
||||||
|
|
||||||
node.props.options = [
|
const options = [
|
||||||
{
|
{
|
||||||
label: i18n.global.t(
|
label: i18n.global.t(
|
||||||
"core.user.grant_permission_modal.fields.role.placeholder"
|
"core.user.grant_permission_modal.fields.role.placeholder"
|
||||||
|
@ -29,6 +29,9 @@ function optionsHandler(node: FormKitNode) {
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
if (node.context) {
|
||||||
|
node.context.attrs.options = options;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,5 +39,5 @@ export const roleSelect: FormKitTypeDefinition = {
|
||||||
...select,
|
...select,
|
||||||
props: ["placeholder"],
|
props: ["placeholder"],
|
||||||
forceTypeProp: "nativeSelect",
|
forceTypeProp: "nativeSelect",
|
||||||
features: [optionsHandler, selects, defaultIcon("select", "select")],
|
features: [optionsHandler],
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,7 @@ import SelectContainer from "./SelectContainer.vue";
|
||||||
import { axiosInstance } from "@halo-dev/api-client";
|
import { axiosInstance } from "@halo-dev/api-client";
|
||||||
import { get, has, type PropertyPath } from "lodash-es";
|
import { get, has, type PropertyPath } from "lodash-es";
|
||||||
import { useDebounceFn } from "@vueuse/core";
|
import { useDebounceFn } from "@vueuse/core";
|
||||||
|
import { useFuse } from "@vueuse/integrations/useFuse";
|
||||||
import type { AxiosRequestConfig } from "axios";
|
import type { AxiosRequestConfig } from "axios";
|
||||||
|
|
||||||
export interface SelectProps {
|
export interface SelectProps {
|
||||||
|
@ -184,7 +185,6 @@ const selectProps: SelectProps = shallowReactive({
|
||||||
placeholder: "",
|
placeholder: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const hasSelected = ref(false);
|
|
||||||
const isRemote = computed(() => !!selectProps.action || !!selectProps.remote);
|
const isRemote = computed(() => !!selectProps.action || !!selectProps.remote);
|
||||||
const hasMoreOptions = computed(
|
const hasMoreOptions = computed(
|
||||||
() => options.value && options.value.length < total.value
|
() => options.value && options.value.length < total.value
|
||||||
|
@ -400,7 +400,7 @@ const mapUnresolvedOptions = async (
|
||||||
value: string;
|
value: string;
|
||||||
}>
|
}>
|
||||||
> => {
|
> => {
|
||||||
if (!selectProps.action || !selectProps.remote) {
|
if (!isRemote.value) {
|
||||||
if (selectProps.allowCreate) {
|
if (selectProps.allowCreate) {
|
||||||
// TODO: Add mapped values to options
|
// TODO: Add mapped values to options
|
||||||
return unmappedSelectValues.map((value) => ({ label: value, value }));
|
return unmappedSelectValues.map((value) => ({ label: value, value }));
|
||||||
|
@ -413,10 +413,17 @@ const mapUnresolvedOptions = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
// Asynchronous request for options, fetch label and value via API.
|
// Asynchronous request for options, fetch label and value via API.
|
||||||
let mappedOptions: Array<{
|
let mappedOptions:
|
||||||
|
| Array<{
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
}> = [];
|
}>
|
||||||
|
| undefined = undefined;
|
||||||
|
if (noNeedFetchOptions.value) {
|
||||||
|
mappedOptions = cacheAllOptions.value?.filter((option) =>
|
||||||
|
unmappedSelectValues.includes(option.value)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
if (selectProps.action) {
|
if (selectProps.action) {
|
||||||
mappedOptions = await fetchRemoteMappedOptions(unmappedSelectValues);
|
mappedOptions = await fetchRemoteMappedOptions(unmappedSelectValues);
|
||||||
} else if (selectProps.remote) {
|
} else if (selectProps.remote) {
|
||||||
|
@ -425,6 +432,11 @@ const mapUnresolvedOptions = async (
|
||||||
unmappedSelectValues
|
unmappedSelectValues
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mappedOptions) {
|
||||||
|
return unmappedSelectValues.map((value) => ({ label: value, value }));
|
||||||
|
}
|
||||||
// Get values that are still unresolved.
|
// Get values that are still unresolved.
|
||||||
const unmappedValues = unmappedSelectValues.filter(
|
const unmappedValues = unmappedSelectValues.filter(
|
||||||
(value) => !mappedOptions.find((option) => option.value === value)
|
(value) => !mappedOptions.find((option) => option.value === value)
|
||||||
|
@ -496,11 +508,12 @@ onMounted(async () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
const stopSelectedWatch = watch(
|
||||||
() => [options.value, props.context.value],
|
() => [options.value, props.context.value],
|
||||||
async () => {
|
async () => {
|
||||||
if (!hasSelected.value && options.value) {
|
if (options.value) {
|
||||||
selectOptions.value = await fetchSelectedOptions();
|
const selectedOption = await fetchSelectedOptions();
|
||||||
|
selectOptions.value = selectedOption;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -521,7 +534,7 @@ watch(
|
||||||
|
|
||||||
const handleUpdate = (value: Array<{ label: string; value: string }>) => {
|
const handleUpdate = (value: Array<{ label: string; value: string }>) => {
|
||||||
const values = value.map((item) => item.value);
|
const values = value.map((item) => item.value);
|
||||||
hasSelected.value = true;
|
stopSelectedWatch();
|
||||||
selectOptions.value = value;
|
selectOptions.value = value;
|
||||||
if (selectProps.multiple) {
|
if (selectProps.multiple) {
|
||||||
props.context.node.input(values);
|
props.context.node.input(values);
|
||||||
|
@ -544,14 +557,23 @@ const fetchOptions = async (
|
||||||
}
|
}
|
||||||
// If the total number of options is less than the page size, no more requests are made.
|
// If the total number of options is less than the page size, no more requests are made.
|
||||||
if (noNeedFetchOptions.value) {
|
if (noNeedFetchOptions.value) {
|
||||||
const filterOptions = cacheAllOptions.value?.filter((option) =>
|
const { results } = useFuse<{
|
||||||
option.label.includes(tempKeyword)
|
label: string;
|
||||||
);
|
value: string;
|
||||||
|
}>(tempKeyword, cacheAllOptions.value || [], {
|
||||||
|
fuseOptions: {
|
||||||
|
keys: ["label", "value"],
|
||||||
|
threshold: 0,
|
||||||
|
ignoreLocation: true,
|
||||||
|
},
|
||||||
|
matchAllWhenSearchEmpty: true,
|
||||||
|
});
|
||||||
|
const filterOptions = results.value?.map((fuseItem) => fuseItem.item) || [];
|
||||||
return {
|
return {
|
||||||
options: filterOptions || [],
|
options: filterOptions || [],
|
||||||
page: page.value,
|
page: page.value,
|
||||||
size: size.value,
|
size: size.value,
|
||||||
total: filterOptions?.length || 0,
|
total: filterOptions.length || 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
|
|
|
@ -3,21 +3,57 @@ import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
||||||
import { defaultIcon, select, selects } from "@formkit/inputs";
|
import { defaultIcon, select, selects } from "@formkit/inputs";
|
||||||
import { consoleApiClient } from "@halo-dev/api-client";
|
import { consoleApiClient } from "@halo-dev/api-client";
|
||||||
|
|
||||||
function optionsHandler(node: FormKitNode) {
|
async function search({ page, size, keyword }) {
|
||||||
node.on("created", async () => {
|
|
||||||
const { data } = await consoleApiClient.content.singlePage.listSinglePages({
|
const { data } = await consoleApiClient.content.singlePage.listSinglePages({
|
||||||
|
page,
|
||||||
|
size,
|
||||||
|
keyword,
|
||||||
labelSelector: [
|
labelSelector: [
|
||||||
`${singlePageLabels.DELETED}=false`,
|
`${singlePageLabels.DELETED}=false`,
|
||||||
`${singlePageLabels.PUBLISHED}=true`,
|
`${singlePageLabels.PUBLISHED}=true`,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
node.props.options = data.items.map((singlePage) => {
|
return {
|
||||||
|
options: data.items.map((singlePage) => {
|
||||||
|
return {
|
||||||
|
value: singlePage.page.metadata.name,
|
||||||
|
label: singlePage.page.spec.title,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
total: data.total,
|
||||||
|
size: data.size,
|
||||||
|
page: data.page,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findOptionsByValues(values: string[]) {
|
||||||
|
if (values.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await consoleApiClient.content.singlePage.listSinglePages({
|
||||||
|
fieldSelector: [`metadata.name=(${values.join(",")})`],
|
||||||
|
});
|
||||||
|
|
||||||
|
return data.items.map((singlePage) => {
|
||||||
return {
|
return {
|
||||||
value: singlePage.page.metadata.name,
|
value: singlePage.page.metadata.name,
|
||||||
label: singlePage.page.spec.title,
|
label: singlePage.page.spec.title,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function optionsHandler(node: FormKitNode) {
|
||||||
|
node.on("created", async () => {
|
||||||
|
node.props = {
|
||||||
|
...node.props,
|
||||||
|
remote: true,
|
||||||
|
remoteOption: {
|
||||||
|
search,
|
||||||
|
findOptionsByValues,
|
||||||
|
},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,25 +2,62 @@
|
||||||
// We will provide searchable user selection components in the future.
|
// We will provide searchable user selection components in the future.
|
||||||
|
|
||||||
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
||||||
import { defaultIcon, select, selects } from "@formkit/inputs";
|
import { consoleApiClient } from "@halo-dev/api-client";
|
||||||
import { coreApiClient } from "@halo-dev/api-client";
|
import { select } from "./select";
|
||||||
|
|
||||||
|
const search = async ({ page, size, keyword }) => {
|
||||||
|
const { data } = await consoleApiClient.user.listUsers({
|
||||||
|
page,
|
||||||
|
size,
|
||||||
|
keyword,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
options: data.items?.map((user) => {
|
||||||
|
return {
|
||||||
|
value: user.user.metadata.name,
|
||||||
|
label: user.user.spec.displayName,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
total: data.total,
|
||||||
|
size: data.size,
|
||||||
|
page: data.page,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const findOptionsByValues = async (values: string[]) => {
|
||||||
|
if (values.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await consoleApiClient.user.listUsers({
|
||||||
|
fieldSelector: [`metadata.name=(${values.join(",")})`],
|
||||||
|
});
|
||||||
|
|
||||||
|
return data.items?.map((user) => {
|
||||||
|
return {
|
||||||
|
value: user.user.metadata.name,
|
||||||
|
label: user.user.spec.displayName,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
function optionsHandler(node: FormKitNode) {
|
function optionsHandler(node: FormKitNode) {
|
||||||
node.on("created", async () => {
|
node.on("created", async () => {
|
||||||
const { data } = await coreApiClient.user.listUser();
|
node.props = {
|
||||||
|
...node.props,
|
||||||
node.props.options = data.items.map((user) => {
|
remote: true,
|
||||||
return {
|
remoteOption: {
|
||||||
value: user.metadata.name,
|
search,
|
||||||
label: user.spec.displayName,
|
findOptionsByValues,
|
||||||
|
},
|
||||||
|
searchable: true,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const userSelect: FormKitTypeDefinition = {
|
export const userSelect: FormKitTypeDefinition = {
|
||||||
...select,
|
...select,
|
||||||
props: ["placeholder"],
|
props: ["placeholder"],
|
||||||
forceTypeProp: "nativeSelect",
|
forceTypeProp: "select",
|
||||||
features: [optionsHandler, selects, defaultIcon("select", "select")],
|
features: [optionsHandler],
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue