@xen-orchestra/web-core 0.26.0 → 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.
@@ -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
- TBaseSource extends CollectionItemId,
28
- TAllowEmpty extends boolean = false,
28
+ TSource extends CollectionItemId,
29
29
  TCustomProperties extends CollectionItemProperties = EmptyObject,
30
- TGetValue extends GetOptionValue<$TSource, TCustomProperties> = undefined,
30
+ TGetValue extends GetOptionValue<TSource, TCustomProperties> = undefined,
31
31
  TMultiple extends boolean = false,
32
- $TSource = TAllowEmpty extends true ? TBaseSource | undefined : TBaseSource,
33
- $TValue = ExtractValue<$TSource, TGetValue>,
32
+ TEmptyValue = never,
33
+ $TValue = ExtractValue<TSource, TGetValue>,
34
34
  >(
35
- sources: MaybeRefOrGetter<TBaseSource[]>,
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<TBaseSource>
49
- value?: TGetValue | ((source: $TSource, properties: TCustomProperties) => $TValue)
50
- properties?: (source: $TSource) => TCustomProperties
51
- label?: GetOptionLabel<$TSource, TCustomProperties>
52
- selectedLabel?: (source: $TSource, properties: TCustomProperties) => string
53
- disabled?: (source: $TSource, properties: TCustomProperties) => boolean
54
- searchableTerm?: (source: $TSource, properties: TCustomProperties) => MaybeArray<string>
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, $TSource, $TValue, TMultiple>
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
- TBaseSource extends { id: CollectionItemId; label: string },
63
- TAllowEmpty extends boolean = false,
67
+ TSource extends { id: CollectionItemId; label: string },
64
68
  TCustomProperties extends CollectionItemProperties = EmptyObject,
65
- TGetValue extends GetOptionValue<$TSource, TCustomProperties> = undefined,
69
+ TGetValue extends GetOptionValue<TSource, TCustomProperties> = undefined,
66
70
  TMultiple extends boolean = false,
67
- $TSource = TAllowEmpty extends true ? TBaseSource | undefined : TBaseSource,
68
- $TValue = ExtractValue<$TSource, TGetValue>,
71
+ TEmptyValue = never,
72
+ $TValue = ExtractValue<TSource, TGetValue>,
69
73
  >(
70
- sources: MaybeRefOrGetter<TBaseSource[]>,
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<TBaseSource>
84
- value?: TGetValue | ((source: $TSource, properties: TCustomProperties) => $TValue)
85
- properties?: (source: $TSource) => TCustomProperties
86
- label?: GetOptionLabel<$TSource, TCustomProperties>
87
- selectedLabel?: (source: $TSource, properties: TCustomProperties) => string
88
- disabled?: (source: $TSource, properties: TCustomProperties) => boolean
89
- searchableTerm?: (source: $TSource, properties: TCustomProperties) => MaybeArray<string>
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, $TSource, $TValue, TMultiple>
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
- TBaseSource extends { id: CollectionItemId },
98
- TAllowEmpty extends boolean = false,
106
+ TSource extends { id: CollectionItemId },
99
107
  TCustomProperties extends CollectionItemProperties = EmptyObject,
100
- TGetValue extends GetOptionValue<$TSource, TCustomProperties> = undefined,
108
+ TGetValue extends GetOptionValue<TSource, TCustomProperties> = undefined,
101
109
  TMultiple extends boolean = false,
102
- $TSource = TAllowEmpty extends true ? TBaseSource | undefined : TBaseSource,
103
- $TValue = ExtractValue<$TSource, TGetValue>,
110
+ TEmptyValue = never,
111
+ $TValue = ExtractValue<TSource, TGetValue>,
104
112
  >(
105
- sources: MaybeRefOrGetter<TBaseSource[]>,
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<TBaseSource>
119
- value?: TGetValue | ((source: $TSource, properties: TCustomProperties) => $TValue)
120
- properties?: (source: $TSource) => TCustomProperties
121
- label: GetOptionLabel<$TSource, TCustomProperties>
122
- selectedLabel?: (source: $TSource, properties: TCustomProperties) => string
123
- disabled?: (source: $TSource, properties: TCustomProperties) => boolean
124
- searchableTerm?: (source: $TSource, properties: TCustomProperties) => MaybeArray<string>
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, $TSource, $TValue, TMultiple>
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
- TBaseSource extends { label: string },
133
- TAllowEmpty extends boolean = false,
145
+ TSource extends { label: string },
134
146
  TCustomProperties extends CollectionItemProperties = EmptyObject,
135
- TGetValue extends GetOptionValue<$TSource, TCustomProperties> = undefined,
147
+ TGetValue extends GetOptionValue<TSource, TCustomProperties> = undefined,
136
148
  TMultiple extends boolean = false,
137
- $TSource = TAllowEmpty extends true ? TBaseSource | undefined : TBaseSource,
138
- $TValue = ExtractValue<$TSource, TGetValue>,
149
+ TEmptyValue = never,
150
+ $TValue = ExtractValue<TSource, TGetValue>,
139
151
  >(
140
- sources: MaybeRefOrGetter<TBaseSource[]>,
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<TBaseSource>
154
- value?: TGetValue | ((source: $TSource, properties: TCustomProperties) => $TValue)
155
- properties?: (source: $TSource) => TCustomProperties
156
- label?: GetOptionLabel<$TSource, TCustomProperties>
157
- selectedLabel?: (source: $TSource, properties: TCustomProperties) => string
158
- disabled?: (source: $TSource, properties: TCustomProperties) => boolean
159
- searchableTerm?: (source: $TSource, properties: TCustomProperties) => MaybeArray<string>
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, $TSource, $TValue, TMultiple>
179
+ ): UseFormSelectReturn<TCustomProperties, TSource, $TValue | TEmptyValue, TMultiple>
163
180
 
164
181
  // Overload #5: Any other case
165
182
 
166
183
  export function useFormSelect<
167
- TBaseSource,
168
- TAllowEmpty extends boolean = false,
184
+ TSource,
169
185
  TCustomProperties extends CollectionItemProperties = EmptyObject,
170
- TGetValue extends GetOptionValue<$TSource, TCustomProperties> = undefined,
186
+ TGetValue extends GetOptionValue<TSource, TCustomProperties> = undefined,
171
187
  TMultiple extends boolean = false,
172
- $TSource = TAllowEmpty extends true ? TBaseSource | undefined : TBaseSource,
173
- $TValue = ExtractValue<$TSource, TGetValue>,
188
+ TEmptyValue = never,
189
+ $TValue = ExtractValue<TSource, TGetValue>,
174
190
  >(
175
- sources: MaybeRefOrGetter<TBaseSource[]>,
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<TBaseSource>
189
- value?: TGetValue | ((source: $TSource, properties: TCustomProperties) => $TValue)
190
- properties?: (source: $TSource) => TCustomProperties
191
- label: GetOptionLabel<$TSource, TCustomProperties>
192
- selectedLabel?: (source: $TSource, properties: TCustomProperties) => string
193
- disabled?: (source: $TSource, properties: TCustomProperties) => boolean
194
- searchableTerm?: (source: $TSource, properties: TCustomProperties) => MaybeArray<string>
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, $TSource, $TValue, TMultiple>
218
+ ): UseFormSelectReturn<TCustomProperties, TSource, $TValue | TEmptyValue, TMultiple>
198
219
 
199
220
  // Implementation
200
221
 
201
222
  export function useFormSelect<
202
- TBaseSource,
203
- TAllowEmpty extends boolean = false,
223
+ TSource,
204
224
  TCustomProperties extends CollectionItemProperties = EmptyObject,
205
- TGetValue extends GetOptionValue<$TSource, TCustomProperties> = undefined,
225
+ TGetValue extends GetOptionValue<TSource, TCustomProperties> = undefined,
206
226
  TMultiple extends boolean = false,
207
- $TSource = TAllowEmpty extends true ? TBaseSource | undefined : TBaseSource,
208
- $TValue = ExtractValue<$TSource, TGetValue>,
227
+ TEmptyValue = never,
228
+ $TValue = ExtractValue<TSource, TGetValue>,
209
229
  >(
210
- baseSources: MaybeRefOrGetter<TBaseSource[]>,
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<TBaseSource>
224
- value?: GetOptionValue<$TSource, TCustomProperties>
225
- properties?: (source: $TSource) => TCustomProperties
226
- label?: GetOptionLabel<$TSource, TCustomProperties>
227
- selectedLabel?: (source: $TSource, properties: TCustomProperties) => string
228
- disabled?: (source: $TSource, properties: TCustomProperties) => boolean
229
- searchableTerm?: (source: $TSource, properties: TCustomProperties) => MaybeArray<string>
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, $TSource, $TValue, TMultiple> {
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?.allowEmpty ? [undefined, ...toValue(baseSources)] : toValue(baseSources)
253
- ) as ComputedRef<$TSource[]>
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 === undefined ? '__EMPTY_OPTION__' : guessItemId(source as TBaseSource, config?.option?.id),
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
- option.toggleFlag('selected', (modelValue as $TValue[]).includes(option.properties.value as $TValue))
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.find(option => option.properties.value === modelValue)?.toggleFlag('selected', true)
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, $TSource, $TValue, TMultiple>
405
+ } satisfies FormSelect<TCustomProperties, any> as FormSelect<TCustomProperties, TSource, $TValue, TMultiple>
362
406
 
363
- const id = Symbol('useFormSelect ID') as FormSelectId<TCustomProperties, $TSource, $TValue, TMultiple>
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, $TSource, $TValue, TMultiple>
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
+ ```