@xen-orchestra/web-core 0.26.1 → 0.27.0
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/lib/components/ui/collapsible-list/UiCollapsibleList.vue +73 -0
- package/lib/components/ui/{quoteCode/UiQuoteCode.vue → log-entry-viewer/UiLogEntryViewer.vue} +54 -21
- package/lib/components/ui/table-pagination/UiTablePagination.vue +19 -79
- package/lib/composables/default-tab.composable.md +42 -0
- package/lib/composables/default-tab.composable.ts +26 -0
- package/lib/composables/link-component.composable.ts +3 -2
- package/lib/locales/en.json +3 -0
- package/lib/locales/fr.json +3 -0
- package/lib/packages/form-select/types.ts +3 -0
- package/lib/packages/form-select/use-form-option-controller.ts +5 -6
- package/lib/packages/form-select/use-form-select-controller.ts +1 -0
- package/lib/packages/form-select/use-form-select.ts +153 -109
- package/lib/packages/remote-resource/README.md +115 -0
- package/lib/packages/remote-resource/define-remote-resource.ts +294 -0
- package/lib/packages/remote-resource/types.ts +28 -0
- package/package.json +2 -2
|
@@ -7,34 +7,33 @@ import {
|
|
|
7
7
|
} from '@core/packages/collection'
|
|
8
8
|
import type { EmptyObject, MaybeArray } from '@core/types/utility.type.ts'
|
|
9
9
|
import { toArray } from '@core/utils/to-array.utils.ts'
|
|
10
|
-
import type
|
|
11
|
-
ExtractValue,
|
|
12
|
-
FormOptionCollectionItemProperties,
|
|
13
|
-
FormSelect,
|
|
14
|
-
FormSelectId,
|
|
15
|
-
GetOptionLabel,
|
|
16
|
-
GetOptionValue,
|
|
17
|
-
UseFormSelectReturn,
|
|
18
|
-
} from './types.ts'
|
|
19
|
-
import { computed, type ComputedRef, type MaybeRefOrGetter, provide, ref, type Ref, toValue, watch } from 'vue'
|
|
10
|
+
import { computed, type ComputedRef, type MaybeRefOrGetter, provide, ref, type Ref, toRaw, toValue, watch } from 'vue'
|
|
20
11
|
import { guessLabel } from './guess-label.ts'
|
|
21
12
|
import { guessValue } from './guess-value.ts'
|
|
22
13
|
import { normalizeSearchTerm } from './normalize-search-term.ts'
|
|
14
|
+
import {
|
|
15
|
+
EMPTY_OPTION,
|
|
16
|
+
type ExtractValue,
|
|
17
|
+
type FormOptionCollectionItemProperties,
|
|
18
|
+
type FormSelect,
|
|
19
|
+
type FormSelectId,
|
|
20
|
+
type GetOptionLabel,
|
|
21
|
+
type GetOptionValue,
|
|
22
|
+
type UseFormSelectReturn,
|
|
23
|
+
} from './types.ts'
|
|
23
24
|
|
|
24
25
|
// Overload #1: Source is CollectionItemId
|
|
25
26
|
|
|
26
27
|
export function useFormSelect<
|
|
27
|
-
|
|
28
|
-
TAllowEmpty extends boolean = false,
|
|
28
|
+
TSource extends CollectionItemId,
|
|
29
29
|
TCustomProperties extends CollectionItemProperties = EmptyObject,
|
|
30
|
-
TGetValue extends GetOptionValue
|
|
30
|
+
TGetValue extends GetOptionValue<TSource, TCustomProperties> = undefined,
|
|
31
31
|
TMultiple extends boolean = false,
|
|
32
|
-
|
|
33
|
-
$TValue = ExtractValue
|
|
32
|
+
TEmptyValue = never,
|
|
33
|
+
$TValue = ExtractValue<TSource, TGetValue>,
|
|
34
34
|
>(
|
|
35
|
-
sources: MaybeRefOrGetter<
|
|
35
|
+
sources: MaybeRefOrGetter<TSource[]>,
|
|
36
36
|
config?: {
|
|
37
|
-
allowEmpty?: MaybeRefOrGetter<TAllowEmpty>
|
|
38
37
|
multiple?: MaybeRefOrGetter<TMultiple>
|
|
39
38
|
model?: Ref<unknown>
|
|
40
39
|
disabled?: MaybeRefOrGetter<boolean>
|
|
@@ -44,32 +43,36 @@ export function useFormSelect<
|
|
|
44
43
|
loading?: MaybeRefOrGetter<boolean>
|
|
45
44
|
required?: MaybeRefOrGetter<boolean>
|
|
46
45
|
searchable?: MaybeRefOrGetter<boolean>
|
|
46
|
+
emptyOption?: MaybeRefOrGetter<{
|
|
47
|
+
value: TEmptyValue
|
|
48
|
+
properties?: TCustomProperties
|
|
49
|
+
label: string
|
|
50
|
+
selectedLabel?: string
|
|
51
|
+
}>
|
|
47
52
|
option?: {
|
|
48
|
-
id?: GetItemId<
|
|
49
|
-
value?: TGetValue | ((source:
|
|
50
|
-
properties?: (source:
|
|
51
|
-
label?: GetOptionLabel
|
|
52
|
-
selectedLabel?: (source:
|
|
53
|
-
disabled?: (source:
|
|
54
|
-
searchableTerm?: (source:
|
|
53
|
+
id?: GetItemId<TSource>
|
|
54
|
+
value?: TGetValue | ((source: TSource, properties: TCustomProperties) => $TValue)
|
|
55
|
+
properties?: (source: TSource) => TCustomProperties
|
|
56
|
+
label?: GetOptionLabel<TSource, TCustomProperties>
|
|
57
|
+
selectedLabel?: (source: TSource, properties: TCustomProperties) => string
|
|
58
|
+
disabled?: (source: TSource, properties: TCustomProperties) => boolean
|
|
59
|
+
searchableTerm?: (source: TSource, properties: TCustomProperties) => MaybeArray<string>
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
|
-
): UseFormSelectReturn<TCustomProperties,
|
|
62
|
+
): UseFormSelectReturn<TCustomProperties, TSource, $TValue | TEmptyValue, TMultiple>
|
|
58
63
|
|
|
59
64
|
// Overload #2: Source is an object with id and label
|
|
60
65
|
|
|
61
66
|
export function useFormSelect<
|
|
62
|
-
|
|
63
|
-
TAllowEmpty extends boolean = false,
|
|
67
|
+
TSource extends { id: CollectionItemId; label: string },
|
|
64
68
|
TCustomProperties extends CollectionItemProperties = EmptyObject,
|
|
65
|
-
TGetValue extends GetOptionValue
|
|
69
|
+
TGetValue extends GetOptionValue<TSource, TCustomProperties> = undefined,
|
|
66
70
|
TMultiple extends boolean = false,
|
|
67
|
-
|
|
68
|
-
$TValue = ExtractValue
|
|
71
|
+
TEmptyValue = never,
|
|
72
|
+
$TValue = ExtractValue<TSource, TGetValue>,
|
|
69
73
|
>(
|
|
70
|
-
sources: MaybeRefOrGetter<
|
|
74
|
+
sources: MaybeRefOrGetter<TSource[]>,
|
|
71
75
|
config?: {
|
|
72
|
-
allowEmpty?: MaybeRefOrGetter<TAllowEmpty>
|
|
73
76
|
multiple?: MaybeRefOrGetter<TMultiple>
|
|
74
77
|
model?: Ref<unknown>
|
|
75
78
|
disabled?: MaybeRefOrGetter<boolean>
|
|
@@ -79,32 +82,36 @@ export function useFormSelect<
|
|
|
79
82
|
loading?: MaybeRefOrGetter<boolean>
|
|
80
83
|
required?: MaybeRefOrGetter<boolean>
|
|
81
84
|
searchable?: MaybeRefOrGetter<boolean>
|
|
85
|
+
emptyOption?: MaybeRefOrGetter<{
|
|
86
|
+
value: TEmptyValue
|
|
87
|
+
properties?: TCustomProperties
|
|
88
|
+
label: string
|
|
89
|
+
selectedLabel?: string
|
|
90
|
+
}>
|
|
82
91
|
option?: {
|
|
83
|
-
id?: GetItemId<
|
|
84
|
-
value?: TGetValue | ((source:
|
|
85
|
-
properties?: (source:
|
|
86
|
-
label?: GetOptionLabel
|
|
87
|
-
selectedLabel?: (source:
|
|
88
|
-
disabled?: (source:
|
|
89
|
-
searchableTerm?: (source:
|
|
92
|
+
id?: GetItemId<TSource>
|
|
93
|
+
value?: TGetValue | ((source: TSource, properties: TCustomProperties) => $TValue)
|
|
94
|
+
properties?: (source: TSource) => TCustomProperties
|
|
95
|
+
label?: GetOptionLabel<TSource, TCustomProperties>
|
|
96
|
+
selectedLabel?: (source: TSource, properties: TCustomProperties) => string
|
|
97
|
+
disabled?: (source: TSource, properties: TCustomProperties) => boolean
|
|
98
|
+
searchableTerm?: (source: TSource, properties: TCustomProperties) => MaybeArray<string>
|
|
90
99
|
}
|
|
91
100
|
}
|
|
92
|
-
): UseFormSelectReturn<TCustomProperties,
|
|
101
|
+
): UseFormSelectReturn<TCustomProperties, TSource, $TValue | TEmptyValue, TMultiple>
|
|
93
102
|
|
|
94
103
|
// Overload #3: Source is an object with id only
|
|
95
104
|
|
|
96
105
|
export function useFormSelect<
|
|
97
|
-
|
|
98
|
-
TAllowEmpty extends boolean = false,
|
|
106
|
+
TSource extends { id: CollectionItemId },
|
|
99
107
|
TCustomProperties extends CollectionItemProperties = EmptyObject,
|
|
100
|
-
TGetValue extends GetOptionValue
|
|
108
|
+
TGetValue extends GetOptionValue<TSource, TCustomProperties> = undefined,
|
|
101
109
|
TMultiple extends boolean = false,
|
|
102
|
-
|
|
103
|
-
$TValue = ExtractValue
|
|
110
|
+
TEmptyValue = never,
|
|
111
|
+
$TValue = ExtractValue<TSource, TGetValue>,
|
|
104
112
|
>(
|
|
105
|
-
sources: MaybeRefOrGetter<
|
|
113
|
+
sources: MaybeRefOrGetter<TSource[]>,
|
|
106
114
|
config: {
|
|
107
|
-
allowEmpty?: MaybeRefOrGetter<TAllowEmpty>
|
|
108
115
|
multiple?: MaybeRefOrGetter<TMultiple>
|
|
109
116
|
model?: Ref<unknown>
|
|
110
117
|
disabled?: MaybeRefOrGetter<boolean>
|
|
@@ -114,32 +121,36 @@ export function useFormSelect<
|
|
|
114
121
|
loading?: MaybeRefOrGetter<boolean>
|
|
115
122
|
required?: MaybeRefOrGetter<boolean>
|
|
116
123
|
searchable?: MaybeRefOrGetter<boolean>
|
|
124
|
+
emptyOption?: MaybeRefOrGetter<{
|
|
125
|
+
value: TEmptyValue
|
|
126
|
+
properties?: TCustomProperties
|
|
127
|
+
label: string
|
|
128
|
+
selectedLabel?: string
|
|
129
|
+
}>
|
|
117
130
|
option: {
|
|
118
|
-
id?: GetItemId<
|
|
119
|
-
value?: TGetValue | ((source:
|
|
120
|
-
properties?: (source:
|
|
121
|
-
label: GetOptionLabel
|
|
122
|
-
selectedLabel?: (source:
|
|
123
|
-
disabled?: (source:
|
|
124
|
-
searchableTerm?: (source:
|
|
131
|
+
id?: GetItemId<TSource>
|
|
132
|
+
value?: TGetValue | ((source: TSource, properties: TCustomProperties) => $TValue)
|
|
133
|
+
properties?: (source: TSource) => TCustomProperties
|
|
134
|
+
label: GetOptionLabel<TSource, TCustomProperties>
|
|
135
|
+
selectedLabel?: (source: TSource, properties: TCustomProperties) => string
|
|
136
|
+
disabled?: (source: TSource, properties: TCustomProperties) => boolean
|
|
137
|
+
searchableTerm?: (source: TSource, properties: TCustomProperties) => MaybeArray<string>
|
|
125
138
|
}
|
|
126
139
|
}
|
|
127
|
-
): UseFormSelectReturn<TCustomProperties,
|
|
140
|
+
): UseFormSelectReturn<TCustomProperties, TSource, $TValue | TEmptyValue, TMultiple>
|
|
128
141
|
|
|
129
142
|
// Overload #4: Source is an object with label only
|
|
130
143
|
|
|
131
144
|
export function useFormSelect<
|
|
132
|
-
|
|
133
|
-
TAllowEmpty extends boolean = false,
|
|
145
|
+
TSource extends { label: string },
|
|
134
146
|
TCustomProperties extends CollectionItemProperties = EmptyObject,
|
|
135
|
-
TGetValue extends GetOptionValue
|
|
147
|
+
TGetValue extends GetOptionValue<TSource, TCustomProperties> = undefined,
|
|
136
148
|
TMultiple extends boolean = false,
|
|
137
|
-
|
|
138
|
-
$TValue = ExtractValue
|
|
149
|
+
TEmptyValue = never,
|
|
150
|
+
$TValue = ExtractValue<TSource, TGetValue>,
|
|
139
151
|
>(
|
|
140
|
-
sources: MaybeRefOrGetter<
|
|
152
|
+
sources: MaybeRefOrGetter<TSource[]>,
|
|
141
153
|
config: {
|
|
142
|
-
allowEmpty?: MaybeRefOrGetter<TAllowEmpty>
|
|
143
154
|
multiple?: MaybeRefOrGetter<TMultiple>
|
|
144
155
|
model?: Ref<unknown>
|
|
145
156
|
disabled?: MaybeRefOrGetter<boolean>
|
|
@@ -149,32 +160,36 @@ export function useFormSelect<
|
|
|
149
160
|
loading?: MaybeRefOrGetter<boolean>
|
|
150
161
|
required?: MaybeRefOrGetter<boolean>
|
|
151
162
|
searchable?: MaybeRefOrGetter<boolean>
|
|
163
|
+
emptyOption?: MaybeRefOrGetter<{
|
|
164
|
+
value: TEmptyValue
|
|
165
|
+
properties?: TCustomProperties
|
|
166
|
+
label: string
|
|
167
|
+
selectedLabel?: string
|
|
168
|
+
}>
|
|
152
169
|
option: {
|
|
153
|
-
id: GetItemId<
|
|
154
|
-
value?: TGetValue | ((source:
|
|
155
|
-
properties?: (source:
|
|
156
|
-
label?: GetOptionLabel
|
|
157
|
-
selectedLabel?: (source:
|
|
158
|
-
disabled?: (source:
|
|
159
|
-
searchableTerm?: (source:
|
|
170
|
+
id: GetItemId<TSource>
|
|
171
|
+
value?: TGetValue | ((source: TSource, properties: TCustomProperties) => $TValue)
|
|
172
|
+
properties?: (source: TSource) => TCustomProperties
|
|
173
|
+
label?: GetOptionLabel<TSource, TCustomProperties>
|
|
174
|
+
selectedLabel?: (source: TSource, properties: TCustomProperties) => string
|
|
175
|
+
disabled?: (source: TSource, properties: TCustomProperties) => boolean
|
|
176
|
+
searchableTerm?: (source: TSource, properties: TCustomProperties) => MaybeArray<string>
|
|
160
177
|
}
|
|
161
178
|
}
|
|
162
|
-
): UseFormSelectReturn<TCustomProperties,
|
|
179
|
+
): UseFormSelectReturn<TCustomProperties, TSource, $TValue | TEmptyValue, TMultiple>
|
|
163
180
|
|
|
164
181
|
// Overload #5: Any other case
|
|
165
182
|
|
|
166
183
|
export function useFormSelect<
|
|
167
|
-
|
|
168
|
-
TAllowEmpty extends boolean = false,
|
|
184
|
+
TSource,
|
|
169
185
|
TCustomProperties extends CollectionItemProperties = EmptyObject,
|
|
170
|
-
TGetValue extends GetOptionValue
|
|
186
|
+
TGetValue extends GetOptionValue<TSource, TCustomProperties> = undefined,
|
|
171
187
|
TMultiple extends boolean = false,
|
|
172
|
-
|
|
173
|
-
$TValue = ExtractValue
|
|
188
|
+
TEmptyValue = never,
|
|
189
|
+
$TValue = ExtractValue<TSource, TGetValue>,
|
|
174
190
|
>(
|
|
175
|
-
sources: MaybeRefOrGetter<
|
|
191
|
+
sources: MaybeRefOrGetter<TSource[]>,
|
|
176
192
|
config: {
|
|
177
|
-
allowEmpty?: MaybeRefOrGetter<TAllowEmpty>
|
|
178
193
|
multiple?: MaybeRefOrGetter<TMultiple>
|
|
179
194
|
model?: Ref<unknown>
|
|
180
195
|
disabled?: MaybeRefOrGetter<boolean>
|
|
@@ -184,32 +199,36 @@ export function useFormSelect<
|
|
|
184
199
|
loading?: MaybeRefOrGetter<boolean>
|
|
185
200
|
required?: MaybeRefOrGetter<boolean>
|
|
186
201
|
searchable?: MaybeRefOrGetter<boolean>
|
|
202
|
+
emptyOption?: MaybeRefOrGetter<{
|
|
203
|
+
value: TEmptyValue
|
|
204
|
+
properties?: TCustomProperties
|
|
205
|
+
label: string
|
|
206
|
+
selectedLabel?: string
|
|
207
|
+
}>
|
|
187
208
|
option: {
|
|
188
|
-
id: GetItemId<
|
|
189
|
-
value?: TGetValue | ((source:
|
|
190
|
-
properties?: (source:
|
|
191
|
-
label: GetOptionLabel
|
|
192
|
-
selectedLabel?: (source:
|
|
193
|
-
disabled?: (source:
|
|
194
|
-
searchableTerm?: (source:
|
|
209
|
+
id: GetItemId<TSource>
|
|
210
|
+
value?: TGetValue | ((source: TSource, properties: TCustomProperties) => $TValue)
|
|
211
|
+
properties?: (source: TSource) => TCustomProperties
|
|
212
|
+
label: GetOptionLabel<TSource, TCustomProperties>
|
|
213
|
+
selectedLabel?: (source: TSource, properties: TCustomProperties) => string
|
|
214
|
+
disabled?: (source: TSource, properties: TCustomProperties) => boolean
|
|
215
|
+
searchableTerm?: (source: TSource, properties: TCustomProperties) => MaybeArray<string>
|
|
195
216
|
}
|
|
196
217
|
}
|
|
197
|
-
): UseFormSelectReturn<TCustomProperties,
|
|
218
|
+
): UseFormSelectReturn<TCustomProperties, TSource, $TValue | TEmptyValue, TMultiple>
|
|
198
219
|
|
|
199
220
|
// Implementation
|
|
200
221
|
|
|
201
222
|
export function useFormSelect<
|
|
202
|
-
|
|
203
|
-
TAllowEmpty extends boolean = false,
|
|
223
|
+
TSource,
|
|
204
224
|
TCustomProperties extends CollectionItemProperties = EmptyObject,
|
|
205
|
-
TGetValue extends GetOptionValue
|
|
225
|
+
TGetValue extends GetOptionValue<TSource, TCustomProperties> = undefined,
|
|
206
226
|
TMultiple extends boolean = false,
|
|
207
|
-
|
|
208
|
-
$TValue = ExtractValue
|
|
227
|
+
TEmptyValue = never,
|
|
228
|
+
$TValue = ExtractValue<TSource, TGetValue>,
|
|
209
229
|
>(
|
|
210
|
-
baseSources: MaybeRefOrGetter<
|
|
230
|
+
baseSources: MaybeRefOrGetter<TSource[]>,
|
|
211
231
|
config?: {
|
|
212
|
-
allowEmpty?: MaybeRefOrGetter<boolean>
|
|
213
232
|
multiple?: MaybeRefOrGetter<boolean>
|
|
214
233
|
model?: Ref<unknown>
|
|
215
234
|
disabled?: MaybeRefOrGetter<boolean>
|
|
@@ -219,17 +238,23 @@ export function useFormSelect<
|
|
|
219
238
|
loading?: MaybeRefOrGetter<boolean>
|
|
220
239
|
required?: MaybeRefOrGetter<boolean>
|
|
221
240
|
searchable?: MaybeRefOrGetter<boolean>
|
|
241
|
+
emptyOption?: MaybeRefOrGetter<{
|
|
242
|
+
value: TEmptyValue
|
|
243
|
+
properties?: TCustomProperties
|
|
244
|
+
label: string
|
|
245
|
+
selectedLabel?: string
|
|
246
|
+
}>
|
|
222
247
|
option?: {
|
|
223
|
-
id?: GetItemId<
|
|
224
|
-
value?: GetOptionValue
|
|
225
|
-
properties?: (source:
|
|
226
|
-
label?: GetOptionLabel
|
|
227
|
-
selectedLabel?: (source:
|
|
228
|
-
disabled?: (source:
|
|
229
|
-
searchableTerm?: (source:
|
|
248
|
+
id?: GetItemId<TSource>
|
|
249
|
+
value?: GetOptionValue<TSource, TCustomProperties>
|
|
250
|
+
properties?: (source: TSource) => TCustomProperties
|
|
251
|
+
label?: GetOptionLabel<TSource, TCustomProperties>
|
|
252
|
+
selectedLabel?: (source: TSource, properties: TCustomProperties) => string
|
|
253
|
+
disabled?: (source: TSource, properties: TCustomProperties) => boolean
|
|
254
|
+
searchableTerm?: (source: TSource, properties: TCustomProperties) => MaybeArray<string>
|
|
230
255
|
}
|
|
231
256
|
}
|
|
232
|
-
): UseFormSelectReturn<TCustomProperties,
|
|
257
|
+
): UseFormSelectReturn<TCustomProperties, TSource, $TValue | TEmptyValue, TMultiple> {
|
|
233
258
|
const searchTerm = ref('')
|
|
234
259
|
|
|
235
260
|
const normalizedSearchTerm = computed(() => normalizeSearchTerm(searchTerm))
|
|
@@ -249,8 +274,8 @@ export function useFormSelect<
|
|
|
249
274
|
const isSearchable = computed(() => toValue(config?.searchable) ?? false)
|
|
250
275
|
|
|
251
276
|
const sources = computed(() =>
|
|
252
|
-
config?.
|
|
253
|
-
) as ComputedRef
|
|
277
|
+
config?.emptyOption !== undefined ? [EMPTY_OPTION, ...toValue(baseSources)] : toValue(baseSources)
|
|
278
|
+
) as ComputedRef<(TSource | typeof EMPTY_OPTION)[]>
|
|
254
279
|
|
|
255
280
|
const {
|
|
256
281
|
items: allOptions,
|
|
@@ -258,12 +283,25 @@ export function useFormSelect<
|
|
|
258
283
|
useFlag,
|
|
259
284
|
} = useCollection(sources, {
|
|
260
285
|
itemId: source =>
|
|
261
|
-
source ===
|
|
286
|
+
source === EMPTY_OPTION ? '__EMPTY_OPTION__' : guessItemId(source as TSource, config?.option?.id),
|
|
262
287
|
flags: {
|
|
263
288
|
active: { multiple: false },
|
|
264
289
|
selected: { multiple: isMultiple },
|
|
265
290
|
},
|
|
266
291
|
properties: (source): FormOptionCollectionItemProperties<TCustomProperties, $TValue> => {
|
|
292
|
+
if (source === EMPTY_OPTION) {
|
|
293
|
+
const emptyOption = toValue(config?.emptyOption)
|
|
294
|
+
|
|
295
|
+
return {
|
|
296
|
+
value: computed(() => emptyOption?.value as $TValue),
|
|
297
|
+
label: computed(() => emptyOption?.label ?? ''),
|
|
298
|
+
selectedLabel: computed(() => emptyOption?.selectedLabel),
|
|
299
|
+
matching: computed(() => normalizedSearchTerm.value === ''),
|
|
300
|
+
disabled: computed(() => false),
|
|
301
|
+
...(emptyOption?.properties ?? ({} as TCustomProperties)),
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
267
305
|
const customProperties = config?.option?.properties?.(source) ?? ({} as TCustomProperties)
|
|
268
306
|
const label = computed(() => guessLabel(source, customProperties, config?.option?.label))
|
|
269
307
|
const value = computed(() => guessValue(source, customProperties, config?.option?.value) as $TValue)
|
|
@@ -296,7 +334,7 @@ export function useFormSelect<
|
|
|
296
334
|
|
|
297
335
|
const { items: options } = useSubset(option => option.properties.matching)
|
|
298
336
|
|
|
299
|
-
const { items: selectedOptions } = useFlag('selected')
|
|
337
|
+
const { items: selectedOptions, toggleAll: toggleSelectAll } = useFlag('selected')
|
|
300
338
|
|
|
301
339
|
const selectedOption = computed(() => selectedOptions.value[0])
|
|
302
340
|
|
|
@@ -318,12 +356,18 @@ export function useFormSelect<
|
|
|
318
356
|
watch(
|
|
319
357
|
model,
|
|
320
358
|
modelValue => {
|
|
359
|
+
toggleSelectAll(false)
|
|
360
|
+
|
|
321
361
|
if (isMultiple.value) {
|
|
322
362
|
allOptions.value.forEach(option => {
|
|
323
|
-
|
|
363
|
+
if ((modelValue as $TValue[]).includes(toRaw(option.properties.value) as $TValue)) {
|
|
364
|
+
option.toggleFlag('selected', true)
|
|
365
|
+
}
|
|
324
366
|
})
|
|
325
367
|
} else {
|
|
326
|
-
allOptions.value
|
|
368
|
+
allOptions.value
|
|
369
|
+
.find(option => toRaw(option.properties.value) === toRaw(modelValue))
|
|
370
|
+
?.toggleFlag('selected', true)
|
|
327
371
|
}
|
|
328
372
|
},
|
|
329
373
|
{ immediate: true }
|
|
@@ -358,14 +402,14 @@ export function useFormSelect<
|
|
|
358
402
|
selectedOption,
|
|
359
403
|
selectedOptions,
|
|
360
404
|
selectedLabel,
|
|
361
|
-
} satisfies FormSelect<TCustomProperties, any> as FormSelect<TCustomProperties,
|
|
405
|
+
} satisfies FormSelect<TCustomProperties, any> as FormSelect<TCustomProperties, TSource, $TValue, TMultiple>
|
|
362
406
|
|
|
363
|
-
const id = Symbol('useFormSelect ID') as FormSelectId<TCustomProperties,
|
|
407
|
+
const id = Symbol('useFormSelect ID') as FormSelectId<TCustomProperties, TSource, $TValue, TMultiple>
|
|
364
408
|
|
|
365
409
|
provide(id, select)
|
|
366
410
|
|
|
367
411
|
return {
|
|
368
412
|
id,
|
|
369
413
|
...select,
|
|
370
|
-
} satisfies UseFormSelectReturn<TCustomProperties,
|
|
414
|
+
} satisfies UseFormSelectReturn<TCustomProperties, TSource, $TValue, TMultiple>
|
|
371
415
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# `defineRemoteResource` documentation
|
|
2
|
+
|
|
3
|
+
This utility will create a function that can be used to fetch data from a remote resource (i.e., an API endpoint)
|
|
4
|
+
|
|
5
|
+
When called, a subscription to the resource is registered.
|
|
6
|
+
|
|
7
|
+
If multiple Components call that same function, a subscription will be registered for each of them.
|
|
8
|
+
|
|
9
|
+
They will share the same data and state, which means that the data will be fetched only once and shared across all Components.
|
|
10
|
+
|
|
11
|
+
The fetch will start automatically when at least one Component is using the resource, then, when the last Component releases the resource, the polling will stop.
|
|
12
|
+
|
|
13
|
+
The data is cached for a certain duration, so if a Component remounts in the frame time, the data will be retrieved from the cache to be displayed immediately, while the data is being fetched again in the background.
|
|
14
|
+
|
|
15
|
+
## Basic usage
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
const useMyResource = defineRemoteResource({
|
|
19
|
+
url: '/api/path/to/resource',
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
By default, the initial data (`TData`) will be `undefined`, the returned state will be `{ data: Ref<TData> }` and when new data is received, it will just replace the previous one.
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
const { data } = useMyResource()
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Initial data, state and received data handling
|
|
30
|
+
|
|
31
|
+
The initial data, the state and what happen when data is retrieved can be customized by passing the `initialData`, `state` and `onDataReceived` properties:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
const useMyResource = defineRemoteResource({
|
|
35
|
+
url: '/api/path/to/resource',
|
|
36
|
+
initialData: () => {} as Partial<MyResource>,
|
|
37
|
+
state: (data) => {
|
|
38
|
+
return {
|
|
39
|
+
myResource: data,
|
|
40
|
+
customProp: computed(() => data.foobar)
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
onDataReceived: (currentData, receivedData) => {
|
|
44
|
+
deepMerge(currentData.value, receivedData)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const { myResource, customProp } = useMyResource()
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Context
|
|
52
|
+
|
|
53
|
+
The context is available in the `$context` property of the returned state.
|
|
54
|
+
|
|
55
|
+
It contains the following properties:
|
|
56
|
+
|
|
57
|
+
| Property | Type | Description |
|
|
58
|
+
| ------------- | --------------------------------- | ---------------------------------------------------------------------------------- |
|
|
59
|
+
| `scope` | `EffectScope` | The Vue effect scope used to be shared across the resource and its dependencies |
|
|
60
|
+
| `args` | `any[]` | The arguments passed to the `useMyResource` function |
|
|
61
|
+
| `isReady` | `ComputedRef<boolean>` | Indicates if the resource is ready (i.e., the data has been fetched at least once) |
|
|
62
|
+
| `hasError` | `ComputedRef<boolean>` | Indicates if an error occurred during the last fetch |
|
|
63
|
+
| `lastError` | `ComputedRef<Error \| undefined>` | The last error that occurred during the fetch, if any |
|
|
64
|
+
| `isEnabled` | `Ref<boolean>` | Whether the resource is enabled (execute the request) or not |
|
|
65
|
+
| `enable` | `() => void` | Function to manually enable the resource |
|
|
66
|
+
| `disable` | `() => void` | Function to manually disable the resource |
|
|
67
|
+
| `forceReload` | `() => void` | Function to manually reload the resource |
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
const { myResource, $context } = useMyResource()
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
It's also passed as the second argument of the `state` builder and can be used to extend the resource state.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
const useMyResource = defineRemoteResource({
|
|
77
|
+
url: '/api/path/to/resource',
|
|
78
|
+
state: (data, context) => ({
|
|
79
|
+
myResource: data,
|
|
80
|
+
isMyResourceReady: context.isReady,
|
|
81
|
+
}),
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const { myResource, isMyResourceReady } = useMyResource()
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Dynamic URLs
|
|
88
|
+
|
|
89
|
+
You can define a dynamic URL by using a function that returns the URL string.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
const useMyResource = defineRemoteResource({
|
|
93
|
+
url: (id: string) => `/api/path/to/resource/${id}`,
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
const { id } = defineProps<{
|
|
97
|
+
id: string
|
|
98
|
+
}>()
|
|
99
|
+
|
|
100
|
+
const { data } = useMyResource(() => id)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Polling and caching
|
|
104
|
+
|
|
105
|
+
By default, the resource will poll for updates every 30 seconds and cache the result for 10 seconds.
|
|
106
|
+
|
|
107
|
+
This can be customized by passing the `pollingIntervalMs` and `cacheDurationMs` properties:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const useMyResource = defineRemoteResource({
|
|
111
|
+
url: '/api/path/to/resource',
|
|
112
|
+
pollingIntervalMs: 60_000, // Poll every 60 seconds
|
|
113
|
+
cacheDurationMs: 5 * 60_000, // Cache for 5 minutes
|
|
114
|
+
})
|
|
115
|
+
```
|