@xen-orchestra/web-core 0.48.1 → 0.49.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.
@@ -0,0 +1,61 @@
1
+ import type { FormSelectId } from '@core/packages/form-select'
2
+ import { computed, type ComputedRef } from 'vue'
3
+
4
+ export type ModelBinding<T> = {
5
+ modelValue: T
6
+ 'onUpdate:modelValue': (value: T) => void
7
+ }
8
+
9
+ export type UseFormBindingsReturn<T extends Record<string, unknown>> = {
10
+ useField: {
11
+ <K extends keyof T>(key: K): ComputedRef<ModelBinding<T[K]>>
12
+ <K extends keyof T, E extends Record<string, unknown>>(
13
+ key: K,
14
+ metadata: () => E
15
+ ): ComputedRef<ModelBinding<T[K]> & E>
16
+ }
17
+ useSelect: {
18
+ (id: FormSelectId): ComputedRef<{ id: FormSelectId }>
19
+ <E extends Record<string, unknown>>(id: FormSelectId, metadata: () => E): ComputedRef<{ id: FormSelectId } & E>
20
+ }
21
+ }
22
+
23
+ export function useFormBindings<T extends Record<string, unknown>>(source: T): UseFormBindingsReturn<T> {
24
+ function useField<K extends keyof T>(key: K): ComputedRef<ModelBinding<T[K]>>
25
+ function useField<K extends keyof T, E extends Record<string, unknown>>(
26
+ key: K,
27
+ metadata: () => E
28
+ ): ComputedRef<ModelBinding<T[K]> & E>
29
+ function useField<K extends keyof T, E extends Record<string, unknown> = Record<string, unknown>>(
30
+ key: K,
31
+ metadata?: () => E
32
+ ) {
33
+ return computed(() => ({
34
+ modelValue: source[key],
35
+ 'onUpdate:modelValue': (value: T[K]) => {
36
+ source[key] = value
37
+ },
38
+ ...metadata?.(),
39
+ }))
40
+ }
41
+
42
+ function useSelect(id: FormSelectId): ComputedRef<{ id: FormSelectId }>
43
+ function useSelect<E extends Record<string, unknown>>(
44
+ id: FormSelectId,
45
+ metadata: () => E
46
+ ): ComputedRef<{ id: FormSelectId } & E>
47
+ function useSelect<E extends Record<string, unknown> = Record<string, unknown>>(
48
+ id: FormSelectId,
49
+ metadata?: () => E
50
+ ) {
51
+ return computed(() => ({
52
+ id,
53
+ ...metadata?.(),
54
+ }))
55
+ }
56
+
57
+ return {
58
+ useField,
59
+ useSelect,
60
+ }
61
+ }
@@ -7,8 +7,8 @@ import {
7
7
  import type { ResourceContext, UseRemoteResource } from '@core/packages/remote-resource/types.ts'
8
8
  import type { VoidFunction } from '@core/types/utility.type.ts'
9
9
  import { ifElse } from '@core/utils/if-else.utils.ts'
10
- import { type MaybeRef, noop, useTimeoutPoll } from '@vueuse/core'
11
- import { debounce, merge, remove } from 'lodash-es'
10
+ import { type MaybeRef, noop, useDebounceFn, useTimeoutPoll } from '@vueuse/core'
11
+ import { merge, remove } from 'lodash-es'
12
12
  import readNDJSONStream from 'ndjson-readablestream'
13
13
  import {
14
14
  computed,
@@ -25,6 +25,7 @@ import {
25
25
  toRef,
26
26
  toValue,
27
27
  watch,
28
+ effectScope,
28
29
  } from 'vue'
29
30
 
30
31
  const DEFAULT_CACHE_EXPIRATION_MS = 10_000
@@ -106,6 +107,7 @@ export function defineRemoteResource<
106
107
  pause: VoidFunction
107
108
  resume: VoidFunction
108
109
  state: object
110
+ stateScope: EffectScope
109
111
  isReady: Ref<boolean>
110
112
  isFetching: Ref<boolean>
111
113
  lastError: Ref<Error | undefined>
@@ -239,6 +241,7 @@ export function defineRemoteResource<
239
241
 
240
242
  if (cacheExpiration !== false) {
241
243
  setTimeout(() => {
244
+ cache.get(url)?.stateScope.stop()
242
245
  cache.delete(url)
243
246
  }, cacheExpiration)
244
247
  }
@@ -259,7 +262,18 @@ export function defineRemoteResource<
259
262
 
260
263
  const data = shallowRef(buildData()) as Ref<TData>
261
264
  // trigger reactivity on data when no more updates since 100ms or after 500ms
262
- const flushData = debounce(() => triggerRef(data), 100, { maxWait: 500 })
265
+ const flushData = useDebounceFn(
266
+ () => {
267
+ if (Array.isArray(data.value)) {
268
+ triggerRef(data)
269
+ } else if (data.value != null) {
270
+ // create a new JS reference to ensure vueJS detect the change
271
+ data.value = { ...data.value }
272
+ }
273
+ },
274
+ 100,
275
+ { maxWait: 500 }
276
+ )
263
277
 
264
278
  async function execute() {
265
279
  try {
@@ -329,13 +343,15 @@ export function defineRemoteResource<
329
343
  resume = timeoutPoll.resume
330
344
  }
331
345
 
332
- const state = buildState(data, context)
346
+ const stateScope = effectScope(true)
347
+ const state = stateScope.run(() => buildState(data, context))!
333
348
 
334
349
  cache.set(url, {
335
350
  count: 0,
336
351
  pause,
337
352
  resume,
338
353
  state,
354
+ stateScope,
339
355
  isReady,
340
356
  isFetching,
341
357
  lastError,
@@ -49,7 +49,7 @@ export const useSseStore = defineStore('sse', () => {
49
49
  return false
50
50
  }
51
51
 
52
- return now.value.getTime() - sse.value.lastPing > 32_000
52
+ return now.value.getTime() - sse.value.lastPing > 40_000
53
53
  })
54
54
 
55
55
  const hasErrorSse = computed(() => isError.value || sse.value.errorSse !== null)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xen-orchestra/web-core",
3
3
  "type": "module",
4
- "version": "0.48.1",
4
+ "version": "0.49.0",
5
5
  "private": false,
6
6
  "exports": {
7
7
  "./*": {