@proveanything/smartlinks 1.11.1 → 1.11.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/dist/docs/API_SUMMARY.md +1 -1
- package/dist/docs/app-manifest.md +24 -16
- package/dist/docs/app-objects.md +17 -4
- package/dist/docs/mobile-admin-container.md +3 -1
- package/dist/docs/records-admin-pattern.md +305 -334
- package/dist/docs/ui-utils.md +257 -41
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/mobile-admin/errors.d.ts +65 -0
- package/dist/mobile-admin/errors.js +72 -0
- package/dist/mobile-admin/types.d.ts +199 -0
- package/dist/mobile-admin/types.js +1 -0
- package/docs/API_SUMMARY.md +1 -1
- package/docs/app-manifest.md +24 -16
- package/docs/app-objects.md +17 -4
- package/docs/mobile-admin-container.md +3 -1
- package/docs/records-admin-pattern.md +305 -334
- package/docs/ui-utils.md +257 -41
- package/package.json +1 -1
package/docs/ui-utils.md
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
# SmartLinks UI Utils (`@proveanything/smartlinks-utils-ui`)
|
|
2
2
|
|
|
3
|
-
> Companion React component library for the SmartLinks SDK. Ships the heavy,
|
|
3
|
+
> Companion React component library for the SmartLinks SDK. Ships the heavy,
|
|
4
|
+
> opinionated admin UI pieces that almost every SmartLinks microapp ends up
|
|
5
|
+
> needing — built once, theme-able, tree-shakeable, and wired straight into
|
|
6
|
+
> the SmartLinks SDK.
|
|
4
7
|
>
|
|
5
|
-
> Package: `@proveanything/smartlinks-utils-ui`
|
|
6
|
-
> Tracks: `@proveanything/smartlinks ≥ 1.9`
|
|
8
|
+
> Package: `@proveanything/smartlinks-utils-ui`
|
|
9
|
+
> Tracks: `@proveanything/smartlinks ≥ 1.9` (some hooks require ≥ 1.10)
|
|
7
10
|
|
|
8
11
|
---
|
|
9
12
|
|
|
10
13
|
## What is this module for?
|
|
11
14
|
|
|
12
|
-
`@proveanything/smartlinks-utils-ui` sits on top of `@proveanything/smartlinks`.
|
|
15
|
+
`@proveanything/smartlinks-utils-ui` sits on top of `@proveanything/smartlinks`.
|
|
16
|
+
The core SDK handles data — records, configurations, interactions. This module
|
|
17
|
+
handles **UI** — the shared React components, hooks, and admin shells that
|
|
18
|
+
translate SDK data into consistent admin interfaces.
|
|
13
19
|
|
|
14
20
|
**When do you need it?**
|
|
15
21
|
|
|
@@ -17,11 +23,15 @@
|
|
|
17
23
|
- You need a media asset picker, icon picker, or font picker in an admin panel
|
|
18
24
|
- You need a recursive rule/conditions editor for targeting or audience logic
|
|
19
25
|
- You want the standard inheritance/override editor for scoped records
|
|
20
|
-
- You need the `useResolvedRecord`
|
|
26
|
+
- You need the `useResolvedRecord` / `useCollectedRecords` hooks on the public widget side
|
|
21
27
|
|
|
22
|
-
You do **not** need it for apps that only use `appConfiguration`, basic widgets
|
|
28
|
+
You do **not** need it for apps that only use `appConfiguration`, basic widgets
|
|
29
|
+
without scoped data, or executor bundles.
|
|
23
30
|
|
|
24
|
-
> **Admin
|
|
31
|
+
> **Admin components are admin-only**: `RecordsAdminShell`, `AssetPicker` upload,
|
|
32
|
+
> `FacetRuleEditor`, etc. call the SDK with `admin: true`. Do not render them in
|
|
33
|
+
> public-facing views. The public-side hooks (`useResolvedRecord`,
|
|
34
|
+
> `useCollectedRecords`) are safe in widgets.
|
|
25
35
|
|
|
26
36
|
---
|
|
27
37
|
|
|
@@ -34,18 +44,21 @@ npm install @proveanything/smartlinks-utils-ui
|
|
|
34
44
|
Peer dependencies (you already have these in a SmartLinks app):
|
|
35
45
|
|
|
36
46
|
```bash
|
|
37
|
-
npm install react react-dom @proveanything/smartlinks
|
|
38
|
-
# Recommended — enables caching and pagination in Records Admin Shell:
|
|
39
|
-
npm install @tanstack/react-query
|
|
47
|
+
npm install react react-dom @proveanything/smartlinks @tanstack/react-query
|
|
40
48
|
```
|
|
41
49
|
|
|
50
|
+
`@tanstack/react-query` is required by every hook and by `RecordsAdminShell`
|
|
51
|
+
(caching, pagination, optimistic save). Wrap your app in a
|
|
52
|
+
`<QueryClientProvider>` somewhere up the tree.
|
|
53
|
+
|
|
42
54
|
Import the compiled styles **once** in your app entry (e.g. `main.tsx`):
|
|
43
55
|
|
|
44
56
|
```tsx
|
|
45
57
|
import '@proveanything/smartlinks-utils-ui/styles.css';
|
|
46
58
|
```
|
|
47
59
|
|
|
48
|
-
Components inherit your shadcn-compatible CSS variables (`--primary`,
|
|
60
|
+
Components inherit your shadcn-compatible CSS variables (`--primary`,
|
|
61
|
+
`--background`, `--border`, …) so they pick up your theme automatically.
|
|
49
62
|
|
|
50
63
|
---
|
|
51
64
|
|
|
@@ -53,7 +66,8 @@ Components inherit your shadcn-compatible CSS variables (`--primary`, `--backgro
|
|
|
53
66
|
|
|
54
67
|
| Module | What it is | When to reach for it |
|
|
55
68
|
|--------|------------|----------------------|
|
|
56
|
-
| [Records Admin Shell](#records-admin-shell) | Full admin UI for `app.records` with scope inheritance | Per-product / per-variant / per-batch / per-facet config tools |
|
|
69
|
+
| [Records Admin Shell](#records-admin-shell) | Full admin UI for `app.records` with scope inheritance, cardinality, and rule editing | Per-product / per-variant / per-batch / per-facet config tools |
|
|
70
|
+
| [FacetRuleEditor](#facet-rule-editor) | Standalone facet-rule builder with live preview | When you need rule editing outside the admin shell |
|
|
57
71
|
| [Asset Picker](#asset-picker) | Browse / upload / paste / URL-import images and files | Any time the admin needs to pick or upload media |
|
|
58
72
|
| [Icon Picker](#icon-picker) | Searchable Font Awesome 7 Pro picker | Configurable buttons, badges, menus, tiles |
|
|
59
73
|
| [Font Picker](#font-picker) | Google Fonts + custom uploaded fonts | Theme editors, brand customisation panels |
|
|
@@ -63,7 +77,10 @@ Components inherit your shadcn-compatible CSS variables (`--primary`, `--backgro
|
|
|
63
77
|
|
|
64
78
|
## Records Admin Shell
|
|
65
79
|
|
|
66
|
-
The primary export. A complete admin UI for managing `app.records` — typed
|
|
80
|
+
The primary export. A complete admin UI for managing `app.records` — typed
|
|
81
|
+
JSON blobs attached to facets, products, variants, batches, the collection
|
|
82
|
+
root, or matched via facet rules — with scope inheritance built in. You
|
|
83
|
+
provide the form for one record; the shell handles everything else.
|
|
67
84
|
|
|
68
85
|
```tsx
|
|
69
86
|
import {
|
|
@@ -82,25 +99,44 @@ import * as SL from '@proveanything/smartlinks';
|
|
|
82
99
|
scopes={['facet', 'product', 'variant', 'batch']}
|
|
83
100
|
contextScope={{ productId, variantId, batchId }} // from iframe URL — optional
|
|
84
101
|
defaultData={() => ({})}
|
|
85
|
-
csvSchema={{ columns: [/* ... */] }}
|
|
102
|
+
csvSchema={{ columns: [/* ... */] }} // optional — omit to disable CSV
|
|
86
103
|
renderEditor={(ctx) => <NutritionForm ctx={ctx} />}
|
|
87
|
-
renderPreview={({ resolved }) =>
|
|
104
|
+
renderPreview={({ resolved }) => (
|
|
105
|
+
<pre>{JSON.stringify(resolved, null, 2)}</pre>
|
|
106
|
+
)}
|
|
88
107
|
/>
|
|
89
108
|
```
|
|
90
109
|
|
|
91
|
-
|
|
110
|
+
### Valid `ScopeKind` values
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
type ScopeKind = 'collection' | 'product' | 'facet' | 'variant' | 'batch' | 'rule';
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
- `'collection'` — terminal default (one record for the whole brand)
|
|
117
|
+
- `'facet'` — anchored to a facet value (e.g. `bagel-type=white`)
|
|
118
|
+
- `'product'` / `'variant'` / `'batch'` — anchored to that node
|
|
119
|
+
- `'rule'` — synthetic UI scope: records targeted via a `facetRule`
|
|
120
|
+
(AND-of-OR over facets) rather than pinned to a node. Their refs start
|
|
121
|
+
with `rule:`, and the shell renders the `<FacetRuleEditor>` inline when the
|
|
122
|
+
user opens one of these records.
|
|
92
123
|
|
|
93
|
-
|
|
94
|
-
|
|
124
|
+
### What it handles
|
|
125
|
+
|
|
126
|
+
- **Browser pane** with scope tabs (Facet / Product / Variant / Batch / Rule), search, and status filter pills
|
|
127
|
+
- **Editor pane** with sticky save / discard / delete footer, optimistic save, and an unsaved-drafts tray
|
|
95
128
|
- **Per-field `<InheritanceMarker>`** showing whether a value is the record's own or inherited from a parent scope, with one-click revert-to-inherited
|
|
96
|
-
- **Inheritance resolver** walks `batch → variant → product → facet
|
|
129
|
+
- **Inheritance resolver** walks `proof → batch → variant → product → rule → facet → collection` server-side via SDK 1.10 `match()`
|
|
97
130
|
- **Collection-aware tabs**: calls `collection.get` and hides Variants / Batches tabs unless `collection.variants` / `collection.batches` are true — no flicker
|
|
98
131
|
- **Server-side pagination** via `useInfiniteQuery` — handles thousands of products with a "Load more" button
|
|
99
132
|
- **Context-aware**: pass `contextScope` from your iframe URL (`productId` / `variantId` / `batchId`) and the browser is constrained to that subtree with the right tab auto-selected
|
|
133
|
+
- **Cardinality**: `cardinality="singleton"` (one record per scope) or `"collection"` (many — FAQs, recipes, SOPs). Collection mode adds an item-list view (`itemViews: ['table' | 'cards' | 'gallery']`) and Back / prev / next nav
|
|
134
|
+
- **Multiple presentations** for the rail (`presentations: ['list' | 'compact']`) and right pane
|
|
100
135
|
- **CSV import / export** (optional — provide `csvSchema` to enable); failed rows come back as an annotated CSV
|
|
101
136
|
- **Bulk actions menu** (apply-to-many, copy-from, clear) via `bulkUpsert` / `bulkDelete`
|
|
137
|
+
- **Clipboard** — copy a record's value, paste onto another scope
|
|
102
138
|
- **Telemetry hook** (`onTelemetry`) emits typed events for save, delete, scope change, CSV import/export, bulk apply
|
|
103
|
-
- **i18n strings** fully overridable
|
|
139
|
+
- **i18n strings** fully overridable via the `i18n` prop
|
|
104
140
|
|
|
105
141
|
> Requires a `<QueryClientProvider>` from `@tanstack/react-query` somewhere up the tree.
|
|
106
142
|
|
|
@@ -109,37 +145,137 @@ import * as SL from '@proveanything/smartlinks';
|
|
|
109
145
|
```tsx
|
|
110
146
|
import {
|
|
111
147
|
// Hooks
|
|
112
|
-
useRecordList, useRecordEditor, useResolvedRecord,
|
|
148
|
+
useRecordList, useRecordEditor, useResolvedRecord, useCollectedRecords,
|
|
149
|
+
useResolveAllRecords, useRulePreview, useScopeProbe,
|
|
113
150
|
// Data helpers
|
|
114
151
|
parseRef, buildRef, resolutionChain,
|
|
115
|
-
listRecords,
|
|
152
|
+
listRecords, getRecordById, createRecord, upsertRecord,
|
|
153
|
+
removeRecord, restoreRecord, matchRecords,
|
|
116
154
|
bulkUpsert, bulkDelete,
|
|
117
155
|
exportCsv, importCsv, downloadBlob,
|
|
118
156
|
// UI pieces
|
|
119
157
|
RecordBrowser, RecordEditor, ScopeBreadcrumb,
|
|
120
158
|
InheritanceMarker, ResolvedPreview, BulkActionsMenu,
|
|
159
|
+
// Drafts / unsaved state
|
|
160
|
+
DirtyDraftProvider, useDirtyDrafts, useUnsavedGuard,
|
|
121
161
|
} from '@proveanything/smartlinks-utils-ui/records-admin';
|
|
122
162
|
```
|
|
123
163
|
|
|
164
|
+
Note the names: it's `getRecordById` (not `getRecordByRef`) and `removeRecord`
|
|
165
|
+
(not `deleteRecord`). Records are addressed by UUID `id` internally; refs
|
|
166
|
+
(`product:abc123`) are only used for display / breadcrumb / URL purposes.
|
|
167
|
+
|
|
124
168
|
### `useResolvedRecord` hook
|
|
125
169
|
|
|
170
|
+
Use on the **public widget side** when the app shows one answer for the
|
|
171
|
+
current product (singleton cardinality). Walks the inheritance chain
|
|
172
|
+
server-side and returns the first match.
|
|
173
|
+
|
|
126
174
|
```ts
|
|
127
175
|
import { useResolvedRecord } from '@proveanything/smartlinks-utils-ui/records-admin';
|
|
128
176
|
|
|
129
|
-
const { data, source, isLoading } =
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
177
|
+
const { data, source, sourceRef, recordId, facetRule, isLoading, error } =
|
|
178
|
+
useResolvedRecord<NutritionData>({
|
|
179
|
+
SL,
|
|
180
|
+
appId,
|
|
181
|
+
recordType: 'nutrition',
|
|
182
|
+
collectionId,
|
|
183
|
+
productId,
|
|
184
|
+
variantId, // optional
|
|
185
|
+
batchId, // optional
|
|
186
|
+
proofId, // optional
|
|
187
|
+
// recordId, // optional — direct UUID lookup, bypasses inheritance
|
|
188
|
+
});
|
|
189
|
+
// source: 'self' | 'proof' | 'batch' | 'variant' | 'product' | 'facet' | 'universal' | 'empty'
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
- `source` — which scope the winning record came from. `'self'` is returned
|
|
193
|
+
when you passed an explicit `recordId`; `'empty'` when nothing matched.
|
|
194
|
+
- `sourceRef` — the ref of the matched record (e.g. `product:abc123`).
|
|
195
|
+
- `recordId` — the UUID of the matched record.
|
|
196
|
+
- `facetRule` — present when the match came from a rule-targeted record.
|
|
197
|
+
|
|
198
|
+
Pass `recordId` directly when you already know the UUID (deep links, rule
|
|
199
|
+
records) — the hook will skip the inheritance walk entirely.
|
|
200
|
+
|
|
201
|
+
### `useCollectedRecords` hook
|
|
202
|
+
|
|
203
|
+
Use on the **public widget side** when the app shows many answers
|
|
204
|
+
(collection cardinality — FAQs, recipes, care tips). Returns every matching
|
|
205
|
+
record across the chain, most-specific first.
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
import { useCollectedRecords } from '@proveanything/smartlinks-utils-ui/records-admin';
|
|
209
|
+
|
|
210
|
+
const { items, isLoading, error } = useCollectedRecords<FaqEntry>({
|
|
211
|
+
SL, appId, collectionId,
|
|
212
|
+
recordType: 'faq',
|
|
134
213
|
productId,
|
|
135
|
-
|
|
136
|
-
batchId, // optional
|
|
137
|
-
proofId, // optional
|
|
214
|
+
// sort: { kind: 'field', field: 'order', direction: 'asc' },
|
|
138
215
|
});
|
|
139
|
-
//
|
|
216
|
+
// items: CollectedRecord<FaqEntry>[] — each has { data, scope, ref, depth }
|
|
140
217
|
```
|
|
141
218
|
|
|
142
|
-
|
|
219
|
+
`depth: 0` is the most-specific match. Default sort is by specificity
|
|
220
|
+
descending; pass `{ kind: 'field', field, direction }` to sort by a payload
|
|
221
|
+
field instead (with specificity as a stable tiebreak).
|
|
222
|
+
|
|
223
|
+
### `useResolveAllRecords` hook
|
|
224
|
+
|
|
225
|
+
When you need every record of every declared type that applies to a context —
|
|
226
|
+
rare, mostly used in executors and SEO surfaces.
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
import { useResolveAllRecords } from '@proveanything/smartlinks-utils-ui/records-admin';
|
|
230
|
+
|
|
231
|
+
const { entries, isLoading } = useResolveAllRecords({
|
|
232
|
+
SL, collectionId, appId,
|
|
233
|
+
context: { productId, facets: { brand: 'acme' } },
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### `useRulePreview` hook
|
|
238
|
+
|
|
239
|
+
Wire into `<FacetRuleEditor>` (or any custom rule UI) to show a live
|
|
240
|
+
"matches N products" count as the rule is edited. Debounced — safe to call
|
|
241
|
+
on every keystroke.
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
import { useRulePreview } from '@proveanything/smartlinks-utils-ui/records-admin';
|
|
245
|
+
|
|
246
|
+
const preview = useRulePreview({
|
|
247
|
+
SL, collectionId, appId,
|
|
248
|
+
rule, // FacetRule | null
|
|
249
|
+
// limit: 20,
|
|
250
|
+
// debounceMs: 350,
|
|
251
|
+
});
|
|
252
|
+
// preview: {
|
|
253
|
+
// totalMatches: number | null;
|
|
254
|
+
// sampleProductIds: string[];
|
|
255
|
+
// isLoading: boolean;
|
|
256
|
+
// isStale: boolean;
|
|
257
|
+
// error: Error | null;
|
|
258
|
+
// }
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Pass `preview` straight into `<FacetRuleEditor preview={preview} />`.
|
|
262
|
+
|
|
263
|
+
### `useScopeProbe` hook
|
|
264
|
+
|
|
265
|
+
Reports whether a collection has variants/batches enabled, so the shell
|
|
266
|
+
(or your own UI) can hide the corresponding tabs without flicker.
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
import { useScopeProbe } from '@proveanything/smartlinks-utils-ui/records-admin';
|
|
270
|
+
|
|
271
|
+
const { hasVariants, hasBatches, isLoading } = useScopeProbe({
|
|
272
|
+
SL, collectionId,
|
|
273
|
+
});
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
It's a thin wrapper around `SL.collection.get(collectionId).variants /.batches`
|
|
277
|
+
(SDK ≥ 1.9). It does **not** report per-scope record status — that's handled
|
|
278
|
+
internally by the shell's status pills.
|
|
143
279
|
|
|
144
280
|
### `parseRef` / `buildRef` utilities
|
|
145
281
|
|
|
@@ -147,13 +283,50 @@ Walks the resolution chain defined in [records-admin-pattern.md §4](records-adm
|
|
|
147
283
|
import { parseRef, buildRef } from '@proveanything/smartlinks-utils-ui/records-admin';
|
|
148
284
|
|
|
149
285
|
const parsed = parseRef('variant:prod_abc:var_500ml');
|
|
150
|
-
// → { kind: 'variant', productId: 'prod_abc', variantId: 'var_500ml' }
|
|
286
|
+
// → { kind: 'variant', productId: 'prod_abc', variantId: 'var_500ml', raw: '...' }
|
|
151
287
|
|
|
152
|
-
const ref = buildRef({ kind: '
|
|
153
|
-
// → '
|
|
288
|
+
const ref = buildRef({ kind: 'product', productId: 'prod_abc', raw: '' });
|
|
289
|
+
// → 'product:prod_abc'
|
|
154
290
|
```
|
|
155
291
|
|
|
156
|
-
|
|
292
|
+
Refs are for display, breadcrumbs, and URLs. Records are addressed by UUID
|
|
293
|
+
`id` everywhere internally.
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Facet Rule Editor
|
|
298
|
+
|
|
299
|
+
A standalone facet-rule builder. The `<RecordsAdminShell>` embeds this
|
|
300
|
+
automatically when `'rule'` is in `scopes` and the user opens a rule-targeted
|
|
301
|
+
record — reach for it directly only when you need rule editing elsewhere
|
|
302
|
+
(e.g. a settings page, a conditions sidebar).
|
|
303
|
+
|
|
304
|
+
```tsx
|
|
305
|
+
import { FacetRuleEditor } from '@proveanything/smartlinks-utils-ui/facet-rule-editor';
|
|
306
|
+
import { useRulePreview } from '@proveanything/smartlinks-utils-ui/records-admin';
|
|
307
|
+
|
|
308
|
+
const preview = useRulePreview({ SL, collectionId, appId, rule });
|
|
309
|
+
|
|
310
|
+
<FacetRuleEditor
|
|
311
|
+
value={rule}
|
|
312
|
+
onChange={setRule}
|
|
313
|
+
collectionId={collectionId} // lazy-fetches facet definitions via SL.facets.publicList
|
|
314
|
+
preview={preview} // optional — wire from useRulePreview
|
|
315
|
+
onClear={() => setRule(null)} // optional — renders a "Remove rule" affordance
|
|
316
|
+
/>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Props:
|
|
320
|
+
|
|
321
|
+
- `value: FacetRule | null` / `onChange: (next: FacetRule | null) => void` — controlled
|
|
322
|
+
- `facets?: FacetOption[]` — supply directly, **or** pass `collectionId` and the editor lazy-fetches via `SL.facets.publicList`
|
|
323
|
+
- `getFacets?: (collectionId: string) => Promise<FacetOption[]>` — override the lazy-fetcher
|
|
324
|
+
- `preview?` — `{ totalMatches, sampleProductIds?, isLoading?, isStale?, error? }` (matches the `useRulePreview` return shape)
|
|
325
|
+
- `readOnly?`, `onClear?`, `title?`, `description?`, `className?`
|
|
326
|
+
|
|
327
|
+
Free-text facet entry is **not** supported — admins must pick from defined facets.
|
|
328
|
+
|
|
329
|
+
See [records-admin-pattern.md §4](records-admin-pattern.md#4-admin-side----recordsadminshell-the-only-thing-you-should-be-writing) for the standalone usage example.
|
|
157
330
|
|
|
158
331
|
---
|
|
159
332
|
|
|
@@ -172,8 +345,20 @@ import { AssetPicker } from '@proveanything/smartlinks-utils-ui/asset-picker';
|
|
|
172
345
|
/>
|
|
173
346
|
```
|
|
174
347
|
|
|
348
|
+
Scope shape:
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
type AssetScope =
|
|
352
|
+
| { type: 'collection'; collectionId: string }
|
|
353
|
+
| { type: 'product'; collectionId: string; productId: string }
|
|
354
|
+
| { type: 'proof'; collectionId: string; productId: string; proofId: string };
|
|
355
|
+
```
|
|
356
|
+
|
|
175
357
|
**What it does:**
|
|
176
|
-
|
|
358
|
+
|
|
359
|
+
- Browses assets at **collection**, **product**, or **proof** scope
|
|
360
|
+
- Optional `productScope` prop adds a second tab so users can pick from product-level assets while editing at collection scope (or vice versa)
|
|
361
|
+
- Optional `appId` prop stamps every upload with the owning app and adds "This app" / "All in collection" pill tabs with provenance badges on assets owned by other apps
|
|
177
362
|
- Four ingest paths: file upload, URL import, clipboard paste (with rename preview), and selection from existing assets
|
|
178
363
|
- Inline mode for embedding in a panel; dialog mode for modal pickers
|
|
179
364
|
- Double-click a tile to confirm instantly
|
|
@@ -193,8 +378,20 @@ import { IconPicker } from '@proveanything/smartlinks-utils-ui/icon-picker';
|
|
|
193
378
|
/>
|
|
194
379
|
```
|
|
195
380
|
|
|
381
|
+
`onSelect` receives:
|
|
382
|
+
|
|
383
|
+
```ts
|
|
384
|
+
interface IconSelection {
|
|
385
|
+
name: string; // full CSS class, e.g. 'fa-solid fa-heart'
|
|
386
|
+
family: 'classic' | 'duotone' | 'brands';
|
|
387
|
+
style: 'solid' | 'regular' | 'light' | null; // null for brands
|
|
388
|
+
label?: string;
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
196
392
|
**What it does:**
|
|
197
|
-
|
|
393
|
+
|
|
394
|
+
- Searches **Font Awesome 7 Pro** (uses the shared kit)
|
|
198
395
|
- Family hierarchy: Classic (Solid / Regular / Light), Duotone, and Brands
|
|
199
396
|
- Search-first with a background catalogue crawler — first results appear instantly, the full index fills in behind
|
|
200
397
|
- Auto-switches families intelligently (e.g. brand searches surface brand icons even when "Classic" is selected)
|
|
@@ -229,6 +426,7 @@ import { FontPicker } from '@proveanything/smartlinks-utils-ui/font-picker';
|
|
|
229
426
|
```
|
|
230
427
|
|
|
231
428
|
**What it does:**
|
|
429
|
+
|
|
232
430
|
- Full **Google Fonts** catalogue plus any **custom fonts** uploaded for the brand (stored via `appConfiguration` under `customFonts`)
|
|
233
431
|
- Upload zone auto-detects weight/style from the filename (e.g. `MyFont-BoldItalic.woff2`)
|
|
234
432
|
- Returns a `FontSelection` with `family`, `cssFontFamily`, and a ready-to-inject `loadSnippet` (`<link>` for Google fonts, `@font-face` CSS for custom uploads)
|
|
@@ -251,12 +449,19 @@ import { ConditionsEditor } from '@proveanything/smartlinks-utils-ui/conditions-
|
|
|
251
449
|
```
|
|
252
450
|
|
|
253
451
|
**What it does:**
|
|
452
|
+
|
|
254
453
|
- Recursive AND / OR group builder — nest conditions to any depth
|
|
255
454
|
- **12 condition types:** Version, Country, Value, User, Date, Device, Tag, Facet, Geofence, Product, Item Status, Condition Reference
|
|
256
455
|
- **Facet condition** auto-fetches definitions from `facets.publicList(collectionId, { includeValues: true })` when only `collectionId` is passed (SDK ≥ 1.9.20)
|
|
257
456
|
- **Country picker** is a searchable multi-select with removable ISO 3166-1 chips and a "Use regions" toggle
|
|
258
457
|
- Renders correctly inside iframe contexts — avoids `overflow-hidden` so dropdowns escape their cards
|
|
259
458
|
|
|
459
|
+
> `ConditionsEditor` and `FacetRuleEditor` solve different problems.
|
|
460
|
+
> `FacetRuleEditor` is a focused AND-of-OR rule over facets only, used to
|
|
461
|
+
> target which records apply to which products. `ConditionsEditor` is the
|
|
462
|
+
> full recursive logical builder over 12 condition types, used for runtime
|
|
463
|
+
> gating, audience segmentation, and version targeting.
|
|
464
|
+
|
|
260
465
|
---
|
|
261
466
|
|
|
262
467
|
## Tree shaking
|
|
@@ -271,13 +476,24 @@ import { AssetPicker } from '@proveanything/smartlinks-utils-ui/asset-picker';
|
|
|
271
476
|
import { AssetPicker } from '@proveanything/smartlinks-utils-ui';
|
|
272
477
|
```
|
|
273
478
|
|
|
274
|
-
If you use subpath imports, import `styles.css` separately — subpaths do not
|
|
479
|
+
If you use subpath imports, import `styles.css` separately — subpaths do not
|
|
480
|
+
pull it in automatically.
|
|
481
|
+
|
|
482
|
+
Available subpaths:
|
|
483
|
+
|
|
484
|
+
- `/records-admin`
|
|
485
|
+
- `/facet-rule-editor`
|
|
486
|
+
- `/asset-picker`
|
|
487
|
+
- `/icon-picker`
|
|
488
|
+
- `/font-picker`
|
|
489
|
+
- `/conditions-editor`
|
|
490
|
+
- `/styles.css`
|
|
275
491
|
|
|
276
492
|
---
|
|
277
493
|
|
|
278
494
|
## Relationship to the core SDK
|
|
279
495
|
|
|
280
|
-
```
|
|
496
|
+
```text
|
|
281
497
|
@proveanything/smartlinks ← data layer (records, config, interactions, …)
|
|
282
498
|
↑
|
|
283
499
|
@proveanything/smartlinks-utils-ui ← UI layer (components, hooks, admin shells)
|
|
@@ -291,4 +507,4 @@ your microapp ← domain logic and custom forms
|
|
|
291
507
|
|
|
292
508
|
- [records-admin-pattern.md](records-admin-pattern.md) — the data contract that `RecordsAdminShell` implements
|
|
293
509
|
- [building-react-components.md](building-react-components.md) — dual-mode rendering rules that apply to all components
|
|
294
|
-
- [app-manifest.md](app-manifest.md) — the `records` manifest block that drives tab generation
|
|
510
|
+
- [app-manifest.md](app-manifest.md) — the `records` manifest block that drives tab generation
|