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

pref: implement business selector using new selector component #6525

Merged
merged 3 commits into from
Aug 27, 2024
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
20 changes: 11 additions & 9 deletions ui/src/formkit/inputs/attachment-group-select.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
import { defaultIcon, select, selects } from "@formkit/inputs";
import { coreApiClient } from "@halo-dev/api-client";
import { select } from "./select";

function optionsHandler(node: FormKitNode) {
node.on("created", async () => {
Expand All @@ -9,18 +9,20 @@ function optionsHandler(node: FormKitNode) {
sort: ["metadata.creationTimestamp,desc"],
});

node.props.options = data.items.map((group) => {
return {
value: group.metadata.name,
label: group.spec.displayName,
};
});
if (node.context) {
node.context.attrs.options = data.items.map((group) => {
return {
value: group.metadata.name,
label: group.spec.displayName,
};
});
}
});
}

export const attachmentGroupSelect: FormKitTypeDefinition = {
...select,
props: ["placeholder"],
forceTypeProp: "nativeSelect",
features: [optionsHandler, selects, defaultIcon("select", "select")],
forceTypeProp: "select",
features: [optionsHandler],
};
20 changes: 11 additions & 9 deletions ui/src/formkit/inputs/attachment-policy-select.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
import { defaultIcon, select, selects } from "@formkit/inputs";
import { coreApiClient } from "@halo-dev/api-client";
import { select } from "./select";

function optionsHandler(node: FormKitNode) {
node.on("created", async () => {
const { data } = await coreApiClient.storage.policy.listPolicy();

node.props.options = data.items.map((policy) => {
return {
value: policy.metadata.name,
label: policy.spec.displayName,
};
});
if (node.context) {
node.context.attrs.options = data.items.map((policy) => {
return {
value: policy.metadata.name,
label: policy.spec.displayName,
};
});
}
});
}

export const attachmentPolicySelect: FormKitTypeDefinition = {
...select,
props: ["placeholder"],
forceTypeProp: "nativeSelect",
features: [optionsHandler, selects, defaultIcon("select", "select")],
forceTypeProp: "select",
features: [optionsHandler],
};
20 changes: 11 additions & 9 deletions ui/src/formkit/inputs/menu-item-select.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
import { defaultIcon, select, selects } from "@formkit/inputs";
import { coreApiClient } from "@halo-dev/api-client";
import { select } from "./select";

function optionsHandler(node: FormKitNode) {
node.on("created", async () => {
const { data } = await coreApiClient.menuItem.listMenuItem({
fieldSelector: [`name=(${node.props.menuItems.join(",")})`],
});

node.props.options = data.items.map((menuItem) => {
return {
value: menuItem.metadata.name,
label: menuItem.status?.displayName,
};
});
if (node.context) {
node.context.attrs.options = data.items.map((menuItem) => {
return {
value: menuItem.metadata.name,
label: menuItem.status?.displayName,
};
});
}
});
}

export const menuItemSelect: FormKitTypeDefinition = {
...select,
props: ["placeholder", "menuItems"],
forceTypeProp: "nativeSelect",
features: [optionsHandler, selects, defaultIcon("select", "select")],
forceTypeProp: "select",
features: [optionsHandler],
};
62 changes: 49 additions & 13 deletions ui/src/formkit/inputs/post-select.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,65 @@
import { postLabels } from "@/constants/labels";
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
import { defaultIcon, select, selects } from "@formkit/inputs";
import { consoleApiClient } from "@halo-dev/api-client";
import { select } from "./select";

