@pilotiq/pilotiq 0.6.2 → 0.7.1
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/.turbo/turbo-build.log +6 -2
- package/CHANGELOG.md +614 -0
- package/CLAUDE.md +6 -5
- package/dist/Column.d.ts +35 -0
- package/dist/Column.d.ts.map +1 -1
- package/dist/Column.js +41 -0
- package/dist/Column.js.map +1 -1
- package/dist/Page.d.ts +13 -4
- package/dist/Page.d.ts.map +1 -1
- package/dist/Page.js +9 -2
- package/dist/Page.js.map +1 -1
- package/dist/Pilotiq.d.ts +84 -0
- package/dist/Pilotiq.d.ts.map +1 -1
- package/dist/Pilotiq.js +66 -0
- package/dist/Pilotiq.js.map +1 -1
- package/dist/Resource.d.ts +26 -0
- package/dist/Resource.d.ts.map +1 -1
- package/dist/Resource.js +9 -0
- package/dist/Resource.js.map +1 -1
- package/dist/actions/exportFactory.js +1 -1
- package/dist/actions/exportFactory.js.map +1 -1
- package/dist/columns/SelectColumn.d.ts +32 -5
- package/dist/columns/SelectColumn.d.ts.map +1 -1
- package/dist/columns/SelectColumn.js +37 -7
- package/dist/columns/SelectColumn.js.map +1 -1
- package/dist/defaultPages.d.ts.map +1 -1
- package/dist/defaultPages.js +3 -0
- package/dist/defaultPages.js.map +1 -1
- package/dist/elements/Form.d.ts +17 -0
- package/dist/elements/Form.d.ts.map +1 -1
- package/dist/elements/Form.js +17 -0
- package/dist/elements/Form.js.map +1 -1
- package/dist/elements/Table.d.ts +26 -0
- package/dist/elements/Table.d.ts.map +1 -1
- package/dist/elements/Table.js +15 -1
- package/dist/elements/Table.js.map +1 -1
- package/dist/elements/TableGroup.d.ts +84 -0
- package/dist/elements/TableGroup.d.ts.map +1 -1
- package/dist/elements/TableGroup.js +103 -0
- package/dist/elements/TableGroup.js.map +1 -1
- package/dist/elements/dispatchForm.d.ts.map +1 -1
- package/dist/elements/dispatchForm.js +36 -6
- package/dist/elements/dispatchForm.js.map +1 -1
- package/dist/elements/dispatchTable.d.ts +12 -0
- package/dist/elements/dispatchTable.d.ts.map +1 -1
- package/dist/elements/dispatchTable.js +103 -28
- package/dist/elements/dispatchTable.js.map +1 -1
- package/dist/fields/Field.d.ts +7 -2
- package/dist/fields/Field.d.ts.map +1 -1
- package/dist/fields/Field.js +8 -3
- package/dist/fields/Field.js.map +1 -1
- package/dist/fields/RepeaterField.d.ts +65 -0
- package/dist/fields/RepeaterField.d.ts.map +1 -1
- package/dist/fields/RepeaterField.js +48 -0
- package/dist/fields/RepeaterField.js.map +1 -1
- package/dist/orm/modelDefaults.d.ts.map +1 -1
- package/dist/orm/modelDefaults.js +19 -0
- package/dist/orm/modelDefaults.js.map +1 -1
- package/dist/pageData.d.ts +20 -0
- package/dist/pageData.d.ts.map +1 -1
- package/dist/pageData.js +242 -34
- package/dist/pageData.js.map +1 -1
- package/dist/react/AppShell.d.ts +17 -1
- package/dist/react/AppShell.d.ts.map +1 -1
- package/dist/react/AppShell.js +34 -3
- package/dist/react/AppShell.js.map +1 -1
- package/dist/react/PendingSuggestionApplierRegistry.d.ts +34 -0
- package/dist/react/PendingSuggestionApplierRegistry.d.ts.map +1 -0
- package/dist/react/PendingSuggestionApplierRegistry.js +51 -0
- package/dist/react/PendingSuggestionApplierRegistry.js.map +1 -0
- package/dist/react/PendingSuggestionOverlayRegistry.d.ts +46 -0
- package/dist/react/PendingSuggestionOverlayRegistry.d.ts.map +1 -0
- package/dist/react/PendingSuggestionOverlayRegistry.js +16 -0
- package/dist/react/PendingSuggestionOverlayRegistry.js.map +1 -0
- package/dist/react/PendingSuggestionsContext.d.ts +153 -0
- package/dist/react/PendingSuggestionsContext.d.ts.map +1 -0
- package/dist/react/PendingSuggestionsContext.js +46 -0
- package/dist/react/PendingSuggestionsContext.js.map +1 -0
- package/dist/react/SchemaRenderer.d.ts.map +1 -1
- package/dist/react/SchemaRenderer.js +312 -39
- package/dist/react/SchemaRenderer.js.map +1 -1
- package/dist/react/cells/EditableCell.d.ts +8 -0
- package/dist/react/cells/EditableCell.d.ts.map +1 -1
- package/dist/react/cells/EditableCell.js +6 -2
- package/dist/react/cells/EditableCell.js.map +1 -1
- package/dist/react/fields/CheckboxListInput.d.ts.map +1 -1
- package/dist/react/fields/CheckboxListInput.js +29 -2
- package/dist/react/fields/CheckboxListInput.js.map +1 -1
- package/dist/react/fields/ColorInput.d.ts.map +1 -1
- package/dist/react/fields/ColorInput.js +28 -2
- package/dist/react/fields/ColorInput.js.map +1 -1
- package/dist/react/fields/DateTimeInput.d.ts.map +1 -1
- package/dist/react/fields/DateTimeInput.js +28 -2
- package/dist/react/fields/DateTimeInput.js.map +1 -1
- package/dist/react/fields/FieldShell.d.ts.map +1 -1
- package/dist/react/fields/FieldShell.js +161 -3
- package/dist/react/fields/FieldShell.js.map +1 -1
- package/dist/react/fields/FileUploadInput.d.ts.map +1 -1
- package/dist/react/fields/FileUploadInput.js +27 -2
- package/dist/react/fields/FileUploadInput.js.map +1 -1
- package/dist/react/fields/KeyValueInput.d.ts.map +1 -1
- package/dist/react/fields/KeyValueInput.js +33 -2
- package/dist/react/fields/KeyValueInput.js.map +1 -1
- package/dist/react/fields/RadioInput.d.ts.map +1 -1
- package/dist/react/fields/RadioInput.js +28 -2
- package/dist/react/fields/RadioInput.js.map +1 -1
- package/dist/react/fields/SelectFieldInput.d.ts.map +1 -1
- package/dist/react/fields/SelectFieldInput.js +31 -2
- package/dist/react/fields/SelectFieldInput.js.map +1 -1
- package/dist/react/fields/SliderInput.d.ts.map +1 -1
- package/dist/react/fields/SliderInput.js +26 -2
- package/dist/react/fields/SliderInput.js.map +1 -1
- package/dist/react/fields/TagsInput.d.ts.map +1 -1
- package/dist/react/fields/TagsInput.js +26 -2
- package/dist/react/fields/TagsInput.js.map +1 -1
- package/dist/react/fields/ToggleFieldInput.d.ts.map +1 -1
- package/dist/react/fields/ToggleFieldInput.js +29 -2
- package/dist/react/fields/ToggleFieldInput.js.map +1 -1
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +3 -0
- package/dist/react/index.js.map +1 -1
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +55 -2
- package/dist/routes.js.map +1 -1
- package/dist/schema/Section.d.ts +16 -0
- package/dist/schema/Section.d.ts.map +1 -1
- package/dist/schema/Section.js +16 -0
- package/dist/schema/Section.js.map +1 -1
- package/dist/schema/Wizard.d.ts +45 -0
- package/dist/schema/Wizard.d.ts.map +1 -1
- package/dist/schema/Wizard.js +50 -0
- package/dist/schema/Wizard.js.map +1 -1
- package/dist/schema/resolveSchema.d.ts +8 -0
- package/dist/schema/resolveSchema.d.ts.map +1 -1
- package/dist/schema/resolveSchema.js +70 -1
- package/dist/schema/resolveSchema.js.map +1 -1
- package/dist/sessionFilters.d.ts.map +1 -1
- package/dist/sessionFilters.js +12 -1
- package/dist/sessionFilters.js.map +1 -1
- package/dist/styles/file-upload.css +13 -0
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +19 -12
- package/dist/vite.js.map +1 -1
- package/package.json +6 -4
- package/src/Column.test.ts +36 -0
- package/src/Column.ts +54 -0
- package/src/Page.ts +13 -4
- package/src/Pilotiq.ts +109 -0
- package/src/Resource.ts +29 -0
- package/src/actions/exportFactory.ts +1 -1
- package/src/columns/SelectColumn.ts +46 -8
- package/src/columns/editableColumns.test.ts +45 -0
- package/src/defaultPages.ts +3 -0
- package/src/elements/Form.ts +19 -0
- package/src/elements/Table.ts +35 -1
- package/src/elements/TableGroup.test.ts +111 -0
- package/src/elements/TableGroup.ts +135 -0
- package/src/elements/dispatchForm.ts +34 -7
- package/src/elements/dispatchTable.test.ts +267 -0
- package/src/elements/dispatchTable.ts +111 -32
- package/src/fields/Field.test.ts +15 -0
- package/src/fields/Field.ts +8 -3
- package/src/fields/RepeaterField.ts +104 -0
- package/src/fields/RepeaterRelationship.test.ts +173 -0
- package/src/nestedRelationManagerData.test.ts +21 -0
- package/src/orm/modelDefaults.ts +21 -0
- package/src/pageData.ts +267 -47
- package/src/react/AppShell.tsx +55 -4
- package/src/react/PendingSuggestionApplierRegistry.ts +80 -0
- package/src/react/PendingSuggestionOverlayRegistry.ts +54 -0
- package/src/react/PendingSuggestionsContext.tsx +172 -0
- package/src/react/SchemaRenderer.tsx +504 -95
- package/src/react/cells/EditableCell.tsx +11 -2
- package/src/react/fields/CheckboxListInput.tsx +23 -2
- package/src/react/fields/ColorInput.tsx +22 -2
- package/src/react/fields/DateTimeInput.tsx +22 -2
- package/src/react/fields/FieldShell.tsx +167 -3
- package/src/react/fields/FileUploadInput.tsx +21 -2
- package/src/react/fields/KeyValueInput.tsx +32 -2
- package/src/react/fields/RadioInput.tsx +23 -2
- package/src/react/fields/SelectFieldInput.tsx +25 -2
- package/src/react/fields/SliderInput.tsx +20 -2
- package/src/react/fields/TagsInput.tsx +20 -2
- package/src/react/fields/ToggleFieldInput.tsx +23 -2
- package/src/react/index.ts +18 -0
- package/src/relationManagerData.test.ts +451 -2
- package/src/routes.ts +58 -2
- package/src/schema/Section.ts +17 -0
- package/src/schema/Wizard.ts +67 -0
- package/src/schema/containers.test.ts +90 -0
- package/src/schema/resolveSchema.test.ts +50 -0
- package/src/schema/resolveSchema.ts +79 -1
- package/src/sessionFilters.test.ts +23 -0
- package/src/sessionFilters.ts +11 -1
- package/src/styles/file-upload.css +13 -0
- package/src/vite.ts +19 -12
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ComponentType } from 'react'
|
|
2
|
+
import type { PendingSuggestion } from './PendingSuggestionsContext.js'
|
|
3
|
+
import type { ElementMeta } from '../schema/Element.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Props the per-field overlay component receives from `FieldShell` when
|
|
7
|
+
* one or more pending suggestions target the field.
|
|
8
|
+
*
|
|
9
|
+
* The overlay is responsible for both rendering AND applying the
|
|
10
|
+
* suggestion. On Approve, call `onApprove()` (which dismisses the
|
|
11
|
+
* suggestion from the queue) AFTER mutating the form's field value to
|
|
12
|
+
* `suggestion.suggestedValue` — the queue is just a notification surface,
|
|
13
|
+
* applying is the renderer's job.
|
|
14
|
+
*
|
|
15
|
+
* The Tiptap `RichTextField` renderer skips this slot entirely — it
|
|
16
|
+
* mirrors suggestions into the editor's inline AiSuggestion extension
|
|
17
|
+
* instead. Other field types (Text / Textarea / Select / Number / …)
|
|
18
|
+
* render the registered overlay below their input.
|
|
19
|
+
*/
|
|
20
|
+
export interface PendingSuggestionOverlayProps {
|
|
21
|
+
/** First suggestion targeting this field. Aggregate UIs handle stacks. */
|
|
22
|
+
suggestion: PendingSuggestion
|
|
23
|
+
/** Drop from queue (callable after applying). */
|
|
24
|
+
onApprove: () => void
|
|
25
|
+
/** Drop from queue without applying. */
|
|
26
|
+
onReject: () => void
|
|
27
|
+
/** Field type string (`text` / `select` / `toggle` / `slider` / `color` /
|
|
28
|
+
* …) so per-fieldType overlay renderers can branch. Sparse — older
|
|
29
|
+
* hosts may omit. Phase C of ai-review-mode. */
|
|
30
|
+
fieldType?: string
|
|
31
|
+
/** Resolved field meta — gives the overlay access to per-field config
|
|
32
|
+
* (`options` for Select, `min/max` for Slider, etc.) needed to render
|
|
33
|
+
* human-friendly comparisons rather than raw values. Sparse — older
|
|
34
|
+
* hosts may omit. */
|
|
35
|
+
el?: ElementMeta
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let _component: ComponentType<PendingSuggestionOverlayProps> | null = null
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Register a component to render below any `FieldShell` whose field has at
|
|
42
|
+
* least one matching pending suggestion in the
|
|
43
|
+
* `<PendingSuggestionsContext>` queue. Called once at boot by a plugin
|
|
44
|
+
* (e.g. `@pilotiq-pro/ai`). No-op when no plugin registers — `FieldShell`
|
|
45
|
+
* skips the overlay slot.
|
|
46
|
+
*/
|
|
47
|
+
export function registerPendingSuggestionOverlay(C: ComponentType<PendingSuggestionOverlayProps>): void {
|
|
48
|
+
_component = C
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Returns the registered overlay component, or `null`. */
|
|
52
|
+
export function getPendingSuggestionOverlay(): ComponentType<PendingSuggestionOverlayProps> | null {
|
|
53
|
+
return _component
|
|
54
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import React, { createContext, useContext, useMemo } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* One AI- (or extension-) sourced suggested field-value change. Sits in a
|
|
5
|
+
* unified queue read by:
|
|
6
|
+
*
|
|
7
|
+
* - the Tiptap `RichTextField` renderer — for `richtext` fields, the
|
|
8
|
+
* suggestion is mirrored into the editor's `AiSuggestion` extension as
|
|
9
|
+
* an inline diff with per-hunk Approve/Reject chips
|
|
10
|
+
* - `FieldShell`'s overlay slot — for any other field type, a registered
|
|
11
|
+
* overlay component renders a `currentValue` vs `suggestedValue` diff
|
|
12
|
+
* card below the input
|
|
13
|
+
* - aggregate "Pending suggestions" pills (e.g. in a chat sidebar) — read
|
|
14
|
+
* the full list to show counts + bulk approve / reject affordances
|
|
15
|
+
*
|
|
16
|
+
* The shape is intentionally generic — the `meta` bag carries field-type-
|
|
17
|
+
* specific extras (e.g. `editorRange: { from, to }` for `richtext`).
|
|
18
|
+
*
|
|
19
|
+
* Pilotiq core does NOT push or apply suggestions itself. The provider
|
|
20
|
+
* implementation + push paths live in plugin packages (e.g.
|
|
21
|
+
* `@pilotiq-pro/ai`); core just owns the type, context, and hooks so any
|
|
22
|
+
* field renderer can subscribe through one open-core seam.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Where a suggestion came from. Lets aggregate consumers (pill UIs)
|
|
26
|
+
* filter the shared queue by surface — e.g. a popover-chat scoped pill
|
|
27
|
+
* shows only suggestions produced by its own session, while the
|
|
28
|
+
* sidebar pill shows everything. Sparse on push; consumers treat
|
|
29
|
+
* absence as "unknown origin, include in unfiltered views".
|
|
30
|
+
*/
|
|
31
|
+
export interface PendingSuggestionOrigin {
|
|
32
|
+
/**
|
|
33
|
+
* Which UI surface initiated the agent run that produced this
|
|
34
|
+
* suggestion. `'field-action'` covers the `✦` per-field dropdown.
|
|
35
|
+
*/
|
|
36
|
+
surface: 'sidebar' | 'popover' | 'field-action'
|
|
37
|
+
/**
|
|
38
|
+
* Stable id of the agent run / chat turn that produced this
|
|
39
|
+
* suggestion. Popover pills filter on this so they only see their
|
|
40
|
+
* own session's output even when the panel-wide queue holds many.
|
|
41
|
+
*/
|
|
42
|
+
runId?: string
|
|
43
|
+
/** Slug of the agent whose tool call produced the suggestion. */
|
|
44
|
+
agentSlug?: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface PendingSuggestion {
|
|
48
|
+
/** Stable id; the producer is responsible for uniqueness. */
|
|
49
|
+
id: string
|
|
50
|
+
/** Field name this suggestion targets. Matched verbatim in the renderer. */
|
|
51
|
+
fieldName: string
|
|
52
|
+
/**
|
|
53
|
+
* Form scope. Multi-form pages (Plan #5 reactive fields) stamp the form's
|
|
54
|
+
* id here so a renderer in form A doesn't pick up a suggestion meant for
|
|
55
|
+
* form B. Optional — when both producer and consumer omit it, suggestions
|
|
56
|
+
* are global.
|
|
57
|
+
*/
|
|
58
|
+
formId?: string
|
|
59
|
+
/** Snapshot of the field's value at the time the suggestion was produced. */
|
|
60
|
+
currentValue: unknown
|
|
61
|
+
/** Proposed replacement. */
|
|
62
|
+
suggestedValue: unknown
|
|
63
|
+
/** Optional attribution surfaced on the diff overlay / inline chip. */
|
|
64
|
+
source?: {
|
|
65
|
+
agentSlug?: string
|
|
66
|
+
agentLabel?: string
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Provenance hint for cross-surface filtering. Sparse — pushed when
|
|
70
|
+
* the producer knows which agent run / chat surface it's running
|
|
71
|
+
* inside (e.g. the popover-chat tagging its turn id so its scoped
|
|
72
|
+
* pill can filter the shared queue). Aggregate consumers that don't
|
|
73
|
+
* care just leave the field unread.
|
|
74
|
+
*/
|
|
75
|
+
origin?: PendingSuggestionOrigin
|
|
76
|
+
/** Wallclock ms when produced. Producers fill this in on `push`. */
|
|
77
|
+
createdAt: number
|
|
78
|
+
/** Field-type-specific extras (e.g. `editorRange: { from, to }`). Sparse. */
|
|
79
|
+
meta?: Record<string, unknown>
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface PendingSuggestionsApi {
|
|
83
|
+
/** Full list across every field + form. Aggregate consumers read this. */
|
|
84
|
+
list: readonly PendingSuggestion[]
|
|
85
|
+
/** Add a suggestion to the queue. Returns the (possibly producer-supplied) id. */
|
|
86
|
+
push: (suggestion: Omit<PendingSuggestion, 'createdAt'> & { createdAt?: number }) => string
|
|
87
|
+
/**
|
|
88
|
+
* Drop the suggestion from the queue. Used by both inline approval
|
|
89
|
+
* paths (where the renderer applies directly and just notifies the
|
|
90
|
+
* queue) AND by Reject. For aggregate consumers (e.g. a chat-pill
|
|
91
|
+
* "Approve all" button) that live outside the form's React tree, see
|
|
92
|
+
* `approve` below — it looks up a registered applier and calls it
|
|
93
|
+
* before dismissing.
|
|
94
|
+
*/
|
|
95
|
+
dismiss: (id: string) => void
|
|
96
|
+
/**
|
|
97
|
+
* Apply + dismiss. Resolves the suggestion's `(formId, fieldName)`
|
|
98
|
+
* pair against `PendingSuggestionApplierRegistry`; if an applier is
|
|
99
|
+
* registered (FieldShell + Tiptap bridge auto-register on mount),
|
|
100
|
+
* runs it and then dismisses. If no applier is registered (or the
|
|
101
|
+
* applier throws), falls through to a plain `dismiss` so the queue
|
|
102
|
+
* still clears — never strands an entry.
|
|
103
|
+
*
|
|
104
|
+
* Use this from cross-tree surfaces (chat-sidebar pending-pill).
|
|
105
|
+
* Inline surfaces (FieldShell overlay, Tiptap chip) apply via their
|
|
106
|
+
* own React-tree-local mutators and call `dismiss` directly.
|
|
107
|
+
*/
|
|
108
|
+
approve: (id: string) => void
|
|
109
|
+
/**
|
|
110
|
+
* Drop every suggestion matching the optional filter. With no filter,
|
|
111
|
+
* empties the entire queue.
|
|
112
|
+
*/
|
|
113
|
+
dismissAll: (filter?: { fieldName?: string; formId?: string }) => void
|
|
114
|
+
/**
|
|
115
|
+
* Apply + dismiss every suggestion matching the optional filter.
|
|
116
|
+
* Calls `approve(id)` per entry — same fall-through semantics if an
|
|
117
|
+
* applier is missing or throws.
|
|
118
|
+
*/
|
|
119
|
+
approveAll: (filter?: { fieldName?: string; formId?: string }) => void
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const NOOP_API: PendingSuggestionsApi = Object.freeze({
|
|
123
|
+
list: Object.freeze([]) as readonly PendingSuggestion[],
|
|
124
|
+
push: () => '',
|
|
125
|
+
dismiss: () => {},
|
|
126
|
+
approve: () => {},
|
|
127
|
+
dismissAll: () => {},
|
|
128
|
+
approveAll: () => {},
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Context default is a no-op API — fields that subscribe in trees without a
|
|
133
|
+
* real provider mounted (e.g. the marketing site's preview, headless tests)
|
|
134
|
+
* see an empty list and no-op approval methods. Never throws.
|
|
135
|
+
*/
|
|
136
|
+
export const PendingSuggestionsContext = createContext<PendingSuggestionsApi>(NOOP_API)
|
|
137
|
+
|
|
138
|
+
/** Read the full queue + producer methods. Used by aggregate UIs. */
|
|
139
|
+
export function usePendingSuggestions(): PendingSuggestionsApi {
|
|
140
|
+
return useContext(PendingSuggestionsContext)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Subscribe a single field-renderer to its slice of the queue. Returns
|
|
145
|
+
* `list` already filtered + a `dismiss` callback that the renderer wires
|
|
146
|
+
* to its Approve / Reject buttons.
|
|
147
|
+
*
|
|
148
|
+
* Matching rules:
|
|
149
|
+
* - `s.fieldName === fieldName` (verbatim)
|
|
150
|
+
* - if both `formId` args are non-`undefined`, they must match; otherwise
|
|
151
|
+
* the entry passes (so global suggestions reach scoped readers and
|
|
152
|
+
* vice-versa)
|
|
153
|
+
*
|
|
154
|
+
* The list reference is stable across renders unless the underlying
|
|
155
|
+
* filtered slice actually changes — useful for `useEffect` dependencies
|
|
156
|
+
* in renderers that mirror suggestions into their own state.
|
|
157
|
+
*/
|
|
158
|
+
export function usePendingSuggestionsForField(
|
|
159
|
+
fieldName: string,
|
|
160
|
+
formId?: string,
|
|
161
|
+
): {
|
|
162
|
+
list: readonly PendingSuggestion[]
|
|
163
|
+
dismiss: (id: string) => void
|
|
164
|
+
} {
|
|
165
|
+
const api = useContext(PendingSuggestionsContext)
|
|
166
|
+
const list = useMemo(() => api.list.filter(s => {
|
|
167
|
+
if (s.fieldName !== fieldName) return false
|
|
168
|
+
if (formId === undefined || s.formId === undefined) return true
|
|
169
|
+
return s.formId === formId
|
|
170
|
+
}), [api.list, fieldName, formId])
|
|
171
|
+
return { list, dismiss: api.dismiss }
|
|
172
|
+
}
|