efficient-easel 26.3.0 → 26.4.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,529 @@
1
+ import { W as WordFit } from './wordFit-7XpmhcaW.js';
2
+ import { ReactNode } from 'react';
3
+ import { P as PanelWords } from './words-BrN08Fup.js';
4
+
5
+ /**
6
+ * The settings-panel primitives — the shared grammar BOTH editors' inspector
7
+ * panels are built from (owner-approved design, 2026-08-20).
8
+ *
9
+ * Every one is CONTROLLED and hook-free: the host owns which groups are open, exactly as the rail's host owns selection. That is what
10
+ * keeps this a directive-free leaf a Next-16 server component can import.
11
+ */
12
+ /**
13
+ * WHERE THIS STRIP'S WORDS STOP FITTING — the one thing the shell cannot know
14
+ * about a segmented control, measured by the consumer that owns the words.
15
+ *
16
+ * The same shape the top-bar rail hands the same rule (`WordFit`, in
17
+ * `src/fit/wordFit.ts`, where both numbers are described).
18
+ *
19
+ * MEASURE THEM, do not count letters. The chip's font resolves to SF on a Mac
20
+ * and Segoe UI on Windows, so one table of letter widths would be wrong on most
21
+ * machines; measured against real labels a letter-count guess is out by up to
22
+ * 7%, which is enough to drop a word that fits. Render the strip and read the
23
+ * label spans — `tests/fixtures/segmented-fit.html` does exactly that and
24
+ * prints both numbers. If a label is about to change and the number cannot be
25
+ * re-measured today, round it up.
26
+ */
27
+ type SettingsSegmentedFit = WordFit;
28
+ /**
29
+ * WHAT AN OPTION IS CALLED, and the one rule that governs it: EVERY option has a
30
+ * name, and the name is a string (card 66303).
31
+ *
32
+ * `label` is spent three ways — the visible mark, `title`, and `aria-label` — so
33
+ * it cannot simply be widened to a `ReactNode` and left there: a node reaches
34
+ * the two attributes as `[object Object]`, which is a tooltip that says nothing
35
+ * and a screen reader that says worse. Nor can it be split into `icon` + `label`
36
+ * and the word left to fall away at a threshold: both spans render, so a strip
37
+ * whose mark IS its label would draw the mark AND the word beside it.
38
+ *
39
+ * So the two cases are told apart in the type, and each carries its own name:
40
+ *
41
+ * a WORDED option `{ label: 'Left' }` — the word is the name. `title` is
42
+ * optional and almost never given; this is every option that
43
+ * exists today and none of them changes.
44
+ * a DRAWN option `{ label: <AlignGlyph/>, title: 'Left' }` — the mark cannot
45
+ * be read aloud, so `title` is REQUIRED and is the name.
46
+ *
47
+ * The part panel's Alignment row is the case that forced it: three alignments
48
+ * have no honest three-letter word between them, so the label is the rule
49
+ * itself. Case is the same shape one step milder — Aa / AA / aa are letterforms,
50
+ * not the words Normal / Uppercase / Lowercase that name them. Passing `title`
51
+ * beside a WORDED label is allowed and overrides the word, which is what a
52
+ * strip whose words are abbreviations wants.
53
+ *
54
+ * ── NO CONSUMER TODAY, AND CARRIED ON PURPOSE ────────────────────────────────
55
+ *
56
+ * Written down so the next reader does not find this unused and tidy it away.
57
+ * The rows above stopped needing it while this was being built: card 66246
58
+ * (`efficient-storefront` 151afa212, merged 2026-08-31) moved the part panel's three
59
+ * tables onto `icon` + a real word — `{ id: "left", label: "Left", icon:
60
+ * <AlignLeftIcon/> }` — on the owner's ruling that an option says its name AND
61
+ * draws its glyph. Every `label` in the shop is now a string, and this arm has
62
+ * no caller anywhere. That is measured on merged main, not on a pending PR.
63
+ *
64
+ * It stays because it is the shape THE PACKAGE NEXT DOOR ALREADY USES.
65
+ * `efficient-shell` ships its own `Segmented`, and its `SegmentedOption` has
66
+ * carried `label: ReactNode` all along. Identical at the two copies installed on
67
+ * this desk — 4.63.0 under `editor-shell`, 4.67.0 under `efficient-storefront`:
68
+ *
69
+ * interface SegmentedOption<T extends string = string> {
70
+ * value: T; label: ReactNode; icon?: ReactNode; disabled?: boolean;
71
+ * }
72
+ *
73
+ * A shared control that refuses a shape its sibling accepts is a fork one level
74
+ * down, which is the fault this whole family of cards exists to stop. Matching
75
+ * it costs nothing while nothing calls it.
76
+ *
77
+ * ── WHERE THE TWO SHELLS CONVERGE, AND WHERE THEY DO NOT ─────────────────────
78
+ *
79
+ * Read this before repeating "the two shells are converged", because it is only
80
+ * true of three fields out of four, and the fourth is the one a caller touches
81
+ * most:
82
+ *
83
+ * efficient-shell editor-shell
84
+ * the label label: ReactNode label: ReactNode converged
85
+ * the glyph icon?: ReactNode icon?: ReactNode converged
86
+ * out of force disabled?: boolean disabled?: boolean converged
87
+ * ─────────────────────────────────────────────────────────────────────────
88
+ * the VALUE value: T id: V NOT converged
89
+ * T extends string V unconstrained
90
+ *
91
+ * BOTH halves of that last row are deliberate.
92
+ *
93
+ * `id` and not `value`, because this field has been called `id` since the type
94
+ * existed and renaming it would move something for every caller already using
95
+ * it — which is the one thing card 66303 was not allowed to do.
96
+ *
97
+ * `V` UNCONSTRAINED and not `T extends string`, because the shop stores a
98
+ * choice as a boolean: `reverse` at the storefront's old block config is
99
+ * `false` = image left and `true` = image right on every published page across
100
+ * every tenant. `T extends string` cannot hold it, and giving that row the house
101
+ * strip by storing "left"/"right" would be a migration of live customer data
102
+ * rather than a control change. So on the value type this package is the WIDER
103
+ * of the two, not the poorer — the opposite of the direction the rest of this
104
+ * note runs.
105
+ *
106
+ * ── AND WHERE WE DELIBERATELY GO FURTHER ─────────────────────────────────────
107
+ *
108
+ * That sibling declaration has NO name field beside its node label. A drawing
109
+ * that cannot name itself is exactly the defect the 2026-08-06 audit closed on
110
+ * the shop's strip, so the `title` here is REQUIRED — a guarantee, not a
111
+ * feature. The union makes a nameless drawing unconstructable rather than a
112
+ * documented risk; the sibling's shape permits one.
113
+ *
114
+ * One more thing that declaration settles, recorded because it was argued: its
115
+ * `disabled` is PER OPTION and it has no strip-level one at all. That is the
116
+ * independent support for the per-option field on
117
+ * `SettingsSegmentedOptionBase` — the sibling has shipped exactly that.
118
+ */
119
+ type SettingsSegmentedLabel = {
120
+ label: string;
121
+ title?: string;
122
+ } | {
123
+ label: ReactNode;
124
+ title: string;
125
+ };
126
+ interface SettingsSegmentedOptionBase<V = string> {
127
+ /**
128
+ * The VALUE this option writes, as `value` and `onChange` name it.
129
+ *
130
+ * `V` and not `string` since card 66303. A weight is a NUMBER — the storefront
131
+ * part panel's row is `[400, 500, 600, 700]` — and "Image side" is a BOOLEAN
132
+ * on every published page across every tenant, so giving those rows the house
133
+ * strip by storing "left"/"right" would be a migration of live customer data
134
+ * rather than a control change. `V` defaults to `string`, so every existing
135
+ * declaration means exactly what it did.
136
+ */
137
+ id: V;
138
+ /**
139
+ * A 16px glyph from `efficient-easel/icons`, so the option can spend less width
140
+ * than its word needs when the panel is dragged narrow.
141
+ *
142
+ * Optional, and the strip is unchanged without it: an option with no glyph
143
+ * has nothing to fall back to, so it keeps its word at every width. Pass
144
+ * glyphs for ALL the options or none of them — half words and half glyphs
145
+ * reads as a rendering fault, not as a decision.
146
+ *
147
+ * NOT the same thing as a DRAWN label. An `icon` is a glyph BESIDE the word,
148
+ * and the word is what it falls back from; a drawn label replaces the word
149
+ * altogether and is named by its `title`. An option wanting the second one
150
+ * puts the mark in `label`, not here.
151
+ */
152
+ icon?: ReactNode;
153
+ /**
154
+ * This choice exists but cannot be taken right now.
155
+ *
156
+ * Renders the button's own `disabled`, not a fade over it, and that is the
157
+ * whole point of it living here: this component owns the `<button>`, so a host
158
+ * dimming a parent instead leaves every segment focusable and announcing as
159
+ * ENABLED — the exact defect the 2026-08-06 audit and card 66034 closed.
160
+ *
161
+ * Dimmed, NEVER hidden, and the pressed segment still reads as pressed: the
162
+ * merchant's saved answer is still their answer, it simply is not in force.
163
+ * Defaults absent, so nothing an existing caller renders moves.
164
+ *
165
+ * NO CONSUMER TODAY, and it is carried on purpose rather than by oversight —
166
+ * so the next reader does not find it unused and tidy it away. The case it is
167
+ * for is one row away: card 66246 draws Weight as four letterform glyphs, and
168
+ * a merchant's typeface may only carry two real weights (measured there —
169
+ * Helvetica, Georgia, Verdana and Impact render Regular and Medium as the same
170
+ * letter, and Semibold and Bold as the same letter). That is a choice the
171
+ * strip CAN offer and the font cannot honour, which is this field and not the
172
+ * strip-level one beside it.
173
+ */
174
+ disabled?: boolean;
175
+ }
176
+ /** One option in a segmented control. */
177
+ type SettingsSegmentedOption<V = string> = SettingsSegmentedOptionBase<V> & SettingsSegmentedLabel;
178
+ /**
179
+ * A SEGMENTED CONTROL — the shell's chip, with the other promise.
180
+ *
181
+ * A tab strip (the shell's `Tabs`) is a TABLIST: its chips switch a panel,
182
+ * which is what `role="tab"` promises. These chips switch nothing. They write
183
+ * a value and the panel stays where it is, so they are pressed buttons in a
184
+ * group, and the
185
+ * marker is `aria-pressed` — the same one `efficient-storefront`'s own `Segmented`
186
+ * has carried since the 2026-08-06 audit.
187
+ *
188
+ * NOT `radiogroup` / `aria-checked`, and the reason is the keyboard rather than
189
+ * the taxonomy. `role="radio"` promises roving focus: one Tab stop for the set,
190
+ * arrows moving between the options and selecting as they go. This is a
191
+ * hook-free controlled leaf — it owns no focus and runs no key handler, and a
192
+ * host cannot add one to a `<button>` it did not render. Radio roles over
193
+ * plain tab-stopped buttons would promise a keyboard that is not there, which
194
+ * is the same defect as `role="tab"` over no panel.
195
+ */
196
+ interface SettingsSegmentedProps<V = string> {
197
+ /**
198
+ * `readonly` so a panel may declare its option table `as const` and keep the
199
+ * literal value types — which is how every one of them is already written.
200
+ * A widening only: a mutable array still binds.
201
+ */
202
+ options: readonly SettingsSegmentedOption<V>[];
203
+ /**
204
+ * The `id` of the option currently set.
205
+ *
206
+ * A value matching NO option presses nothing, and that is a state a caller
207
+ * relies on rather than an edge: the part panel hands `""` when the selection
208
+ * is mixed across several parts. The match is an EQUALITY and never a
209
+ * truthiness test — `false` is a real answer ("image left"), and `value ||
210
+ * fallback` collapses it into the other one.
211
+ */
212
+ value: V;
213
+ onChange: (next: V) => void;
214
+ /**
215
+ * What the group is called — "Weight", "Alignment". Required: a group of
216
+ * pressed buttons with no name is a set a screen reader cannot introduce.
217
+ */
218
+ ariaLabel: string;
219
+ /**
220
+ * Where this strip's words stop fitting. Optional, and WITHOUT IT NOTHING
221
+ * DROPS: an unmeasured strip keeps every word at every width, because the
222
+ * only alternative is guessing where to hide one. Give the options glyphs and
223
+ * this too, or the glyphs have nothing to fall back at.
224
+ */
225
+ fit?: SettingsSegmentedFit;
226
+ /**
227
+ * The whole strip exists but cannot apply right now, because another setting
228
+ * has taken the decision away — the Header's "Text colour at the top" under a
229
+ * pinned Background (card 66216).
230
+ *
231
+ * Every segment goes out of force AND the track says so, so the control goes
232
+ * quiet as one thing rather than as three identically faded chips. Per-option
233
+ * `disabled` is the other half of this: use that for a choice the strip cannot
234
+ * offer, and this for a strip the panel cannot offer.
235
+ *
236
+ * Dimmed, never hidden — a control that disappears reads as a feature that was
237
+ * removed, and leaves the merchant no way to see what to change to get it
238
+ * back. Defaults false, so every existing caller renders byte for byte as it
239
+ * did.
240
+ */
241
+ disabled?: boolean;
242
+ className?: string;
243
+ }
244
+ interface SettingsGroupProps {
245
+ /** The group's name — "Items", "Spacing", "Typeface". */
246
+ title: string;
247
+ /** Open state lives with the host, so it can persist it. */
248
+ open: boolean;
249
+ onToggle: (open: boolean) => void;
250
+ /**
251
+ * How many settings in this group differ from their default. Shown as a chip
252
+ * on the pill so a FOLDED group still admits it holds changes.
253
+ */
254
+ count?: number;
255
+ /**
256
+ * The heading level the title draws at, in the HOST's outline (7.2.0). The
257
+ * fold button sits inside an `h{level}`, the WAI-ARIA accordion pattern.
258
+ * Defaults to 3, a group under a panel's `h2` name.
259
+ */
260
+ level?: 2 | 3 | 4 | 5 | 6;
261
+ /** Ties the pill to its body for assistive tech (`aria-controls`). One DOM
262
+ * id: no whitespace (the body's id is this plus `-body`). */
263
+ id?: string;
264
+ children: ReactNode;
265
+ className?: string;
266
+ }
267
+ /**
268
+ * A group heading with nothing folded under it — see `SettingsGroupHead`.
269
+ *
270
+ * Deliberately NOT `SettingsGroupProps` minus a few fields: a heading has no
271
+ * open state to own, nothing to toggle, and no count, because a heading hides
272
+ * nothing that a count would have to admit to.
273
+ */
274
+ interface SettingsGroupHeadProps {
275
+ /** The heading's name — "Items", "Spacing", "Typeface". */
276
+ title: string;
277
+ /**
278
+ * Which heading level this is in the HOST's outline. Defaults to 3, the level
279
+ * a panel section sits at under a panel title; only the host knows what sits
280
+ * above it.
281
+ */
282
+ level?: 2 | 3 | 4 | 5 | 6;
283
+ /** A DOM id, so the rows it heads can point `aria-labelledby` at it. */
284
+ id?: string;
285
+ className?: string;
286
+ }
287
+ interface SettingsGroupSubProps {
288
+ /** The heading's name — "Content", "Background", "Image". */
289
+ title: string;
290
+ /**
291
+ * Which heading level this is in the HOST's outline. Defaults to 4 — one under
292
+ * `SettingsGroupHead`'s 3, because this tier sits under that pill. Only the
293
+ * host knows what is actually above it.
294
+ */
295
+ level?: 2 | 3 | 4 | 5 | 6;
296
+ /** A DOM id, so the rows it heads can point `aria-labelledby` at it. */
297
+ id?: string;
298
+ className?: string;
299
+ }
300
+ interface SettingsFieldProps {
301
+ label: string;
302
+ /**
303
+ * An `id` for the label text, so a control that cannot be reached by
304
+ * `htmlFor` can still be named by it.
305
+ *
306
+ * That is the boolean row: `htmlFor` associates a <label> with a form
307
+ * control, and a switch is a <button>. Give the label an id here and hand the
308
+ * same id to the switch's `ariaLabelledBy`, and the accessible name and the
309
+ * visible name are the same string.
310
+ */
311
+ labelId?: string;
312
+ /** An 18px glyph from `efficient-easel/icons` — one meaning, one SVG. */
313
+ icon?: ReactNode;
314
+ /** The current value, right-aligned on the label row ("34 px", "style"). */
315
+ value?: ReactNode;
316
+ /** One quiet line under the control. Use it instead of a clause-long label. */
317
+ hint?: ReactNode;
318
+ /** Marks the row as changed from its default: the label takes the accent. */
319
+ changed?: boolean;
320
+ /** Set when the control is a single form element, so the label points at it. */
321
+ htmlFor?: string;
322
+ /**
323
+ * A trailing control on the label row — `SettingsDefaultReset` (Default /
324
+ * Reset), or the SWITCH of a boolean row (card 8801, Option A: a yes-or-no
325
+ * setting is one line, not two, so the switch takes the spot a value like
326
+ * "34 px" normally sits in).
327
+ */
328
+ action?: ReactNode;
329
+ /**
330
+ * The control itself, on its own line under the label.
331
+ *
332
+ * OPTIONAL since card 8801, and only so a boolean row can put its switch in
333
+ * `action` and render no control line at all. Omitting it is not a way to
334
+ * draw a label on its own: a field with NO children and NO action has no
335
+ * control on either slot, so it renders nothing rather than leaving an empty
336
+ * row behind. Every field that passes a control is unaffected.
337
+ */
338
+ children?: ReactNode;
339
+ className?: string;
340
+ }
341
+ /**
342
+ * `SettingsDefaultReset` — the end of a label line: a ↺ button while the value
343
+ * is changed, an empty 22px slot while it follows its default (owner
344
+ * 2026-10-06; option E, final 2026-10-07).
345
+ */
346
+ interface SettingsDefaultResetProps {
347
+ /** The field's visible label. The ↺ button is named after it ("Reset Gap"). */
348
+ label: string;
349
+ /** Is the value changed from its default? The host's to say. */
350
+ changed: boolean;
351
+ /** Clear the value back to its default. The part writes nothing itself. */
352
+ onReset: () => void;
353
+ /**
354
+ * What the default IS, in the host's words — "24px from Site styles". The ↺
355
+ * says it: in its tooltip ("Reset Gap (back to 24px from Site styles)") and
356
+ * as its accessible description, while its NAME stays "Reset Gap" (GG
357
+ * 2026-10-07, E2). Read it live; a baked value goes stale. Nothing shows it
358
+ * while the setting is unchanged: the slot is empty then.
359
+ */
360
+ detail?: string;
361
+ /** Read-only (a sent campaign): ↺ is drawn, and cannot be pressed. */
362
+ disabled?: boolean;
363
+ /**
364
+ * Rare: something to press while the setting is UNCHANGED, for a row a
365
+ * merchant cannot leave by hand. The website's type sizes are the case (GG
366
+ * 2026-10-06, Q3): their default can be a responsive `clamp()` no slider
367
+ * position stores, so one press keeps the section's own built-in size.
368
+ * `icon` is drawn in the 22px slot; `title` is its name and its tooltip.
369
+ * Left out, an unchanged row draws nothing — the usual case.
370
+ */
371
+ defaultAction?: {
372
+ onPress: () => void;
373
+ title: string;
374
+ icon: ReactNode;
375
+ };
376
+ /** The host's words, from `useWords('panel', …)`. English when left out. */
377
+ words?: Partial<Pick<PanelWords, 'followsDefault' | 'reset' | 'resetName' | 'backToDefault' | 'resetTitle'>>;
378
+ className?: string;
379
+ }
380
+ /**
381
+ * A switch has no text of its own, so without one of these it announces
382
+ * "switch, on" and nothing else. The union makes an unnamed switch a COMPILE
383
+ * error rather than a documented risk — both editors are about to grow six of
384
+ * these per panel, and every one is a chance to forget.
385
+ *
386
+ * Prefer `ariaLabelledBy` inside a `SettingsField`: point it at the field's
387
+ * `labelId` and the accessible name IS the visible label, one string doing two
388
+ * jobs, so the two cannot drift apart. `ariaLabel` is for a switch standing on
389
+ * its own with no visible label to point at.
390
+ *
391
+ * `?: never` on the other arm is what makes the two mutually exclusive while
392
+ * still letting the component destructure both names.
393
+ */
394
+ type SettingsSwitchLabel = {
395
+ ariaLabel: string;
396
+ ariaLabelledBy?: never;
397
+ } | {
398
+ ariaLabelledBy: string;
399
+ ariaLabel?: never;
400
+ };
401
+ interface SettingsSwitchBaseProps {
402
+ /** Whether the setting is on. The HOST owns it; the switch never flips itself. */
403
+ checked: boolean;
404
+ /** Asked to flip. `next` is the value the merchant wants, not the current one. */
405
+ onChange: (next: boolean) => void;
406
+ disabled?: boolean;
407
+ id?: string;
408
+ className?: string;
409
+ }
410
+ type SettingsSwitchProps = SettingsSwitchBaseProps & SettingsSwitchLabel;
411
+ /**
412
+ * A DRILL-IN ROW — a name, a one-line preview, and a chevron that says pressing
413
+ * it takes the panel down a level (card 8819).
414
+ *
415
+ * The storefront editor's text editing becomes a drill-down: the section screen
416
+ * lists a section's text parts as these rows, and pressing one scopes the
417
+ * inspector to that one part's editor. It lives in the shell for the reason the
418
+ * rest of this grammar does — the campaign designer draws the same list, and a
419
+ * second hand-rolled copy is exactly how the two editors drift.
420
+ *
421
+ * CONTROLLED and hook-free like the other four: it holds no open/closed state of
422
+ * its own. Pressing it calls `onOpen`, and the HOST decides what drilling down
423
+ * means, because only the host owns the panel's navigation.
424
+ */
425
+ interface SettingsDrillProps {
426
+ /** The part's name — the loud primary line ("Heading", "Slide name"). */
427
+ name: string;
428
+ /**
429
+ * One line of what the part reads as, shown quiet under the name and clipped
430
+ * with an ellipsis. Optional: a row can be a name alone, and an absent preview
431
+ * draws no empty second line rather than a blank one.
432
+ */
433
+ preview?: ReactNode;
434
+ /**
435
+ * Mark the PREVIEW as belonging to a part with no place on the page — an
436
+ * identifier the eye never draws (a `Slide name`), reachable only from this
437
+ * list. It reads quiet and italic. The mark is on the preview, not the row:
438
+ * the name still reads as a name.
439
+ */
440
+ offPage?: boolean;
441
+ /**
442
+ * A trailing glyph before the chevron — the "hidden on the page" eye is the
443
+ * standing case. A ReactNode, not a boolean, so the host owns the icon and its
444
+ * accessible name (a bare glyph says nothing to a screen reader on its own).
445
+ */
446
+ status?: ReactNode;
447
+ /** Asked to open. The host owns what drilling down does. */
448
+ onOpen: () => void;
449
+ /** A DOM id for the row button, so a host can point a label at it. */
450
+ id?: string;
451
+ className?: string;
452
+ }
453
+ /**
454
+ * A FULL-WIDTH ACTION BUTTON that sits under a group's rows and COMMITS.
455
+ *
456
+ * "Apply to all cards" is the first caller: the merchant has styled one card and
457
+ * wants the other eleven to match. That is a WRITE, and a write is why this is
458
+ * not `SettingsDrill` with the chevron taken off — see the docblock on
459
+ * `SettingsAction` for the whole of that argument.
460
+ *
461
+ * Controlled and hook-free like every primitive here. The shell owns the look
462
+ * and the notice (its `toast`); the host owns the words and the write.
463
+ */
464
+ interface SettingsActionBaseProps {
465
+ /**
466
+ * What the button says — "Apply to all cards". The package supplies NO
467
+ * default, the way the shell's `CornersInput` invents no corner names: the
468
+ * words belong to the section that knows what it is about to rewrite.
469
+ */
470
+ label: ReactNode;
471
+ /**
472
+ * Pressed. The write is the host's; this component performs none of it. An
473
+ * async write returns its promise, and the toast waits for it to resolve.
474
+ * A union of two function types, not `() => void | Promise<void>`: that
475
+ * form would refuse a sync callback that returns a value (a `boolean` from a
476
+ * dispatch), which `() => void` has always accepted.
477
+ */
478
+ onPress: (() => void) | (() => Promise<void>);
479
+ /**
480
+ * The press is not available. A REAL `<button disabled>`, dimmed and still
481
+ * there — never hidden. A control that vanishes leaves a merchant looking for
482
+ * something they remember being there, and hiding it also takes away the one
483
+ * place the reason can be read. Say the reason in {@link
484
+ * SettingsActionNote.note}.
485
+ */
486
+ disabled?: boolean;
487
+ /**
488
+ * What the shell's toast says once {@link onPress} has landed — "Applied to
489
+ * 12 cards". The host's words, and the one notice of the press: the button
490
+ * keeps {@link label} and its look (Lewis 2026-10-30, one notice style in the
491
+ * whole editor). A plain string, because a toast draws text. Required: this
492
+ * button rewrites things that are not on screen, so a press that reports
493
+ * nothing would leave the merchant guessing.
494
+ */
495
+ doneLabel: string;
496
+ /** A glyph before the words. `aria-hidden`: the label already names the press. */
497
+ icon?: ReactNode;
498
+ className?: string;
499
+ }
500
+ /**
501
+ * The note, and the id it needs to reach anybody.
502
+ *
503
+ * `note` is where "every card already matches" or "this rewrites all 12 cards"
504
+ * goes, and it is wired to the button by `aria-describedby` — which needs a DOM
505
+ * id, and this package is hook-free so it cannot invent one (no `useId`). The
506
+ * union makes the id a COMPILE error rather than a silent hole: pass a note and
507
+ * you must pass an `id`.
508
+ *
509
+ * ⚠️ THIS IS THE REASON THE CONTROL IS HERE RATHER THAN IN THE SHOP.
510
+ * `efficient-storefront`'s own `components/AddChildRow.tsx` explains its disabled
511
+ * button with a `title`, and a `title` on a DISABLED button reaches no screen
512
+ * reader and no keyboard — the tooltip needs a hover the control no longer
513
+ * answers. That hole cannot be patched from the shop's side while the shop draws
514
+ * its own button, so the button moves here and brings the wiring with it.
515
+ *
516
+ * `?: never` on the other arm is what makes the two mutually exclusive while
517
+ * still letting the component destructure both names — the same shape
518
+ * {@link SettingsSwitchLabel} uses.
519
+ */
520
+ type SettingsActionNote = {
521
+ note: ReactNode;
522
+ id: string;
523
+ } | {
524
+ note?: never;
525
+ id?: string;
526
+ };
527
+ type SettingsActionProps = SettingsActionBaseProps & SettingsActionNote;
528
+
529
+ export type { SettingsDefaultResetProps as S, SettingsSegmentedProps as a, SettingsGroupProps as b, SettingsGroupHeadProps as c, SettingsGroupSubProps as d, SettingsFieldProps as e, SettingsSwitchProps as f, SettingsDrillProps as g, SettingsActionProps as h, SettingsActionBaseProps as i, SettingsActionNote as j, SettingsSegmentedFit as k, SettingsSegmentedOption as l, SettingsSwitchBaseProps as m, SettingsSwitchLabel as n };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "efficient-easel",
3
- "version": "26.3.0",
3
+ "version": "26.4.0",
4
4
  "description": "Efficient Easel: the EFFICIENT editors' shared engine and chrome: the saved document as pure functions, the published renderer, and the editor frame, panels, Layers tree, text grammar and type-in-place box. Published as editor-shell up to 0.66.0 and as efficient-editor up to 0.95.0.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "Efficient Pty Ltd",