function optionsHandler(node: FormKitNode) {
node.on("created", async () => {
const { data } = await consoleApiClient.content.post.listPosts({
labelSelector: [
`${postLabels.DELETED}=false`,
`${postLabels.PUBLISHED}=true`,
],
});
async function search({ page, size, keyword }) {
const { data } = await consoleApiClient.content.post.listPosts({
page,
size,
keyword,
labelSelector: [
`${postLabels.DELETED}=false`,
`${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 {
value: post.post.metadata.name,
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 = {
...select,
props: ["placeholder"],
forceTypeProp: "nativeSelect",
features: [optionsHandler, selects, defaultIcon("select", "select")],
forceTypeProp: "select",
features: [optionsHandler],
};
9 changes: 6 additions & 3 deletions ui/src/formkit/inputs/role-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { rbacAnnotations } from "@/constants/annotations";
import { roleLabels } from "@/constants/labels";
import { i18n } from "@/locales";
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
import { defaultIcon, select, selects } from "@formkit/inputs";
import { coreApiClient } from "@halo-dev/api-client";
import { select } from "./select";

function optionsHandler(node: FormKitNode) {
node.on("created", async () => {
Expand All @@ -13,7 +13,7 @@ function optionsHandler(node: FormKitNode) {
labelSelector: [`!${roleLabels.TEMPLATE}`],
});

node.props.options = [
const options = [
{
label: i18n.global.t(
"core.user.grant_permission_modal.fields.role.placeholder"
Expand All @@ -29,12 +29,15 @@ function optionsHandler(node: FormKitNode) {
};
}),
];
if (node.context) {
node.context.attrs.options = options;
}
});
}

export const roleSelect: FormKitTypeDefinition = {
...select,
props: ["placeholder"],
forceTypeProp: "nativeSelect",
features: [optionsHandler, selects, defaultIcon("select", "select")],
features: [optionsHandler],
};
62 changes: 42 additions & 20 deletions ui/src/formkit/inputs/select/SelectMain.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import SelectContainer from "./SelectContainer.vue";
import { axiosInstance } from "@halo-dev/api-client";
import { get, has, type PropertyPath } from "lodash-es";
import { useDebounceFn } from "@vueuse/core";
import { useFuse } from "@vueuse/integrations/useFuse";
import type { AxiosRequestConfig } from "axios";

export interface SelectProps {
Expand Down Expand Up @@ -184,7 +185,6 @@ const selectProps: SelectProps = shallowReactive({
placeholder: "",
});

const hasSelected = ref(false);
const isRemote = computed(() => !!selectProps.action || !!selectProps.remote);
const hasMoreOptions = computed(
() => options.value && options.value.length < total.value
Expand Down Expand Up @@ -400,7 +400,7 @@ const mapUnresolvedOptions = async (
value: string;
}>
> => {
if (!selectProps.action || !selectProps.remote) {
if (!isRemote.value) {
if (selectProps.allowCreate) {
// TODO: Add mapped values to options
return unmappedSelectValues.map((value) => ({ label: value, value }));
Expand All @@ -413,17 +413,29 @@ const mapUnresolvedOptions = async (
}

// Asynchronous request for options, fetch label and value via API.
let mappedOptions: Array<{
label: string;
value: string;
}> = [];
if (selectProps.action) {
mappedOptions = await fetchRemoteMappedOptions(unmappedSelectValues);
} else if (selectProps.remote) {
const remoteOption = selectProps.remoteOption as SelectRemoteOption;
mappedOptions = await remoteOption.findOptionsByValues(
unmappedSelectValues
let mappedOptions:
| Array<{
label: string;
value: string;
}>
| undefined = undefined;
if (noNeedFetchOptions.value) {
mappedOptions = cacheAllOptions.value?.filter((option) =>
unmappedSelectValues.includes(option.value)
);
} else {
if (selectProps.action) {
mappedOptions = await fetchRemoteMappedOptions(unmappedSelectValues);
} else if (selectProps.remote) {
const remoteOption = selectProps.remoteOption as SelectRemoteOption;
mappedOptions = await remoteOption.findOptionsByValues(
unmappedSelectValues
);
}
}

if (!mappedOptions) {
return unmappedSelectValues.map((value) => ({ label: value, value }));
}
// Get values that are still unresolved.
const unmappedValues = unmappedSelectValues.filter(
Expand Down Expand Up @@ -496,11 +508,12 @@ onMounted(async () => {
}
});

watch(
const stopSelectedWatch = watch(
() => [options.value, props.context.value],
async () => {
if (!hasSelected.value && options.value) {
selectOptions.value = await fetchSelectedOptions();
if (options.value) {
const selectedOption = await fetchSelectedOptions();
selectOptions.value = selectedOption;
}
},
{
Expand All @@ -521,7 +534,7 @@ watch(

const handleUpdate = (value: Array<{ label: string; value: string }>) => {
const values = value.map((item) => item.value);
hasSelected.value = true;
stopSelectedWatch();
selectOptions.value = value;
if (selectProps.multiple) {
props.context.node.input(values);
Expand All @@ -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 (noNeedFetchOptions.value) {
const filterOptions = cacheAllOptions.value?.filter((option) =>
option.label.includes(tempKeyword)
);
const { results } = useFuse<{
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 {
options: filterOptions || [],
page: page.value,
size: size.value,
total: filterOptions?.length || 0,
total: filterOptions.length || 0,
};
}
isLoading.value = true;
Expand Down
Loading
Loading