@jskit-ai/users-web 0.1.59 → 0.1.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.descriptor.mjs +19 -7
- package/package.json +11 -7
- package/src/client/composables/crud/crudSchemaFormHelpers.js +11 -3
- package/src/client/composables/internal/crudListFilterLookupSupport.js +161 -0
- package/src/client/composables/useCrudListFilterLookups.js +141 -0
- package/src/client/composables/useCrudListFilters.js +503 -0
- package/test/exportsContract.test.js +7 -1
- package/test/useCrudAddEdit.test.js +80 -0
- package/test/useCrudListFilterLookups.test.js +114 -0
- package/test/useCrudListFilters.test.js +182 -0
package/package.descriptor.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { HOME_TOOLS_OUTLET } from "./src/shared/toolsOutletContracts.js";
|
|
|
3
3
|
export default Object.freeze({
|
|
4
4
|
packageVersion: 1,
|
|
5
5
|
packageId: "@jskit-ai/users-web",
|
|
6
|
-
version: "0.1.
|
|
6
|
+
version: "0.1.61",
|
|
7
7
|
kind: "runtime",
|
|
8
8
|
description: "Users web module: account/profile UI plus shared users web widgets.",
|
|
9
9
|
dependsOn: [
|
|
@@ -66,6 +66,14 @@ export default Object.freeze({
|
|
|
66
66
|
subpath: "./client/composables/useCommand",
|
|
67
67
|
summary: "Exports command operation composable."
|
|
68
68
|
},
|
|
69
|
+
{
|
|
70
|
+
subpath: "./client/composables/useEndpointResource",
|
|
71
|
+
summary: "Exports low-level endpoint resource composable for custom client requests."
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
subpath: "./client/composables/useCrudListFilterLookups",
|
|
75
|
+
summary: "Exports lookup-backed CRUD list filter helper for remote autocomplete filters."
|
|
76
|
+
},
|
|
69
77
|
{
|
|
70
78
|
subpath: "./client/composables/useView",
|
|
71
79
|
summary: "Exports read/view operation composable."
|
|
@@ -78,6 +86,10 @@ export default Object.freeze({
|
|
|
78
86
|
subpath: "./client/composables/usePaths",
|
|
79
87
|
summary: "Exports surface route path resolver composable."
|
|
80
88
|
},
|
|
89
|
+
{
|
|
90
|
+
subpath: "./client/lib/httpClient",
|
|
91
|
+
summary: "Exports the shared users-web HTTP client with credentials and CSRF behavior."
|
|
92
|
+
},
|
|
81
93
|
{
|
|
82
94
|
subpath: "./client/composables/useAccountSettingsRuntime",
|
|
83
95
|
summary: "Exports account settings runtime composable for app-owned settings UI."
|
|
@@ -142,12 +154,12 @@ export default Object.freeze({
|
|
|
142
154
|
runtime: {
|
|
143
155
|
"@tanstack/vue-query": "5.92.12",
|
|
144
156
|
"@mdi/js": "^7.4.47",
|
|
145
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
146
|
-
"@jskit-ai/realtime": "0.1.
|
|
147
|
-
"@jskit-ai/kernel": "0.1.
|
|
148
|
-
"@jskit-ai/shell-web": "0.1.
|
|
149
|
-
"@jskit-ai/uploads-image-web": "0.1.
|
|
150
|
-
"@jskit-ai/users-core": "0.1.
|
|
157
|
+
"@jskit-ai/http-runtime": "0.1.45",
|
|
158
|
+
"@jskit-ai/realtime": "0.1.45",
|
|
159
|
+
"@jskit-ai/kernel": "0.1.46",
|
|
160
|
+
"@jskit-ai/shell-web": "0.1.45",
|
|
161
|
+
"@jskit-ai/uploads-image-web": "0.1.24",
|
|
162
|
+
"@jskit-ai/users-core": "0.1.56",
|
|
151
163
|
vuetify: "^4.0.0"
|
|
152
164
|
},
|
|
153
165
|
dev: {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/users-web",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.61",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
"./client/composables/useCommand": "./src/client/composables/useCommand.js",
|
|
14
14
|
"./client/composables/useCrudAddEdit": "./src/client/composables/records/useCrudAddEdit.js",
|
|
15
15
|
"./client/composables/crudLookupFieldRuntime": "./src/client/composables/crud/crudLookupFieldRuntime.js",
|
|
16
|
+
"./client/composables/useCrudListFilterLookups": "./src/client/composables/useCrudListFilterLookups.js",
|
|
17
|
+
"./client/composables/useCrudListFilters": "./src/client/composables/useCrudListFilters.js",
|
|
18
|
+
"./client/composables/useEndpointResource": "./src/client/composables/runtime/useEndpointResource.js",
|
|
16
19
|
"./client/composables/useList": "./src/client/composables/records/useList.js",
|
|
17
20
|
"./client/composables/useCrudList": "./src/client/composables/records/useCrudList.js",
|
|
18
21
|
"./client/composables/useCrudListParentTitle": "./src/client/composables/useCrudListParentTitle.js",
|
|
@@ -24,6 +27,7 @@
|
|
|
24
27
|
"./client/composables/useAccountSettingsRuntime": "./src/client/composables/useAccountSettingsRuntime.js",
|
|
25
28
|
"./client/composables/usePaths": "./src/client/composables/usePaths.js",
|
|
26
29
|
"./client/composables/runtime/useUiFeedback": "./src/client/composables/runtime/useUiFeedback.js",
|
|
30
|
+
"./client/lib/httpClient": "./src/client/lib/httpClient.js",
|
|
27
31
|
"./client/lib/bootstrap": "./src/client/lib/bootstrap.js",
|
|
28
32
|
"./client/lib/permissions": "./src/client/lib/permissions.js",
|
|
29
33
|
"./client/support/contractGuards": "./src/client/support/contractGuards.js"
|
|
@@ -31,12 +35,12 @@
|
|
|
31
35
|
"dependencies": {
|
|
32
36
|
"@tanstack/vue-query": "5.92.12",
|
|
33
37
|
"@mdi/js": "^7.4.47",
|
|
34
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
35
|
-
"@jskit-ai/kernel": "0.1.
|
|
36
|
-
"@jskit-ai/realtime": "0.1.
|
|
37
|
-
"@jskit-ai/shell-web": "0.1.
|
|
38
|
-
"@jskit-ai/uploads-image-web": "0.1.
|
|
39
|
-
"@jskit-ai/users-core": "0.1.
|
|
38
|
+
"@jskit-ai/http-runtime": "0.1.45",
|
|
39
|
+
"@jskit-ai/kernel": "0.1.46",
|
|
40
|
+
"@jskit-ai/realtime": "0.1.45",
|
|
41
|
+
"@jskit-ai/shell-web": "0.1.45",
|
|
42
|
+
"@jskit-ai/uploads-image-web": "0.1.24",
|
|
43
|
+
"@jskit-ai/users-core": "0.1.56",
|
|
40
44
|
"vuetify": "^4.0.0"
|
|
41
45
|
}
|
|
42
46
|
}
|
|
@@ -48,6 +48,10 @@ function resolveFormFieldFormat(field = {}) {
|
|
|
48
48
|
return String(field.format || "").trim().toLowerCase();
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
function isNullableFormField(field = {}) {
|
|
52
|
+
return field?.nullable === true;
|
|
53
|
+
}
|
|
54
|
+
|
|
51
55
|
function padDateTimePart(value) {
|
|
52
56
|
return String(value).padStart(2, "0");
|
|
53
57
|
}
|
|
@@ -132,7 +136,7 @@ function resolveFormFieldInitialValue(field = {}) {
|
|
|
132
136
|
|
|
133
137
|
const fieldType = resolveFormFieldType(field);
|
|
134
138
|
if (fieldType === "boolean") {
|
|
135
|
-
return false;
|
|
139
|
+
return isNullableFormField(field) ? null : false;
|
|
136
140
|
}
|
|
137
141
|
|
|
138
142
|
return "";
|
|
@@ -176,7 +180,9 @@ function buildCrudFormPayload(fields = [], model = {}) {
|
|
|
176
180
|
const rawValue = sourceModel[fieldKey];
|
|
177
181
|
|
|
178
182
|
if (fieldType === "boolean") {
|
|
179
|
-
payload[fieldKey] =
|
|
183
|
+
payload[fieldKey] = rawValue == null && isNullableFormField(field)
|
|
184
|
+
? null
|
|
185
|
+
: Boolean(rawValue);
|
|
180
186
|
continue;
|
|
181
187
|
}
|
|
182
188
|
|
|
@@ -258,7 +264,9 @@ function applyCrudPayloadToForm(fields = [], model = {}, payload = {}) {
|
|
|
258
264
|
const rawValue = sourcePayload[fieldKey];
|
|
259
265
|
|
|
260
266
|
if (fieldType === "boolean") {
|
|
261
|
-
targetModel[fieldKey] =
|
|
267
|
+
targetModel[fieldKey] = rawValue == null && isNullableFormField(field)
|
|
268
|
+
? null
|
|
269
|
+
: Boolean(rawValue);
|
|
262
270
|
continue;
|
|
263
271
|
}
|
|
264
272
|
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { normalizeText, normalizeUniqueTextList } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
|
+
import { resolveLookupItemLabel } from "../crud/crudLookupFieldLabelSupport.js";
|
|
3
|
+
import { asPlainObject } from "../support/scopeHelpers.js";
|
|
4
|
+
|
|
5
|
+
function normalizeLookupQueryKeyPrefix(value = []) {
|
|
6
|
+
const source = Array.isArray(value) ? value : [];
|
|
7
|
+
return Object.freeze(
|
|
8
|
+
source
|
|
9
|
+
.map((entry) => normalizeText(entry))
|
|
10
|
+
.filter(Boolean)
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function normalizeLookupLabelResolverMap(value = {}) {
|
|
15
|
+
const source = value && typeof value === "object" && !Array.isArray(value)
|
|
16
|
+
? value
|
|
17
|
+
: {};
|
|
18
|
+
const normalized = {};
|
|
19
|
+
|
|
20
|
+
for (const [key, entry] of Object.entries(source)) {
|
|
21
|
+
const normalizedKey = normalizeText(key);
|
|
22
|
+
if (!normalizedKey || typeof entry !== "function") {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
normalized[normalizedKey] = entry;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return Object.freeze(normalized);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function normalizeLookupRequestQueryParamsMap(value = {}) {
|
|
33
|
+
const source = value && typeof value === "object" && !Array.isArray(value)
|
|
34
|
+
? value
|
|
35
|
+
: {};
|
|
36
|
+
const normalized = {};
|
|
37
|
+
|
|
38
|
+
for (const [key, entry] of Object.entries(source)) {
|
|
39
|
+
const normalizedKey = normalizeText(key);
|
|
40
|
+
if (!normalizedKey) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (entry == null) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (typeof entry === "function") {
|
|
47
|
+
normalized[normalizedKey] = entry;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (typeof entry !== "object" || Array.isArray(entry)) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
normalized[normalizedKey] = asPlainObject(entry);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return Object.freeze(normalized);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function resolveLookupSelectedValues(filter = {}, rawValue = undefined) {
|
|
61
|
+
if (filter?.type === "recordIdMany") {
|
|
62
|
+
return normalizeUniqueTextList(rawValue, {
|
|
63
|
+
acceptSingle: true
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const normalizedValue = normalizeText(rawValue);
|
|
68
|
+
return normalizedValue ? Object.freeze([normalizedValue]) : Object.freeze([]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function createLookupOptionFromItem(item = {}, filter = {}, labelResolver = null) {
|
|
72
|
+
const sourceRecord = asPlainObject(item);
|
|
73
|
+
const valueKey = normalizeText(filter?.lookup?.valueKey) || "id";
|
|
74
|
+
const labelKey = normalizeText(filter?.lookup?.labelKey);
|
|
75
|
+
const value = normalizeText(sourceRecord[valueKey]);
|
|
76
|
+
if (!value) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const customLabel = typeof labelResolver === "function"
|
|
81
|
+
? normalizeText(labelResolver(sourceRecord, filter))
|
|
82
|
+
: "";
|
|
83
|
+
const fallbackLabel = resolveLookupItemLabel(sourceRecord, labelKey) || value;
|
|
84
|
+
|
|
85
|
+
return Object.freeze({
|
|
86
|
+
value,
|
|
87
|
+
label: customLabel || String(fallbackLabel || value),
|
|
88
|
+
record: sourceRecord
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function createLookupOptionsFromItems(items = [], filter = {}, labelResolver = null) {
|
|
93
|
+
const optionMap = new Map();
|
|
94
|
+
for (const item of Array.isArray(items) ? items : []) {
|
|
95
|
+
const option = createLookupOptionFromItem(item, filter, labelResolver);
|
|
96
|
+
if (!option || optionMap.has(option.value)) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
optionMap.set(option.value, option);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return Object.freeze([...optionMap.values()]);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function mergeSelectedLookupOptions(options = [], selectedValues = [], cachedOptions = new Map()) {
|
|
107
|
+
const optionMap = new Map(
|
|
108
|
+
(Array.isArray(options) ? options : []).map((option) => [normalizeText(option?.value), option])
|
|
109
|
+
);
|
|
110
|
+
const normalizedCache = cachedOptions instanceof Map ? cachedOptions : new Map();
|
|
111
|
+
|
|
112
|
+
for (const value of Array.isArray(selectedValues) ? selectedValues : []) {
|
|
113
|
+
const normalizedValue = normalizeText(value);
|
|
114
|
+
if (!normalizedValue || optionMap.has(normalizedValue)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const cachedOption = normalizedCache.get(normalizedValue);
|
|
119
|
+
if (cachedOption) {
|
|
120
|
+
optionMap.set(normalizedValue, cachedOption);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return Object.freeze(
|
|
125
|
+
[...optionMap.values()].sort((left, right) => {
|
|
126
|
+
return String(left?.label || "").localeCompare(String(right?.label || ""));
|
|
127
|
+
})
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function resolveLookupOptionLabel(options = [], cachedOptions = new Map(), value = "", fallback = "Option") {
|
|
132
|
+
const normalizedValue = normalizeText(value);
|
|
133
|
+
if (!normalizedValue) {
|
|
134
|
+
return normalizeText(fallback) || "Option";
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const matchingOption = (Array.isArray(options) ? options : [])
|
|
138
|
+
.find((option) => normalizeText(option?.value) === normalizedValue);
|
|
139
|
+
if (matchingOption?.label) {
|
|
140
|
+
return String(matchingOption.label);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const normalizedCache = cachedOptions instanceof Map ? cachedOptions : new Map();
|
|
144
|
+
const cachedOption = normalizedCache.get(normalizedValue);
|
|
145
|
+
if (cachedOption?.label) {
|
|
146
|
+
return String(cachedOption.label);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const normalizedFallback = normalizeText(fallback) || "Option";
|
|
150
|
+
return `${normalizedFallback} ${normalizedValue}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export {
|
|
154
|
+
normalizeLookupQueryKeyPrefix,
|
|
155
|
+
normalizeLookupLabelResolverMap,
|
|
156
|
+
normalizeLookupRequestQueryParamsMap,
|
|
157
|
+
resolveLookupSelectedValues,
|
|
158
|
+
createLookupOptionsFromItems,
|
|
159
|
+
mergeSelectedLookupOptions,
|
|
160
|
+
resolveLookupOptionLabel
|
|
161
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { computed, proxyRefs, ref, watch } from "vue";
|
|
2
|
+
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
|
+
import {
|
|
4
|
+
defineCrudListFilters,
|
|
5
|
+
CRUD_LIST_FILTER_TYPE_RECORD_ID,
|
|
6
|
+
CRUD_LIST_FILTER_TYPE_RECORD_ID_MANY
|
|
7
|
+
} from "@jskit-ai/kernel/shared/support/crudListFilters";
|
|
8
|
+
import { useList } from "./records/useList.js";
|
|
9
|
+
import {
|
|
10
|
+
normalizeLookupQueryKeyPrefix,
|
|
11
|
+
normalizeLookupLabelResolverMap,
|
|
12
|
+
normalizeLookupRequestQueryParamsMap,
|
|
13
|
+
resolveLookupSelectedValues,
|
|
14
|
+
createLookupOptionsFromItems,
|
|
15
|
+
mergeSelectedLookupOptions,
|
|
16
|
+
resolveLookupOptionLabel
|
|
17
|
+
} from "./internal/crudListFilterLookupSupport.js";
|
|
18
|
+
|
|
19
|
+
function useCrudListFilterLookups(
|
|
20
|
+
definitions = {},
|
|
21
|
+
{
|
|
22
|
+
values = {},
|
|
23
|
+
adapter = null,
|
|
24
|
+
recordIdParam = "recordId",
|
|
25
|
+
queryKeyPrefix = [],
|
|
26
|
+
placementSourcePrefix = "",
|
|
27
|
+
requestQueryParams = {},
|
|
28
|
+
labelResolvers = {}
|
|
29
|
+
} = {}
|
|
30
|
+
) {
|
|
31
|
+
const filters = defineCrudListFilters(definitions);
|
|
32
|
+
const filterEntries = Object.values(filters);
|
|
33
|
+
const normalizedQueryKeyPrefix = normalizeLookupQueryKeyPrefix(queryKeyPrefix);
|
|
34
|
+
const normalizedPlacementSourcePrefix = normalizeText(placementSourcePrefix);
|
|
35
|
+
const normalizedLabelResolvers = normalizeLookupLabelResolverMap(labelResolvers);
|
|
36
|
+
const normalizedRequestQueryParams = normalizeLookupRequestQueryParamsMap(requestQueryParams);
|
|
37
|
+
const lookups = {};
|
|
38
|
+
|
|
39
|
+
for (const filter of filterEntries) {
|
|
40
|
+
if (
|
|
41
|
+
filter.type !== CRUD_LIST_FILTER_TYPE_RECORD_ID &&
|
|
42
|
+
filter.type !== CRUD_LIST_FILTER_TYPE_RECORD_ID_MANY
|
|
43
|
+
) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (!filter.lookup?.apiSuffix) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const runtime = useList({
|
|
51
|
+
adapter: adapter || undefined,
|
|
52
|
+
apiSuffix: filter.lookup.apiSuffix,
|
|
53
|
+
queryKeyFactory: (surfaceId = "", scopeParamValue = "") => [
|
|
54
|
+
...normalizedQueryKeyPrefix,
|
|
55
|
+
filter.key,
|
|
56
|
+
String(surfaceId || ""),
|
|
57
|
+
String(scopeParamValue || "")
|
|
58
|
+
],
|
|
59
|
+
search: {
|
|
60
|
+
enabled: true,
|
|
61
|
+
mode: "query"
|
|
62
|
+
},
|
|
63
|
+
...(Object.hasOwn(normalizedRequestQueryParams, filter.key)
|
|
64
|
+
? { requestQueryParams: normalizedRequestQueryParams[filter.key] }
|
|
65
|
+
: {}),
|
|
66
|
+
placementSource: normalizedPlacementSourcePrefix
|
|
67
|
+
? `${normalizedPlacementSourcePrefix}.${filter.key}`
|
|
68
|
+
: `crud.list-filter.lookup.${filter.key}`,
|
|
69
|
+
fallbackLoadError: `Unable to load filter options (${filter.lookup.apiSuffix}).`,
|
|
70
|
+
recordIdParam,
|
|
71
|
+
recordIdSelector: (item = {}) => item[filter.lookup.valueKey || "id"],
|
|
72
|
+
viewUrlTemplate: "",
|
|
73
|
+
editUrlTemplate: ""
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const cachedOptions = ref(new Map());
|
|
77
|
+
const selectedValues = computed(() => resolveLookupSelectedValues(filter, values?.[filter.key]));
|
|
78
|
+
const currentOptions = computed(() => createLookupOptionsFromItems(
|
|
79
|
+
Array.isArray(runtime.items) ? runtime.items : [],
|
|
80
|
+
filter,
|
|
81
|
+
normalizedLabelResolvers[filter.key]
|
|
82
|
+
));
|
|
83
|
+
const options = computed(() => {
|
|
84
|
+
return mergeSelectedLookupOptions(
|
|
85
|
+
currentOptions.value,
|
|
86
|
+
selectedValues.value,
|
|
87
|
+
cachedOptions.value
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
watch(currentOptions, (nextOptions) => {
|
|
92
|
+
const nextCache = new Map(cachedOptions.value);
|
|
93
|
+
for (const option of nextOptions) {
|
|
94
|
+
nextCache.set(option.value, option);
|
|
95
|
+
}
|
|
96
|
+
cachedOptions.value = nextCache;
|
|
97
|
+
}, { immediate: true });
|
|
98
|
+
|
|
99
|
+
lookups[filter.key] = proxyRefs({
|
|
100
|
+
filter,
|
|
101
|
+
options,
|
|
102
|
+
searchQuery: computed(() => String(runtime.searchQuery || "")),
|
|
103
|
+
isLoading: computed(() => {
|
|
104
|
+
return Boolean(runtime.isInitialLoading || runtime.isFetching || runtime.isRefetching || runtime.isSearchDebouncing);
|
|
105
|
+
}),
|
|
106
|
+
resolveLabel(value, fallback = filter.label || "Option") {
|
|
107
|
+
return resolveLookupOptionLabel(options.value, cachedOptions.value, value, fallback);
|
|
108
|
+
},
|
|
109
|
+
setSearch(value = "") {
|
|
110
|
+
runtime.searchQuery = normalizeText(value);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function resolveLookup(filterKey = "") {
|
|
116
|
+
return lookups[normalizeText(filterKey)] || null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return Object.freeze({
|
|
120
|
+
lookups: Object.freeze(lookups),
|
|
121
|
+
resolveLookup,
|
|
122
|
+
resolveLookupItems(filterKey = "") {
|
|
123
|
+
return resolveLookup(filterKey)?.options || [];
|
|
124
|
+
},
|
|
125
|
+
resolveLookupLoading(filterKey = "") {
|
|
126
|
+
return Boolean(resolveLookup(filterKey)?.isLoading);
|
|
127
|
+
},
|
|
128
|
+
resolveLookupSearch(filterKey = "") {
|
|
129
|
+
return String(resolveLookup(filterKey)?.searchQuery || "");
|
|
130
|
+
},
|
|
131
|
+
setLookupSearch(filterKey = "", value = "") {
|
|
132
|
+
resolveLookup(filterKey)?.setSearch(value);
|
|
133
|
+
},
|
|
134
|
+
resolveLookupLabel(filterKey = "", value = "", fallback = "Option") {
|
|
135
|
+
return resolveLookup(filterKey)?.resolveLabel(value, fallback)
|
|
136
|
+
|| resolveLookupOptionLabel([], new Map(), value, fallback);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export { useCrudListFilterLookups };
|