@asteby/metacore-runtime-react 23.9.0 → 23.9.2

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/src/types.ts CHANGED
@@ -335,12 +335,46 @@ export type FieldWidget =
335
335
  | 'switch'
336
336
  | 'upload'
337
337
 
338
+ /**
339
+ * Per-option visibility gate for a STATIC enum (`options[]`). The option is only
340
+ * offered when the value of a sibling field (`field`, defaulting to the
341
+ * containing field's `dependsOn`) passes: value ∈ `in` and value ∉ `notIn`.
342
+ * Comparison is by string. Mirrors the kernel v3 `option.when` block; the SDK
343
+ * tolerates both the snake_case (`not_in`) the kernel serves and camelCase.
344
+ * An option WITHOUT a `when` always applies (retrocompat).
345
+ */
346
+ export interface OptionWhen {
347
+ /** Sibling field whose value gates this option. Falls back to `dependsOn`. */
348
+ field?: string
349
+ /** The option applies when the gating field's value is in this list. */
350
+ in?: string[]
351
+ /** The option applies when the gating field's value is NOT in this list. */
352
+ notIn?: string[]
353
+ /** snake_case alias served by the kernel manifest for `notIn`. */
354
+ not_in?: string[]
355
+ }
356
+
357
+ /**
358
+ * A single static enum option. `value`/`label` are the core pair; `icon`,
359
+ * `color` and `image` drive the option's leading visual where the renderer
360
+ * supports it. `when` gates the option's visibility by a sibling field's value
361
+ * (see {@link OptionWhen}) — used for dependent/cascading STATIC enums.
362
+ */
363
+ export interface OptionDef {
364
+ value: string
365
+ label: string
366
+ icon?: string
367
+ color?: string
368
+ image?: string
369
+ when?: OptionWhen
370
+ }
371
+
338
372
  export interface ActionFieldDef {
339
373
  key: string
340
374
  label: string
341
375
  type: string
342
376
  required?: boolean
343
- options?: { value: string; label: string }[]
377
+ options?: OptionDef[]
344
378
  defaultValue?: any
345
379
  placeholder?: string
346
380
  searchEndpoint?: string
@@ -64,6 +64,7 @@ export function useInfiniteScrollSentinel<
64
64
  const sentinelRef = useRef<S | null>(null)
65
65
  const cb = useRef(onLoadMore)
66
66
  const disabledRef = useRef(disabled)
67
+ const intersectingRef = useRef(false)
67
68
  cb.current = onLoadMore
68
69
  disabledRef.current = disabled
69
70
 
@@ -78,6 +79,7 @@ export function useInfiniteScrollSentinel<
78
79
  const observer = new IntersectionObserver(
79
80
  (entries) => {
80
81
  for (const entry of entries) {
82
+ intersectingRef.current = entry.isIntersecting
81
83
  if (entry.isIntersecting && !disabledRef.current) {
82
84
  cb.current()
83
85
  }
@@ -90,5 +92,15 @@ export function useInfiniteScrollSentinel<
90
92
  // Re-create only when the margin changes; onLoadMore/disabled are read live.
91
93
  }, [rootMargin])
92
94
 
95
+ // IntersectionObserver only fires on intersection CHANGES. If a loaded page
96
+ // is too short to push the sentinel out of view (small screens, short
97
+ // lanes), the sentinel stays intersecting and no further event ever comes —
98
+ // the list stalls until the user jiggles the scroll. When a load finishes
99
+ // (disabled flips back to false) and the sentinel is still in view, chain
100
+ // the next page.
101
+ useEffect(() => {
102
+ if (!disabled && intersectingRef.current) cb.current()
103
+ }, [disabled])
104
+
93
105
  return { rootRef, sentinelRef }
94
106
  }