@@ -1,13 +0,0 @@
1
- import{sidesStartEach as e,SidesInput as n,SidesEachToggle as t}from"./chunk-CLAADE4J.js";import{SettingsDefaultReset as o}from"./chunk-2T22IY3J.js";import{deviceField as l}from"./chunk-7GGNFLHT.js";import{noControlField as a,NoControlMark as r}from"./chunk-22I55W37.js";import{describedAs as i}from"./chunk-LX23TCTP.js";import{withStyleKind as u}from"./chunk-CWELHADJ.js";import{phoneKey as s}from"./chunk-E52W3M5I.js";import{foldMetaOf as c,FOLD_FIELD_MARKER as d}from"./chunk-ANPOR6EY.js";import{useSelectedHost as p,useEaselApi as f,useEasel as m}from"./chunk-YAFMYW4J.js";import{DEFAULT_PANEL_WORDS as b}from"./chunk-56ID6FKZ.js";import{SettingsSwitch as h}from"./chunk-OECWLNKJ.js";import{FieldSelect as g}from"./chunk-KFWNGPW3.js";import{SettingsField as v}from"./chunk-MIX7EGRG.js";import{SettingsSegmented as y}from"./chunk-DX3VILZ3.js";import{useWords as k}from"./chunk-MFIRYGHW.js";import{useState as w}from"react";import{AlignmentIcon as C,BoxJustifyIcon as x,WrapIcon as F,GapIcon as j,WidthIcon as L,PlacementIcon as O,NudgeIcon as S,DesktopIcon as P,BoxXEndIcon as E,BoxXCenterIcon as I,BoxXStartIcon as A,PaddingAllIcon as K,BoxYBetweenIcon as R,BoxYStretchIcon as W,BoxYEndIcon as B,BoxYCenterIcon as M,BoxYStartIcon as G,BoxXBetweenIcon as N,BoxXStretchIcon as T,BoxItemAlignmentIcon as V}from"efficient-shell/icons/efficient";import{Slider as J,textAlignOptions as D,AlignmentGrid as $,PositionInput as Y}from"efficient-shell/ui";import{jsx as _,Fragment as X,jsxs as z}from"react/jsx-runtime";var H=e=>e;function Z(){return null}function U(e){if(!e.trim())throw new Error("noGlyph() needs a reason naming the ruling that decided it.");/* @__PURE__ */
2
- return _(Z,{},e)}function q(e){return e.type===Z}function Q(e){return q(e)?void 0:e}function ee(e){for(const[n,t]of Object.entries(e))t&&"object"==typeof t&&!c(t)&&u(t,`section:${n}`);return e}function ne(e,n){return{type:"custom",[d]:{label:e,level:n},marker:!0,fold:e,render:()=>/* @__PURE__ */_(X,{})}}function te(e){return ne(e,2)}function oe(e){return ne(e,1)}var le=[{label:"Start",value:"start"},{label:"Center",value:"center"},{label:"Space between",value:"between"},{label:"End",value:"end"}],ae=[{label:"Start",value:"start"},{label:"Center",value:"center"},{label:"End",value:"end"},{label:"Stretch",value:"stretch"}],re="Wrap",ie=[{label:"Wrap",value:"wrap"},{label:"No wrap",value:"nowrap"}],ue="Direction",se=[{label:"Stacked",value:"column"},{label:"One row",value:"row"}],ce={min:-32,max:64,step:2,builtIn:0},de={min:30,max:100,step:5,fallback:60},pe=["top-left","top-center","top-right","left","center","right","bottom-left","bottom-center","bottom-right"],fe="bottom-left",me={"top-left":"Top left","top-center":"Top center","top-right":"Top right",left:"Left",center:"Center",right:"Right","bottom-left":"Bottom left","bottom-center":"Bottom center","bottom-right":"Bottom right"},be=pe.map(e=>({value:e,label:me[e]}));function he(e,n=fe){return pe.includes(e)?e:n}var ge=40,ve=e=>"number"==typeof e?e:null;function ye(e,n,t){const o={};for(const l of["x","y"])t[l]!==n[l]&&(o[e[l]]=t[l]??void 0);return o}var ke=["top","right","bottom","left"],we={top:"sectionPaddingTop",right:"sectionPaddingRight",bottom:"sectionPaddingBottom",left:"sectionPaddingLeft"},Ce=e=>"number"==typeof e?e:null;function xe(e,n,t){const o={};for(const l of ke)t[l]!==n[l]&&(o[e[l]]=t[l]??void 0);return o}function Fe(e){if("number"==typeof e)return{top:e,right:e,bottom:e,left:e};const n=e&&"object"==typeof e?e:{};return Object.fromEntries(ke.map(e=>[e,Ce(n[e])]))}function je(e){const n={};for(const t of ke)"number"==typeof e[t]&&(n[t]=e[t]);return Object.keys(n).length?n:void 0}function Le(e,n={}){const t=n=>{const t=n[e];return t&&"object"==typeof t?t:{}},o=e=>Object.hasOwn(n,e)?n[e]:e;return{read:(e,n)=>t(e)[o(n)],write:(n,l,a)=>{const r={...t(n)};return void 0===a?delete r[o(l)]:r[o(l)]=a,{[e]:Object.keys(r).length?r:void 0}}}}function Oe(e,n,t){const o=Se(e),l=p();if(t)return[t.value,t.onChange];const a=l?.values??{};return[e.read(a,n),e=>o([[n,e]])]}function Se(e){const n=p(),t=function(){try{return f()}catch{return null}}();return o=>{if(!n)return;const l=t?.getState(),a=(l?"root"===n.id?l.appState.data.root.props:l.getItemById(n.id)?.props:void 0)??n.values??{};let r={};for(const[n,t]of o)r={...r,...e.write({...a,...r},n,t)};n.onChangeMany(r)}}var Pe={read:(e,n)=>e[n],write:(e,n,t)=>({[n]:t})},Ee={fieldKey:s,ownField:!0,read:(e,n)=>e[s(n)],write:(e,n,t)=>({[s(n)]:t})};function Ie(e){return{...Le(e),fieldKey:n=>`_${e}_${n}`}}function Ae({row:e,desktopKey:n,access:t,props:o}){const l=p(),a=l?.values?.[n],[r,i]=Oe(t,n);return t.ownField?e.render({...o,follows:a}):e.render({...o,value:r,storedValue:r,follows:a,onChange:i})}function Ke(e,n,t=Ee){const o={...n,render:o=>/* @__PURE__ */_(Ae,{row:n,desktopKey:e,access:t,props:o})};return{[e]:l("desktop",n),[t.fieldKey(e)]:l("mobile",o)}}var Re=["left","center","right"];function We(e,{options:n,value:t,onChange:o,ariaLabel:l,disabled:a=!1}){/* @__PURE__ */
3
- return _(y,{options:n.map(n=>n.picture?{id:n.value,label:n.picture,title:e(n.title??n.label)}:{id:n.value,label:e(n.label),title:void 0===n.title?void 0:e(n.title)}),value:t,onChange:o,ariaLabel:e(l),disabled:a})}function Be(e,{label:n,value:t,onChange:o,points:l}){const a=l.find(e=>e.disabled),r=l.map(n=>({...n,label:e(n.label),...n.title?{title:e(n.title)}:null}));/* @__PURE__ */
4
- return _(v,{label:e(n),icon:/* @__PURE__ */_(O,{}),hint:a?.title?e(a.title):void 0,children:/* @__PURE__ */_($,{label:e(n),value:t,points:r,onChange:o})})}function Me({say:e,label:n,value:t,onChange:o,pointsFor:l}){const{appState:a,selectedItem:r}=m(e=>e),i=r?.props?.id?String(r.props.id):void 0;return Be(e,{label:n,value:t,onChange:o,points:l(a?.data,i)})}function Ge({say:e,o:n}){const t=p(),l=k("panel",b),a=t?.values??{},r={x:ve(a[n.keys.x]),y:ve(a[n.keys.y])},i=e(n.label??"Position"),u=Boolean(r.x||r.y),s=e=>{const o=ye(n.keys,r,e);Object.keys(o).length&&t?.onChangeMany(o)},c=/* @__PURE__ */_(Y,{label:i,value:r,onChange:s,words:{x:e("Left and right"),y:e("Up and down"),unit:"px"},placeholder:{x:"0",y:"0"},min:n.min,max:n.max,step:n.step??1}),d=/* @__PURE__ */_(o,{words:l,label:i,changed:u,detail:"0 px, 0 px",onReset:()=>s({x:null,y:null})});return null===n.label?/* @__PURE__ */z("div",{className:"sf-position-line",children:[c,d]}):/* @__PURE__ */_(v,{label:i,icon:/* @__PURE__ */_(S,{}),changed:u,action:d,children:c})}var Ne={x:{start:/* @__PURE__ */_(A,{}),center:/* @__PURE__ */_(I,{}),end:/* @__PURE__ */_(E,{}),stretch:/* @__PURE__ */_(T,{}),between:/* @__PURE__ */_(N,{})},y:{start:/* @__PURE__ */_(G,{}),center:/* @__PURE__ */_(M,{}),end:/* @__PURE__ */_(B,{}),stretch:/* @__PURE__ */_(W,{}),between:/* @__PURE__ */_(R,{})}},Te=e=>"row"===e?"row":"column";function Ve({say:e,kind:n,label:t,options:o,value:l,onChange:a,o:r}){const i=p(),u="justify"===n==("row"===(r.runs??Te)(i?.values?.[r.directionKey??"direction"]))?"x":"y";return We(e,{options:o.map(e=>({label:e.label,value:e.value,picture:Ne[u][e.value]})),value:l,onChange:a,ariaLabel:t})}function Je({say:e,tier:n,o:t}){const o=p(),l=o?.values??{},a="mobile"===n?t.phoneKeys:t.keys,r=Object.fromEntries(ke.map(e=>[e,Ce(l[a[e]])])),i="mobile"===n?Object.fromEntries(ke.map(e=>[e,Ce(l[t.keys[e]])])):null;/* @__PURE__ */
5
- return _($e,{say:e,o:t,value:r,follows:i,onChange:e=>{const n=xe(a,r,e);Object.keys(n).length&&o?.onChangeMany(n)}})}function De({say:e,o:n,props:t}){/* @__PURE__ */
6
- return _($e,{say:e,o:n,value:Fe(t.value),follows:"follows"in t?Fe(t.follows):null,readOnly:t.readOnly,onChange:e=>t.onChange(je(e))})}function $e({say:o,o:l,value:a,follows:r,readOnly:i,onChange:u}){const s=l.useFallback?.(),c=Object.fromEntries(ke.map(e=>{return[e,String(r?.[e]??(n=e,s?.[n]??l.fallback[n]))];var n})),[d,p]=w(()=>e(a,c));/* @__PURE__ */
7
- return _(v,{label:o("Padding"),icon:/* @__PURE__ */_(K,{}),action:/* @__PURE__ */_(t,{each:d,label:o("Each side"),onChange:p,disabled:i}),children:/* @__PURE__ */_(n,{label:o("Padding"),each:d,value:a,placeholder:c,min:l.min,max:l.max,step:l.step,readOnly:i,onChange:u,words:{sides:{top:o("Padding top"),right:o("Padding right"),bottom:o("Padding bottom"),left:o("Padding left")},vertical:o("Top and bottom"),horizontal:o("Left and right"),unit:"px"}})})}function Ye(e=H){function n(n,t,l,a,r,u,s="",c){const d="function"==typeof c?{action:c}:c??{},{action:p,clear:f}=d;return{type:"custom",...i({},{type:"number",label:n}),render:i=>{const c="storedValue"in i?i.storedValue:i.value,m="follows"in i,b=m||void 0!==f,h="number"==typeof i.value,g=m&&"number"==typeof i.follows?i.follows:void 0===f?u:"function"==typeof f.hint?f.hint(i):f.hint,y=h?i.value:b?null:u,k=""===s||"px"===s?s:` ${s}`,w=!f?.emptyLabel||m&&"number"==typeof i.follows?void 0:f.emptyLabel,C=w??`${g}${k}`,x=m?e("{value} on desktop").replace("{value}",C):C,F=!0===i.readOnly;/* @__PURE__ */
8
- return _(v,{label:e(n),icon:Q(t),action:b?/* @__PURE__ */_(o,{label:e(n),changed:h,detail:x,disabled:F,onReset:()=>i.onChange(void 0)}):p?p(c,i.onChange):void 0,children:/* @__PURE__ */_("div",{className:"sf-slider",children:/* @__PURE__ */_(J,{min:l,max:a,step:r,value:y,suffix:s||void 0,showInput:!0,label:e(n),disabled:F,...b?{placeholderValue:g}:{},...w?{emptyLabel:w}:{},...void 0!==d.inputMax?{inputMax:d.inputMax}:{},size:"sm",boxSize:"md",className:"sf-slider-track",onChange:e=>{null===e?b&&h&&i.onChange(void 0):e!==y&&i.onChange(e)}})})})}}}function t(n){return We(e,n)}function u(n,l,a,r,u={}){const s=a.map(e=>"string"==typeof e?{label:e,value:e}:e);return{type:"custom",...i({},{type:"radio",label:n,options:s.map(e=>({value:e.value,label:e.label}))}),render:a=>{const{value:i,onChange:c,field:d}=a,p=s.some(e=>e.value===i),f=!0===a.readOnly;if("follows"in a&&!s.some(e=>""===e.value)){const d=u.phoneRest?u.phoneRest(a.follows):a.follows,m=s.find(e=>e.value===d)??s.find(e=>e.value===r);/* @__PURE__ */
9
- return _(v,{label:e(n),icon:Q(l),action:/* @__PURE__ */_(o,{label:e(n),changed:p,detail:e("{value} on desktop").replace("{value}",e(m?.label??"")),disabled:f,onReset:()=>c(void 0)}),children:t({options:s,value:p?i:m?.value??r,onChange:c,ariaLabel:n,disabled:f})})}/* @__PURE__ */
10
- return _(v,{label:e(n),icon:Q(l),children:t({options:s,value:p?i:i||d?.currentValue||r,onChange:c,ariaLabel:n,disabled:f})})}}}function c(e,n){return n.phone?e.unshift({label:"Match desktop",value:"",picture:/* @__PURE__ */_(P,{})}):n.inherit&&e.unshift({label:n.inherit,value:""}),e}function d(e,n){return n.inherit||n.phone?{...e,render:n=>e.render({...n,onChange:e=>n.onChange(""===e?void 0:e)})}:e}function p(e,n,t){return{[e]:l("desktop",n(t)),[s(e)]:l("mobile",n({...t,phone:!0},""))}}function f(e){return c(D(e.allow??Re).map(e=>({label:e.label,value:e.value,picture:e.icon})),e)}function m(e={},n=e.fallback??"left"){return d(u(e.label??"Text alignment",/* @__PURE__ */_(C,{}),f(e),n),e)}function b(e){const n={left:Ne.x.start,center:Ne.x.center,right:Ne.x.end};return c(D((e.allow??Re).filter(e=>e in n)).map(e=>({label:e.label,value:e.value,picture:n[e.value]})),e)}function y(e={},n=e.fallback??"left"){return d(u(e.label??"Block alignment",/* @__PURE__ */_(x,{}),b(e),n),e)}function k(n,t,o,l,a,r){return{type:"custom",options:l,fallback:a,...i({},{type:"radio",label:t,options:l.map(e=>({value:e.value,label:e.label}))}),render:({value:i,onChange:u,field:s})=>{const c=s.options??l,d=s.fallback??a,p=c.some(e=>e.value===i)?i:d;/* @__PURE__ */
11
- return _(v,{label:e(t),icon:Q(o),children:/* @__PURE__ */_(Ve,{say:e,kind:n,label:t,options:c,value:p,onChange:u,o:r})})}}}function w(e={}){return k("justify","Justify",/* @__PURE__ */_(x,{}),le,le[0].value,e)}function O(e={}){return k("align","Item alignment",/* @__PURE__ */_(V,{}),ae,"stretch",e)}function S(){const n=re;return{type:"custom",options:ie,fallback:ie[1].value,...i({},{type:"radio",label:n,options:ie.map(e=>({value:e.value,label:e.label}))}),render:({value:t,onChange:o,id:l,name:a})=>{const r=`${l??a??n}-label`;/* @__PURE__ */
12
- return _(v,{label:e(n),labelId:r,icon:/* @__PURE__ */_(F,{}),action:/* @__PURE__ */_(h,{checked:(i=t,!0===i||"wrap"===i),onChange:e=>o(e?ie[0].value:ie[1].value),ariaLabelledBy:r})});var i}}}function E({offered:e,fallback:n,resolve:t}){const o=t??(t=>e.some(e=>e.value===t)?t:n),l=u(ue,U("`Direction` names how a container’s children run — a MODIFIER, not a thing. doc/naming.md N10.1 gives the idea a dash in its Glyph column; the shell’s own `direction` meaning says the bare row is CORRECT and not an omission (SS seat, 2026-09-11)."),e.map(e=>({label:e.label,value:e.value})),o(void 0));return{type:"custom",render:e=>l.render({...e,value:o(e.value),onChange:n=>e.onChange(n)})}}function I({min:e,max:t,step:o,builtIn:l,resolve:a,action:r,mark:i}){const u=n("Gap",/* @__PURE__ */_(j,{}),e,t,o,l,"px",r),s=a??(e=>"number"==typeof e?e:l);return{...i,type:"custom",render:e=>u.render({...e,value:s(e.value,e.field),storedValue:e.value,onChange:n=>e.onChange(n)})}}function A(n,{fallback:t=fe,pointsFor:o}={}){return{type:"custom",render:({value:l,onChange:a})=>{const r=he(l,t);return o?/* @__PURE__ */_(Me,{say:e,label:n,value:r,onChange:a,pointsFor:o}):Be(e,{label:n,value:r,onChange:a,points:be})}}}function K(n){return{[n.keys.x]:{type:"custom",render:()=>/* @__PURE__ */_(Ge,{say:e,o:n})},[n.keys.y]:{...a({type:"custom",render:()=>/* @__PURE__ */_(r,{})},"always"),reachedBy:n.keys.x}}}function R(n={}){const t={...we,...n.keys},o={keys:t,phoneKeys:Object.fromEntries(ke.map(e=>[e,s(t[e])])),fallback:{top:0,right:0,bottom:0,left:0,...n.fallback},...n.useFallback?{useFallback:n.useFallback}:{},min:n.min??0,max:n.max??200,step:n.step??4},i=!1!==n.phone,u=!1===n.group?{}:{_gSpacing:te("Padding")};if(n.object){const t={type:"custom",render:n=>/* @__PURE__ */_(De,{say:e,o:o,props:n})};return ee({...u,...i?Ke(n.object.key,t,n.object.phone):{[n.object.key]:t}})}const c=n=>{const t={type:"custom",render:()=>/* @__PURE__ */_(Je,{say:e,tier:n,o:o})};return i?l(n,t):t},d=()=>a({type:"custom",render:()=>/* @__PURE__ */_(r,{})},"always"),p=ke.filter(e=>"top"!==e);return ee({...u,[o.keys.top]:c("desktop"),...i?{[o.phoneKeys.top]:c("mobile")}:{},...Object.fromEntries(p.map(e=>[o.keys[e],{...d(),reachedBy:o.keys.top}])),...i?Object.fromEntries(p.map(e=>[o.phoneKeys[e],l("mobile",d())])):{}})}return{slider:n,select:function(n,t,o,l,a){return{type:"custom",options:o,fallback:l,...a?{note:a}:{},...i({},{type:"select",label:n}),render:({value:r,onChange:i,field:u})=>{const s=u.options??o,c=u.fallback??l,d=s.some(e=>e.value===r)?r:c;/* @__PURE__ */
13
- return _(v,{label:e(n),icon:Q(t),children:/* @__PURE__ */_(g,{value:d,ariaLabel:e(n),onChange:i,options:[...a?[{label:e(a),value:"\0note",disabled:!0}]:[],...s.map(n=>({label:e(n.label),value:n.value}))]})})}}},Segmented:t,segmented:u,boxJustifyField:w,boxAlignField:O,boxWrapField:S,directionField:E,gapField:I,placementField:A,PlacementGridView:function(n){return Be(e,n)},positionFields:K,sidesPaddingFields:R,alignmentField:m,alignmentFields:function(e="align",n={}){return p(e,m,n)},AlignmentStrip:function({opts:e={},value:n,onChange:o,ariaLabel:l,disabled:a=!1}){return t({options:f(e),value:n,onChange:o,ariaLabel:l,disabled:a})},blockAlignmentField:y,blockAlignmentFields:function(e="align",n={}){return p(e,y,n)},BlockAlignmentStrip:function({opts:e={},value:n,onChange:o,ariaLabel:l,disabled:a=!1}){return t({options:b(e),value:n,onChange:o,ariaLabel:l,disabled:a})},boxFieldSet:function(e={}){const t=e.width??de,o=e.nudge?.limit??40,l=e.direction??{offered:se,fallback:"column"},a=l.resolve??(e=>l.offered.some(n=>n.value===e)?e:l.fallback),r={runs:e=>"row"===a(e)?"row":"column"};return{_gLayout:te("Layout"),placement:A("Placement",e.placement),width:n("Width",/* @__PURE__ */_(L,{}),t.min,t.max,t.step,t.fallback,"%"),direction:E(l),gap:I(e.gap??ce),justify:w(r),align:O(r),wrap:S(),_gNudge:te("Nudge"),...K({keys:e.nudge?.keys??{x:"nudgeX",y:"nudgeY"},min:-o,max:o,label:null}),...R(e.padding)}}}}var{slider:_e,select:Xe,Segmented:ze,segmented:He,boxJustifyField:Ze,boxAlignField:Ue,boxWrapField:qe,directionField:Qe,gapField:en,placementField:nn,PlacementGridView:tn,positionFields:on,sidesPaddingFields:ln,alignmentField:an,alignmentFields:rn,AlignmentStrip:un,blockAlignmentField:sn,blockAlignmentFields:cn,BlockAlignmentStrip:dn,boxFieldSet:pn}=Ye();export{me as ANCHOR_LABELS,un as AlignmentStrip,ae as BOX_ALIGN_POINTS,se as BOX_DIRECTIONS,ue as BOX_DIRECTION_LABEL,ce as BOX_GAP,le as BOX_JUSTIFY_POINTS,ge as BOX_NUDGE_LIMIT,pe as BOX_PLACEMENTS,fe as BOX_PLACEMENT_DEFAULT,de as BOX_WIDTH,re as BOX_WRAP_LABEL,ie as BOX_WRAP_POINTS,dn as BlockAlignmentStrip,Pe as FLAT_ACCESS,be as OPEN_PLACEMENT_POINTS,Ee as PHONE_KEY_ACCESS,tn as PlacementGridView,we as SIDE_PAD_KEYS,ze as Segmented,an as alignmentField,rn as alignmentFields,sn as blockAlignmentField,cn as blockAlignmentFields,Ue as boxAlignField,Ye as boxFieldBuilders,pn as boxFieldSet,Ze as boxJustifyField,qe as boxWrapField,Qe as directionField,en as gapField,te as group,Q as iconOf,q as isNoGlyph,U as noGlyph,he as normaliseBoxPlacement,Le as objectAccess,Ie as phoneObjectAccess,Ke as phoneTwinFields,nn as placementField,on as positionFields,ye as positionPatch,oe as section,He as segmented,Xe as select,ee as sharedStyleKinds,Fe as sidesFromObject,ln as sidesPaddingFields,xe as sidesPaddingPatch,je as sidesToObject,_e as slider,Se as useAccessPatch,Oe as useAccessValue};
@@ -1 +0,0 @@
1
- import{styleFieldBuilders as e,keptByRow as s}from"./chunk-RKLDSLU6.js";import{boxFieldBuilders as o,PHONE_KEY_ACCESS as l,phoneTwinFields as r,section as i}from"./chunk-6F6OAL3C.js";import{deviceField as t}from"./chunk-7GGNFLHT.js";import{BUTTON_STYLE_KEYS as c,BUTTON_PAD_KEYS as n}from"./chunk-BTQ3FJKT.js";import{TextColorIcon as u,GroundIcon as d}from"efficient-shell/icons/efficient";import{jsx as f}from"react/jsx-runtime";function a(a){const F=o(a.say),p=e(a.say),y=c,m=a.style,k=a.phone??l;return{_dSize:i("Size"),...F.sidesPaddingFields({keys:n,group:!1,...a.padding}),_dDesign:i("Design"),[y.fill]:p.colourField({key:y.fill,access:m,label:"Fill color",icon:/* @__PURE__ */f(d,{}),useFollows:a.follows.useFill,usePresets:a.usePresets,resetTo:a.resetTo,opacity:{key:y.fillOpacity,useUnset:a.follows.useFillOpacity}}),[y.fillOpacity]:s(y.fill),[y.border]:p.borderField({key:y.border,access:m,useFollows:a.follows.useBorder}),[y.text]:p.colourField({key:y.text,access:m,label:"Text color",icon:/* @__PURE__ */f(u,{}),useFollows:a.follows.useText,usePresets:a.usePresets,resetTo:a.resetTo}),[y.corners]:p.cornersField({key:y.corners,access:m,sidesKey:y.cornerSides,useFollows:a.follows.useCorners}),[y.cornerSides]:s(y.corners),_dLayout:i("Layout"),...r("align",F.blockAlignmentField({phone:!0},""),k),align:t("desktop",F.blockAlignmentField())}}export{a as buttonFieldSet};
@@ -1,4 +0,0 @@
1
- import{useAccessPatch as e,FLAT_ACCESS as r,useAccessValue as o}from"./chunk-6F6OAL3C.js";import{CornersInput as t,BorderInput as s,SettingsColour as l}from"./chunk-CLAADE4J.js";import{SettingsDefaultReset as n}from"./chunk-2T22IY3J.js";import{noControlField as i,NoControlMark as a}from"./chunk-22I55W37.js";import{CORNER_SIDES as c}from"./chunk-BTQ3FJKT.js";import{DEFAULT_PANEL_WORDS as d,DEFAULT_COLOUR_ROW_WORDS as u}from"./chunk-56ID6FKZ.js";import{SettingsField as p}from"./chunk-MIX7EGRG.js";import{useWords as y}from"./chunk-MFIRYGHW.js";import{CornerIcon as h,BorderAllIcon as f}from"efficient-shell/icons/efficient";import{jsx as m}from"react/jsx-runtime";var b=e=>e;function g(e,t){const s=e.access?void 0:{value:t.value,onChange:t.onChange};return o(e.access??r,e.key,s)}function v(e,t){return o(e.access??r,t??"")}function w(e){return{...i({type:"custom",render:()=>/* @__PURE__ */m(a,{})},"always"),reachedBy:e}}function k({o:o,say:t,props:s}){const n=y("colourRow",u),i=y("panel",d),a=o.useFollows(),c=o.usePresets?.(),p=o.opacity?.useUnset?.()??100,[h,f]=g(o,s),[b,w]=v(o,o.opacity?.key),k=e(o.access??r),C="string"==typeof h?h:"",F="number"==typeof b,j=o.access?void 0:"";/* @__PURE__ */
2
- return m(l,{label:t(o.label),icon:o.icon,value:C,onChange:e=>f(""===e?j:e),inherited:a,presets:c,changed:""!==C||F,onReset:()=>{F&&o.opacity?k([[o.key,j],[o.opacity.key,void 0]]):f(j)},resetTo:o.resetTo?o.resetTo(a.toUpperCase()):a.toUpperCase(),...o.opacity?{opacity:F?b:p,onOpacityChange:e=>w(e)}:{},readOnly:s.readOnly,words:n,resetWords:i})}var C=20;function F({o:e,say:r,props:o}){const t=e.useFollows(),[l,n]=g(e,o),i=l&&"object"==typeof l?l:{},a=t.width,c={width:r("Border width"),sides:{top:r("Border top"),right:r("Border right"),bottom:r("Border bottom"),left:r("Border left")},style:r("Border style"),styles:{solid:r("Solid"),dashed:r("Dashed"),dotted:r("Dotted")},color:r("Border color"),unit:"px"};/* @__PURE__ */
3
- return m(p,{label:r("Border"),icon:/* @__PURE__ */m(f,{}),children:/* @__PURE__ */m(s,{label:r("Border"),value:i,each:!1,onChange:r=>n(function(e){const r=Object.fromEntries(Object.entries(e).filter(([,e])=>null!=e&&""!==e));return Object.keys(r).length?r:void 0}(e.styles?{width:r.width,color:r.color,style:r.style}:{width:r.width,color:r.color})),placeholder:{sides:{top:a,right:a,bottom:a,left:a},style:"solid",color:t.color},presets:[t.color],words:c,styles:e.styles??!1,max:20,readOnly:o.readOnly})})}var j=40;function B({o:o,say:s,props:l}){const i=y("panel",d),a=o.useFollows(),[u,f]=g(o,l),[b,w]=v(o,o.sidesKey),k=e(o.access??r),C=o.access?void 0:null,F="number"==typeof u&&Number.isFinite(u)?u:null,j=Array.isArray(b)?c.filter(e=>b.includes(e)):[...c],B=[{value:"tl",label:s("Top left")},{value:"tr",label:s("Top right")},{value:"bl",label:s("Bottom left")},{value:"br",label:s("Bottom right")}];/* @__PURE__ */
4
- return m(p,{label:s("Corners"),icon:/* @__PURE__ */m(h,{}),changed:null!==F||Array.isArray(b),action:/* @__PURE__ */m(n,{label:s("Corners"),changed:null!==F||Array.isArray(b),detail:`${a}px`,onReset:()=>Array.isArray(b)?k([[o.key,C],[o.sidesKey,C]]):f(C),words:i}),children:/* @__PURE__ */m(t,{label:s("Corners"),value:j,corners:B,onChange:e=>{const r=c.filter(r=>e.includes(r));w(r.length===c.length?C:r)},radius:F,onRadiusChange:e=>f(e??C),radiusMin:0,radiusMax:40,radiusStep:o.step??1,radiusPlaceholder:String(a),disabled:l.readOnly})})}function A(e=b){return{colourField:r=>({type:"custom",render:o=>/* @__PURE__ */m(k,{o:r,say:e,props:o})}),borderField:r=>({type:"custom",render:o=>/* @__PURE__ */m(F,{o:r,say:e,props:o})}),cornersField:r=>({type:"custom",render:o=>/* @__PURE__ */m(B,{o:r,say:e,props:o})})}}var{colourField:O,borderField:T,cornersField:x}=A();export{C as BORDER_WIDTH_MAX,j as CORNER_RADIUS_MAX,T as borderField,O as colourField,x as cornersField,w as keptByRow,A as styleFieldBuilders};