efficient-easel 0.99.2 → 0.100.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.
- package/README.md +233 -17
- package/dist/autosave/index.d.ts +11 -5
- package/dist/blocks/index.d.ts +1 -1
- package/dist/canvas/index.d.ts +2 -2
- package/dist/canvas/index.js +1 -1
- package/dist/chrome/chrome.css +96 -29
- package/dist/chrome/index.d.ts +67 -18
- package/dist/chrome/index.js +17 -21
- package/dist/chunk-3EDSDPIP.js +1 -0
- package/dist/chunk-67GNT6PF.js +1 -0
- package/dist/{chunk-PI26YXYD.js → chunk-6EIVTKZZ.js} +21 -14
- package/dist/chunk-A5UK76FC.js +6 -0
- package/dist/chunk-AZT7V4UM.js +10 -0
- package/dist/chunk-DICUMYMG.js +12 -0
- package/dist/chunk-HDVD27VB.js +1 -0
- package/dist/chunk-JQM3V7XQ.js +1 -0
- package/dist/chunk-JUQ6YJCG.js +1 -0
- package/dist/chunk-LA54QLP5.js +1 -0
- package/dist/chunk-LGKQXSKO.js +16 -0
- package/dist/chunk-M6F6LS3H.js +1 -0
- package/dist/chunk-MH5GUZFQ.js +9 -0
- package/dist/chunk-P6O363EE.js +6 -0
- package/dist/chunk-PAELDYYS.js +1 -0
- package/dist/{chunk-JYPOOHII.js → chunk-QKVR3K73.js} +3 -3
- package/dist/chunk-SAJUCHOX.js +1 -0
- package/dist/chunk-TRVZ7C23.js +1 -0
- package/dist/chunk-V7GIIXFB.js +16 -0
- package/dist/chunk-WZ7TGAAT.js +3 -0
- package/dist/chunk-ZJEBF4NB.js +1 -0
- package/dist/copy/copy.css +388 -5
- package/dist/copy/index.d.ts +268 -5
- package/dist/copy/index.js +31 -10
- package/dist/drag/drag.css +10 -1
- package/dist/drag/index.d.ts +99 -8
- package/dist/drag/index.js +18 -17
- package/dist/easel/easel.css +15 -0
- package/dist/easel/index.d.ts +104 -33
- package/dist/easel/index.js +9 -1
- package/dist/edit-lock/index.d.ts +1 -3
- package/dist/edit-lock/index.js +1 -1
- package/dist/fields/fields.css +103 -26
- package/dist/fields/index.d.ts +47 -9
- package/dist/fields/index.js +30 -46
- package/dist/gold/index.js +1 -1
- package/dist/icons/index.d.ts +10 -1
- package/dist/icons/index.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/markup/index.d.ts +94 -10
- package/dist/markup/index.js +1 -1
- package/dist/menu/index.d.ts +58 -5
- package/dist/menu/index.js +15 -15
- package/dist/menu/menu.css +17 -6
- package/dist/node-ops/index.d.ts +1 -1
- package/dist/node-ops/index.js +1 -1
- package/dist/panel/index.d.ts +72 -4
- package/dist/panel/index.js +18 -16
- package/dist/panel/panel.css +56 -1
- package/dist/pick/index.d.ts +39 -1
- package/dist/pick/index.js +7 -14
- package/dist/pick/pick.css +14 -0
- package/dist/rail/index.d.ts +3 -7
- package/dist/rail/index.js +1 -1
- package/dist/strings/catalogue.d.ts +13 -35
- package/dist/strings/catalogue.js +1 -1
- package/dist/strings/index.d.ts +12 -18
- package/dist/strings/index.js +2 -2
- package/dist/style-clipboard/index.d.ts +1 -1
- package/dist/style-clipboard/index.js +1 -1
- package/dist/text-styles/index.d.ts +328 -0
- package/dist/text-styles/index.js +38 -0
- package/dist/text-styles/text-styles.css +309 -0
- package/dist/tree/index.d.ts +43 -2
- package/dist/tree/index.js +1 -1
- package/dist/tree/tree.css +71 -0
- package/dist/{types-p9j2Fac7.d.ts → types-DGcCsxs8.d.ts} +72 -7
- package/dist/{types-Bs_gVkVI.d.ts → types-DWS-s9z-.d.ts} +1 -9
- package/dist/{types-DO-b0LNj.d.ts → types-Djdq6Xkd.d.ts} +9 -8
- package/dist/{types-BOSGNvil.d.ts → types-E3HrTPg2.d.ts} +7 -13
- package/dist/{types-C7HFvdtk.d.ts → types-VqL6t5HH.d.ts} +82 -5
- package/dist/{watch-B5RTR-WX.d.ts → watch-BTYOJpQU.d.ts} +18 -1
- package/dist/{words-DagJdwIA.d.ts → words-AHHfiaRY.d.ts} +31 -2
- package/dist/{words-c-3h9xQQ.d.ts → words-BMzV04b1.d.ts} +1 -1
- package/dist/words-CJjUKcV5.d.ts +37 -0
- package/dist/words-CRYvAiHX.d.ts +105 -0
- package/dist/{words-CD3WADyb.d.ts → words-CzoyDNHm.d.ts} +4 -0
- package/dist/{words-C74EUpFr.d.ts → words-DBq7vfHy.d.ts} +57 -4
- package/dist/{words-N4ebT88_.d.ts → words-DgHcY5qB.d.ts} +9 -3
- package/package.json +8 -8
- package/dist/chunk-66CKSTXP.js +0 -14
- package/dist/chunk-672FAEKU.js +0 -1
- package/dist/chunk-67VZRQLG.js +0 -13
- package/dist/chunk-6PS6STGA.js +0 -1
- package/dist/chunk-7A7O2JWQ.js +0 -1
- package/dist/chunk-7JTL3P6P.js +0 -1
- package/dist/chunk-F2PIG3AA.js +0 -10
- package/dist/chunk-H4B72XCJ.js +0 -1
- package/dist/chunk-KDIVA6WD.js +0 -1
- package/dist/chunk-MCLBHENY.js +0 -1
- package/dist/chunk-QB3BZIRA.js +0 -5
- package/dist/chunk-SJQG5ENJ.js +0 -1
- package/dist/chunk-SUO5627P.js +0 -1
- package/dist/chunk-XUE5O62R.js +0 -3
- package/dist/chunk-ZT7M24XD.js +0 -1
- package/dist/editor/index.d.ts +0 -10
- package/dist/editor/index.js +0 -1
- /package/dist/{chunk-2PHD4L6A.js → chunk-OWBO4WKA.js} +0 -0
package/dist/markup/index.d.ts
CHANGED
|
@@ -50,11 +50,12 @@
|
|
|
50
50
|
* [words]{sup} superscript
|
|
51
51
|
* [words]{sub} subscript
|
|
52
52
|
* [words]{color=name} a theme colour, by NAME ({@link isColorName})
|
|
53
|
+
* [words]{color=#rrggbb} any colour, six hex digits ({@link isColorValue})
|
|
53
54
|
*
|
|
54
55
|
* THE SPANS AND THE NUMBERED LINE (plan step E1, approved by GG 2026-10-06)
|
|
55
56
|
* are the final text box's marks. One shape for all four, so one reading rule:
|
|
56
57
|
* `[`, words, `]`, then EXACTLY one of those blocks. Anything else after the
|
|
57
|
-
* `]` — `{foo}`, `{color=#
|
|
58
|
+
* `]` — `{foo}`, `{color=#f00}`, a merge tag's `{{` — is not a span, and the
|
|
58
59
|
* whole thing stays as typed. Two marks on the same words nest
|
|
59
60
|
* (`[[x]{sup}]{color=accent}`); one block never holds two.
|
|
60
61
|
*
|
|
@@ -132,9 +133,18 @@ type SpanKind = 'underline' | 'sup' | 'sub' | 'color';
|
|
|
132
133
|
* from one to the other invisible. */
|
|
133
134
|
type InlineKind = 'text' | 'bold' | 'italic' | 'strike' | 'code' | 'highlight' | 'accent' | 'quiet';
|
|
134
135
|
/** A theme colour's name as a span saves it: lowercase letters, digits and
|
|
135
|
-
* `-`, starting with a letter (`accent`, `brand-2`).
|
|
136
|
-
*
|
|
136
|
+
* `-`, starting with a letter (`accent`, `brand-2`). A name is the theme's,
|
|
137
|
+
* so changing the theme recolours the words. Never a hex — that is the other
|
|
138
|
+
* kind of colour value ({@link isColorValue}). */
|
|
137
139
|
declare function isColorName(name: string): boolean;
|
|
140
|
+
/** A colour a span may carry: a theme NAME ({@link isColorName}), or "any
|
|
141
|
+
* colour" as EXACTLY six hex digits after a `#` (`#0b5cff`). Either case
|
|
142
|
+
* reads; the writers save lowercase ({@link colorValue}). Nothing else —
|
|
143
|
+
* `#f00`, `rgb()`, a typo — is a colour, so it stays plain text.
|
|
144
|
+
*
|
|
145
|
+
* WIDENS E1 (GG 2026-10-06): the final design draws "Any color", and a
|
|
146
|
+
* feature is never cut to fit. A hex does not follow the theme; a name does. */
|
|
147
|
+
declare function isColorValue(value: string): boolean;
|
|
138
148
|
/**
|
|
139
149
|
* The words a press WRITES when nothing is selected — `**bold text**`, left
|
|
140
150
|
* selected so the next keystroke replaces it. English unless the caller of
|
|
@@ -160,11 +170,12 @@ interface WrapResult {
|
|
|
160
170
|
*/
|
|
161
171
|
declare function applyMark(text: string, start: number, end: number, style: MarkStyle, rules?: readonly InlineRule[], placeholders?: MarkPlaceholders): WrapResult;
|
|
162
172
|
/**
|
|
163
|
-
* Colour `text[start:end]` with
|
|
173
|
+
* Colour `text[start:end]` with `name` — a theme colour name or a 6-digit hex
|
|
174
|
+
* ({@link isColorValue}; a hex is saved lowercase) — writing
|
|
164
175
|
* `[words]{color=name}` and leaving the words selected. On words that are
|
|
165
|
-
* already exactly one colour span it changes the
|
|
166
|
-
* off. A
|
|
167
|
-
* never saved as a block the reader would print as text.
|
|
176
|
+
* already exactly one colour span it changes the colour; `null` takes it
|
|
177
|
+
* off. A value {@link isColorValue} refuses changes nothing — `#f00` or a typo
|
|
178
|
+
* is never saved as a block the reader would print as text.
|
|
168
179
|
*
|
|
169
180
|
* Separate from {@link applyMark} because a colour carries a value, as a link
|
|
170
181
|
* does ({@link writeLink}).
|
|
@@ -298,6 +309,14 @@ interface InlineRule {
|
|
|
298
309
|
* what actually does the work.
|
|
299
310
|
*/
|
|
300
311
|
declare const STANDARD_MARKUP: readonly InlineRule[];
|
|
312
|
+
/**
|
|
313
|
+
* Marks the copy box no longer OFFERS (E5a, 2026-10-06: Lewis dropped
|
|
314
|
+
* Emphasis `==` and Quiet `^^` from the final design). They are still read,
|
|
315
|
+
* drawn and written back byte for byte — saved copy wears them — but no bar
|
|
316
|
+
* button writes them, and `CopyBox` draws none for them even when a host asks.
|
|
317
|
+
* Declared before {@link COPY_FIELD_TOOLS} reads it at module load.
|
|
318
|
+
*/
|
|
319
|
+
declare const RETIRED_MARKS: readonly MarkStyle[];
|
|
301
320
|
/**
|
|
302
321
|
* The buttons a COPY FIELD offers — a storefront section's copy box and a
|
|
303
322
|
* campaign block's copy box show exactly these, so a merchant meets one toolbar
|
|
@@ -458,8 +477,8 @@ interface MarkSpan {
|
|
|
458
477
|
}
|
|
459
478
|
/**
|
|
460
479
|
* A NEW run, as the toolbar writes it: `**` for bold, `_` for italic, and so on
|
|
461
|
-
* ({@link applyMark}'s own fences), a link to `value`, or a colour span
|
|
462
|
-
* `value` ({@link
|
|
480
|
+
* ({@link applyMark}'s own fences), a link to `value`, or a colour span of
|
|
481
|
+
* `value` — a theme name or a 6-digit hex, saved lowercase ({@link isColorValue}). Null for a style that writes no run (a line
|
|
463
482
|
* prefix, clear) and for a link or colour with no usable value. A paste filter
|
|
464
483
|
* or an editor making a run asks here, so no delimiter is spelled outside this
|
|
465
484
|
* module (E8).
|
|
@@ -524,5 +543,70 @@ declare function sameMarks(a: readonly MarkedLine[], b: readonly MarkedLine[]):
|
|
|
524
543
|
* literal underscore. And if even that would not read back, nothing changes.
|
|
525
544
|
*/
|
|
526
545
|
declare function clearMarks(text: string, start: number, end: number, rules?: readonly InlineRule[]): WrapResult;
|
|
546
|
+
/**
|
|
547
|
+
* Press `style` on the WORDS in `text[start:end]`, whichever runs they sit in —
|
|
548
|
+
* what a view that hides the delimiters needs, where `applyMark` (which wraps
|
|
549
|
+
* exactly the characters selected) would cross or strand a delimiter.
|
|
550
|
+
*
|
|
551
|
+
* - Every selected word already wears the mark: it comes off them. Part of a
|
|
552
|
+
* run is split off; the rest keeps the mark.
|
|
553
|
+
* - Otherwise the mark goes on, as ONE run over the words and any run of the
|
|
554
|
+
* same mark they touch (`**half** off` → `**half off**`).
|
|
555
|
+
* - Nothing is written that would not read back: a third level of nesting,
|
|
556
|
+
* or `_` inside a word, leaves the text as it was.
|
|
557
|
+
* - With nothing selected it is {@link applyMark}: a selected placeholder.
|
|
558
|
+
*
|
|
559
|
+
* Offsets are the saved string's, before and after.
|
|
560
|
+
*/
|
|
561
|
+
declare function toggleMark(text: string, start: number, end: number, style: MarkStyle, rules?: readonly InlineRule[]): WrapResult;
|
|
562
|
+
/**
|
|
563
|
+
* The marks every word in `text[start:end]` wears — `bold`, `link`, a legacy
|
|
564
|
+
* `accent` … — or, with nothing selected, the marks of the word just before the
|
|
565
|
+
* caret on its line: what typing there would continue. What a toolbar draws
|
|
566
|
+
* as pressed in a view without delimiters.
|
|
567
|
+
*/
|
|
568
|
+
declare function marksIn(text: string, start: number, end: number, rules?: readonly InlineRule[]): Set<string>;
|
|
569
|
+
/**
|
|
570
|
+
* Link the WORDS in `text[start:end]` to `href`, nesting with any run they
|
|
571
|
+
* sit in rather than crossing it: words inside a bold run become a link inside
|
|
572
|
+
* it (`**Book [a fitting](/book)**`), a whole bold run goes inside the link.
|
|
573
|
+
*
|
|
574
|
+
* With nothing selected, inside a link it re-points that link; anywhere else
|
|
575
|
+
* it writes the address as its own words ({@link writeLink}). The caret lands
|
|
576
|
+
* after the link. ⚠️ The address is the caller's to check ({@link isSafeLinkHref}).
|
|
577
|
+
*/
|
|
578
|
+
declare function linkOver(text: string, start: number, end: number, href: string, rules?: readonly InlineRule[]): WrapResult;
|
|
579
|
+
/** Take off the link the caret or selection touches, keeping its words —
|
|
580
|
+
* selected, so a second press of the link button puts it straight back. */
|
|
581
|
+
declare function unlinkAt(text: string, start: number, end: number, rules?: readonly InlineRule[]): WrapResult;
|
|
582
|
+
/**
|
|
583
|
+
* Colour the WORDS in `text[start:end]` with `name` — a theme colour name or
|
|
584
|
+
* a 6-digit hex, saved lowercase (`[words]{color=name}`), nesting with any run they sit in rather than
|
|
585
|
+
* crossing it — the colour window's write, as {@link linkOver} is the link
|
|
586
|
+
* window's. A colour the words already wear is replaced; `null` takes the
|
|
587
|
+
* colour off them, splitting a run that only partly covers them.
|
|
588
|
+
*
|
|
589
|
+
* A value {@link isColorValue} refuses (`#f00`, a word with a space) changes
|
|
590
|
+
* nothing: a block the reader would print as text is never written. With
|
|
591
|
+
* nothing selected nothing changes either — the box keeps a pending colour
|
|
592
|
+
* for the words typed next instead.
|
|
593
|
+
*/
|
|
594
|
+
declare function colorOver(text: string, start: number, end: number, name: string | null, rules?: readonly InlineRule[]): WrapResult;
|
|
595
|
+
/** The ONE colour every word in `text[start:end]` wears — or, with nothing
|
|
596
|
+
* selected, the word before the caret on its line — or null. What the colour
|
|
597
|
+
* window draws as chosen, and the colour under the A. */
|
|
598
|
+
declare function colorIn(text: string, start: number, end: number, rules?: readonly InlineRule[]): string | null;
|
|
599
|
+
/**
|
|
600
|
+
* Make every line `text[start:end]` touches (the caret's line, with nothing
|
|
601
|
+
* selected) a bulleted list, a numbered list, or — `null` — no list. The
|
|
602
|
+
* words and their marks keep their bytes; only the line prefixes change. A
|
|
603
|
+
* numbered list counts on from a numbered line just above it, else from 1.
|
|
604
|
+
* An empty line becomes an empty list line (`- `), which the rich view draws
|
|
605
|
+
* as a bullet with nothing after it yet. The selection keeps its words.
|
|
606
|
+
*/
|
|
607
|
+
declare function setList(text: string, start: number, end: number, kind: 'bullet' | 'number' | null, rules?: readonly InlineRule[]): WrapResult;
|
|
608
|
+
/** `bullet` or `number` when every line `text[start:end]` touches is that
|
|
609
|
+
* kind of list line, else null — what the list buttons draw as pressed. */
|
|
610
|
+
declare function listIn(text: string, start: number, end: number, rules?: readonly InlineRule[]): 'bullet' | 'number' | null;
|
|
527
611
|
|
|
528
|
-
export { CAMPAIGN_MARKUP, COPY_FIELD_TOOLS, type InlineKind, type InlineRule, type InlineToken, type LinkSpan, MARKUP_TOOLS, MARK_NAMES, MARK_PLACEHOLDERS, type MarkPlaceholders, type MarkSpan, type MarkStyle, type MarkedChar, type MarkedLine, type MarkupLine, type MarkupLink, type MarkupNode, type MarkupRun, type MarkupText, type MarkupTool, STANDARD_MARKUP, STOREFRONT_MARKUP, type SpanKind, type WrapResult, applyColor, applyMark, clearMarks, findLink, isColorName, isSafeLinkHref, markedAt, markedLines, markupTools, parseMarkup, removeLink, sameMarks, serializeMarkup, spanFor, stripInline, tokenizeInline, writeLink, writeMarkedLines };
|
|
612
|
+
export { CAMPAIGN_MARKUP, COPY_FIELD_TOOLS, type InlineKind, type InlineRule, type InlineToken, type LinkSpan, MARKUP_TOOLS, MARK_NAMES, MARK_PLACEHOLDERS, type MarkPlaceholders, type MarkSpan, type MarkStyle, type MarkedChar, type MarkedLine, type MarkupLine, type MarkupLink, type MarkupNode, type MarkupRun, type MarkupText, type MarkupTool, RETIRED_MARKS, STANDARD_MARKUP, STOREFRONT_MARKUP, type SpanKind, type WrapResult, applyColor, applyMark, clearMarks, colorIn, colorOver, findLink, isColorName, isColorValue, isSafeLinkHref, linkOver, listIn, markedAt, markedLines, marksIn, markupTools, parseMarkup, removeLink, sameMarks, serializeMarkup, setList, spanFor, stripInline, toggleMark, tokenizeInline, unlinkAt, writeLink, writeMarkedLines };
|
package/dist/markup/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{CAMPAIGN_MARKUP,COPY_FIELD_TOOLS,MARKUP_TOOLS,MARK_NAMES,MARK_PLACEHOLDERS,STANDARD_MARKUP,STOREFRONT_MARKUP,applyColor,applyMark,clearMarks,findLink,isColorName,isSafeLinkHref,markedAt,markedLines,markupTools,parseMarkup,removeLink,sameMarks,serializeMarkup,spanFor,stripInline,tokenizeInline,writeLink,writeMarkedLines}from"../chunk-
|
|
1
|
+
export{CAMPAIGN_MARKUP,COPY_FIELD_TOOLS,MARKUP_TOOLS,MARK_NAMES,MARK_PLACEHOLDERS,RETIRED_MARKS,STANDARD_MARKUP,STOREFRONT_MARKUP,applyColor,applyMark,clearMarks,colorIn,colorOver,findLink,isColorName,isColorValue,isSafeLinkHref,linkOver,listIn,markedAt,markedLines,marksIn,markupTools,parseMarkup,removeLink,sameMarks,serializeMarkup,setList,spanFor,stripInline,toggleMark,tokenizeInline,unlinkAt,writeLink,writeMarkedLines}from"../chunk-HDVD27VB.js";
|
package/dist/menu/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { a as EaselContextMenuProps, b as EaselContextMenuSkin } from '../types-
|
|
3
|
-
export { E as EaselContextMenuItem
|
|
2
|
+
import { a as EaselContextMenuProps, b as EaselContextMenuSkin } from '../types-Djdq6Xkd.js';
|
|
3
|
+
export { E as EaselContextMenuItem } from '../types-Djdq6Xkd.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* The right-click menu box, shared by both editors (card 66400.d).
|
|
@@ -29,8 +29,61 @@ export { E as EaselContextMenuItem, c as EditorContextMenuItem, d as EditorConte
|
|
|
29
29
|
* client boundary.
|
|
30
30
|
*/
|
|
31
31
|
declare function EaselContextMenu({ x, y, items, onClose, maxHeight, skin, portalTo, }: EaselContextMenuProps): react.ReactPortal | null;
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* WHERE THE MENU BOX LIVES — the decision, and the one check behind it
|
|
35
|
+
* (second host, 2026-10-07).
|
|
36
|
+
*
|
|
37
|
+
* The box is portalled to `<body>`. A host's axe-core run flags that under
|
|
38
|
+
* `region` (a best-practice rule, moderate): a `role="menu"` hanging off the
|
|
39
|
+
* body is page content outside every landmark. Two answers were on the table.
|
|
40
|
+
*
|
|
41
|
+
* A. Wrap the box in something a landmark check lets through — a
|
|
42
|
+
* `role="region"`, a `role="dialog"`. REFUSED.
|
|
43
|
+
* A menu is neither. A region is a landmark, and one that appears in the
|
|
44
|
+
* page's landmark list each time a menu opens is noise for the very
|
|
45
|
+
* people landmarks are for; a dialog is announced as one and is expected
|
|
46
|
+
* to hold the focus like one. Changing what a thing IS so a checker
|
|
47
|
+
* stops reading it is the wrong way round.
|
|
48
|
+
*
|
|
49
|
+
* B. Portal the box INTO the editor's root, which sits inside whatever
|
|
50
|
+
* landmark the host put its editor in. TAKEN,
|
|
51
|
+
* as the host's choice (`portalTo`), with this check.
|
|
52
|
+
*
|
|
53
|
+
* WHY IT IS NOT THE DEFAULT. The box is `position: fixed` and placed at the
|
|
54
|
+
* pointer in the VIEWPORT's coordinates. An ancestor that is transformed,
|
|
55
|
+
* filtered or contained becomes the containing block of a fixed box inside it:
|
|
56
|
+
* the box is then placed against that ancestor and clipped by it — the menu
|
|
57
|
+
* opens somewhere the pointer is not, or is cut off. `<body>` is the one place
|
|
58
|
+
* that can never happen, the box does not know where a host's root is, and
|
|
59
|
+
* every surface that draws it today ships on the body. So the body stays the
|
|
60
|
+
* default, and a host that wants the box in its landmarks says where:
|
|
61
|
+
*
|
|
62
|
+
* <EaselContextMenu portalTo={menuPortalTarget(rootRef.current)} … />
|
|
63
|
+
*
|
|
64
|
+
* `menuPortalTarget` hands the root back only when a fixed box in it is still
|
|
65
|
+
* placed against the viewport, and `null` — the body — otherwise. So a root
|
|
66
|
+
* that is scaled on one screen and not on another gets the right answer on
|
|
67
|
+
* both, with no rule of the host's own.
|
|
68
|
+
*
|
|
69
|
+
* Two things the host still owns: the root's stacking (the box is `z-index:
|
|
70
|
+
* 9999` INSIDE the root's stacking context, so an overlay the host draws above
|
|
71
|
+
* its editor is above the menu too), and its tokens (the default skin names
|
|
72
|
+
* `es-editor` on the box, which re-declares the token defaults there — a
|
|
73
|
+
* re-themed root passes a skin without it; see `ES_MENU_SKIN`).
|
|
74
|
+
*
|
|
75
|
+
* Pure DOM, no React, nothing read at import.
|
|
76
|
+
*/
|
|
77
|
+
/**
|
|
78
|
+
* The element to hand `EaselContextMenu` as `portalTo` so the box lives inside
|
|
79
|
+
* the editor: `root` itself when a fixed box in it is still placed against the
|
|
80
|
+
* viewport, `null` (the body, the default) when the root or anything around it
|
|
81
|
+
* would place and clip the box instead — or when there is no root yet.
|
|
82
|
+
*
|
|
83
|
+
* Call it when the menu opens, not once: whether a root is transformed can
|
|
84
|
+
* change with the screen (a host that scales its editor to fit).
|
|
85
|
+
*/
|
|
86
|
+
declare function menuPortalTarget(root: Element | null | undefined): HTMLElement | null;
|
|
34
87
|
|
|
35
88
|
/**
|
|
36
89
|
* The default skin — the storefront editor's approved look, in classes that
|
|
@@ -48,4 +101,4 @@ declare const EditorContextMenu: typeof EaselContextMenu;
|
|
|
48
101
|
*/
|
|
49
102
|
declare const ES_MENU_SKIN: EaselContextMenuSkin;
|
|
50
103
|
|
|
51
|
-
export { ES_MENU_SKIN, EaselContextMenu, EaselContextMenuProps, EaselContextMenuSkin,
|
|
104
|
+
export { ES_MENU_SKIN, EaselContextMenu, EaselContextMenuProps, EaselContextMenuSkin, menuPortalTarget };
|
package/dist/menu/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import{useRef as e,useState as n,useLayoutEffect as t,useEffect as
|
|
2
|
-
/* @__PURE__ */i(
|
|
3
|
-
/* @__PURE__ */
|
|
4
|
-
/* @__PURE__ */
|
|
5
|
-
/* @__PURE__ */
|
|
6
|
-
/* @__PURE__ */
|
|
7
|
-
/* @__PURE__ */
|
|
8
|
-
/* @__PURE__ */
|
|
9
|
-
/* @__PURE__ */
|
|
10
|
-
/* @__PURE__ */
|
|
11
|
-
/* @__PURE__ */
|
|
12
|
-
/* @__PURE__ */
|
|
13
|
-
/* @__PURE__ */
|
|
14
|
-
/* @__PURE__ */
|
|
15
|
-
/* @__PURE__ */
|
|
1
|
+
import{useRef as e,useState as n,useLayoutEffect as t,useEffect as r}from"react";import{createPortal as l}from"react-dom";import{jsxs as i,Fragment as o,jsx as a}from"react/jsx-runtime";var c={menu:"es-editor es-menu",menuSub:"es-editor es-menu",item:"es-menu-item",itemDanger:"es-menu-item is-danger",divider:"es-menu-divider",header:"es-menu-header",icon:"es-menu-icon",shortcut:"es-menu-shortcut",label:"es-menu-label",labelSub:"es-menu-label es-menu-label-sub",chevron:"es-menu-chevron",tick:"es-menu-tick"};function s({x:s,y:u,items:d,onClose:m,maxHeight:h,skin:b,portalTo:p}){const f=e(null),y=e(null),[g,v]=n(null),[w,x]=n({x:s,y:u}),[k,N]=n({x:s,y:u});if(k.x===s&&k.y===u||(N({x:s,y:u}),x({x:s,y:u})),t(()=>{const e=f.current;if(!e)return;const{width:n,height:t}=e.getBoundingClientRect(),r=window.innerWidth,l=window.innerHeight;let i=u;u+t>l-8&&(i=u-t>=8?u-t:Math.max(8,l-t-8));let o=s;s+n>r-8&&(o=s-n>=8?s-n:Math.max(8,r-n-8)),o===w.x&&i===w.y||x({x:o,y:i})},[s,u,d,w.x,w.y]),r(()=>{const e=document.activeElement;return f.current?.querySelector('[role="menuitem"]')?.focus(),()=>e?.focus?.()},[]),r(()=>{const e=e=>{const n=e.target;y.current?.contains(n)||f.current&&!f.current.contains(n)&&m()},n=e=>{"Escape"===e.key&&m()},t=setTimeout(()=>{window.addEventListener("mousedown",e),window.addEventListener("contextmenu",e),window.addEventListener("keydown",n)},0);return()=>{clearTimeout(t),window.removeEventListener("mousedown",e),window.removeEventListener("contextmenu",e),window.removeEventListener("keydown",n)}},[m]),0===d.length)return null;const C=b??c,E=e=>e.danger?C.itemDanger:C.item,S=({on:e})=>/* @__PURE__ */a("span",{className:C.tick,"aria-hidden":"true",children:e?"✓":""}),L=({of:e})=>null==e.icon?null:/* @__PURE__ */a("span",{className:C.icon,"aria-hidden":"true",children:e.icon}),T=({of:e})=>null==e.shortcut?null:/* @__PURE__ */a("span",{className:C.shortcut,"aria-hidden":"true",children:e.shortcut}),B=e=>null!=e.icon||null!=e.shortcut,M=(e,n,t)=>{if(!e.children?.length)return void v(null);const r=t.getBoundingClientRect(),l=f.current,i=l?parseFloat(getComputedStyle(l).minWidth):NaN,o=Number.isFinite(i)&&i>0?i:l?.getBoundingClientRect().width??0,a=r.right+o>window.innerWidth-8?r.left-o:r.right;v({i:n,x:Math.max(8,a),y:r.top})},D=g?d[g.i]:null,F=e=>({...C.surface,...e});return l(
|
|
2
|
+
/* @__PURE__ */i(o,{children:[
|
|
3
|
+
/* @__PURE__ */a("div",{ref:f,"data-es-menu":"",role:"menu","aria-label":d.find(e=>e.header)?.label,className:C.menu,style:F({left:w.x,top:w.y,maxHeight:h||void 0}),children:d.map((e,n)=>"—"===e.label?
|
|
4
|
+
/* @__PURE__ */a("div",{role:"separator",className:C.divider},n):e.header?/* @__PURE__ */a("div",{className:C.header,children:e.label},n):e.children?.length?/* @__PURE__ */i("button",{type:"button",role:"menuitem","aria-haspopup":"menu","aria-expanded":g?.i===n,onMouseEnter:t=>M(e,n,t.currentTarget),onFocus:t=>M(e,n,t.currentTarget),onClick:t=>M(e,n,t.currentTarget),className:E(e),style:C.itemStyle,children:[
|
|
5
|
+
/* @__PURE__ */a(L,{of:e}),
|
|
6
|
+
/* @__PURE__ */a("span",{className:C.label,children:e.label}),
|
|
7
|
+
/* @__PURE__ */a(T,{of:e}),
|
|
8
|
+
/* @__PURE__ */a("span",{"aria-hidden":"true",className:C.chevron,children:"›"})]},n):/* @__PURE__ */a("button",{type:"button",role:"menuitem",onMouseEnter:()=>v(null),onClick:()=>{e.onClick(),m()},className:E(e),style:C.itemStyle,children:B(e)?/* @__PURE__ */i(o,{children:[
|
|
9
|
+
/* @__PURE__ */a(L,{of:e}),
|
|
10
|
+
/* @__PURE__ */a("span",{className:C.label,children:e.label}),
|
|
11
|
+
/* @__PURE__ */a(T,{of:e})]}):e.label},n))}),D?.children?.length&&g?/* @__PURE__ */a("div",{ref:y,"data-es-menu":"",role:"menu","aria-label":D.label,className:C.menuSub??C.menu,style:F({left:g.x,top:g.y}),children:D.children.map((e,n)=>/* @__PURE__ */i("button",{type:"button",role:"menuitemradio","aria-checked":Boolean(e.checked),onClick:()=>{e.onClick(),m()},className:E(e),style:C.itemStyle,children:[
|
|
12
|
+
/* @__PURE__ */a(S,{on:Boolean(e.checked)}),
|
|
13
|
+
/* @__PURE__ */a(L,{of:e}),
|
|
14
|
+
/* @__PURE__ */a("span",{className:C.labelSub??C.label,children:e.label}),
|
|
15
|
+
/* @__PURE__ */a(T,{of:e})]},n))}):null]}),p??document.body)}var u=/\b(transform|translate|rotate|scale|perspective|filter|backdrop-filter|contain)\b/;function d(e){const n=n=>e.getPropertyValue(n).trim(),t=(e,t)=>{const r=n(e);return""!==r&&!t.includes(r)};return!!t("transform",["none"])||(!!(t("translate",["none"])||t("rotate",["none"])||t("scale",["none"]))||(!!t("perspective",["none"])||(!!t("filter",["none"])||(!(!t("backdrop-filter",["none"])&&!t("-webkit-backdrop-filter",["none"]))||(!!/\b(layout|paint|strict|content)\b/.test(n("contain"))||(!!t("container-type",["normal"])||(!!t("content-visibility",["visible"])||u.test(n("will-change")))))))))}function m(e){const n=e?.ownerDocument,t=n?.defaultView;if(!(e&&n&&t&&e.isConnected))return null;for(let r=e;r&&r!==n.body&&r!==n.documentElement;r=r.parentElement)if(d(t.getComputedStyle(r)))return null;return e}export{c as ES_MENU_SKIN,s as EaselContextMenu,m as menuPortalTarget};
|
package/dist/menu/menu.css
CHANGED
|
@@ -115,12 +115,16 @@
|
|
|
115
115
|
than copied as a literal. */
|
|
116
116
|
--es-menu-danger-fg: var(--es-danger-strong);
|
|
117
117
|
|
|
118
|
-
/* THE SHORTCUT COLUMN'S INK — quieter than a label,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
/* THE SHORTCUT COLUMN'S INK — quieter than a label, because a shortcut is a
|
|
119
|
+
reminder and never the thing being read; but it IS read, so it is a grey
|
|
120
|
+
that can be: `--es-muted`, 4.8:1 on the box at 11px.
|
|
121
|
+
⚠️ It was the drawing's own `#b6bcc4` until 2026-10-07 — lighter than
|
|
122
|
+
`--es-disabled`, 1.9:1 on white, the lowest contrast of any text in the
|
|
123
|
+
package. A second host's axe-core run failed it (WCAG AA, 4.5:1) and the
|
|
124
|
+
host put `--es-muted` here itself; this is that, at the source. Still
|
|
125
|
+
behind ONE var, so the design can pick another grey that can be read.
|
|
126
|
+
Held by ratio in tests/contrast-small-text.test.ts. */
|
|
127
|
+
--es-menu-shortcut-fg: var(--es-muted);
|
|
124
128
|
}
|
|
125
129
|
|
|
126
130
|
|
|
@@ -225,6 +229,13 @@
|
|
|
225
229
|
color: var(--es-menu-shortcut-fg);
|
|
226
230
|
font-size: 11px;
|
|
227
231
|
}
|
|
232
|
+
/* On the row that is pointed at the ground is tinted, and the grey that passes
|
|
233
|
+
on white does not pass on the tint (4.4:1). So the shortcut takes the ROW's
|
|
234
|
+
ink there — the accent on a plain row (4.7:1), the red on one that destroys
|
|
235
|
+
(5.9:1): one ink per row, and no new colour. */
|
|
236
|
+
.es-menu-item:hover .es-menu-shortcut {
|
|
237
|
+
color: inherit;
|
|
238
|
+
}
|
|
228
239
|
|
|
229
240
|
/* Present on every row of a flyout that uses one, so the labels line up whether
|
|
230
241
|
or not the row is the state you are already on. */
|
package/dist/node-ops/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { A as AfterRemove, I as ItemSelector, N as NodeSelection, P as Planned, S as StepBounds, c as canStep, f as fromItemSelector, p as parentStep, a as planDuplicate, b as planMove, d as planPaste, e as planRemove, g as planSetProps, h as planStep, t as toItemSelector } from '../plan-DChbUXdS.js';
|
|
2
2
|
export { C as ClipboardEntry, N as NodeClipboard, a as NodeOps, b as NodeOpsHost, c as createNodeClipboard, u as useNodeOps } from '../useNodeOps-BF0fQsif.js';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
|
-
import { E as EaselContextMenuItem } from '../types-
|
|
4
|
+
import { E as EaselContextMenuItem } from '../types-Djdq6Xkd.js';
|
|
5
5
|
import { N as NodeMenuWords } from '../words-DMG4dYq2.js';
|
|
6
6
|
import '../types-41704JE4.js';
|
|
7
7
|
|
package/dist/node-ops/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DEFAULT_NODE_MENU_WORDS as e}from"../chunk-WK3AFPXY.js";export{canStep,fromItemSelector,parentStep,planDuplicate,planMove,planPaste,planRemove,planSetProps,planStep,toItemSelector,useNodeOps}from"../chunk-D2BCHTQR.js";import{TrashIcon as n,EyeIcon as t,EyeOffIcon as o,RenameIcon as p,PasteIcon as l,CopyIcon as c,MoveDownIcon as s,MoveUpIcon as a,DuplicateIcon as i,AddIcon as r,LayersIcon as u}from"../chunk-
|
|
1
|
+
import{DEFAULT_NODE_MENU_WORDS as e}from"../chunk-WK3AFPXY.js";export{canStep,fromItemSelector,parentStep,planDuplicate,planMove,planPaste,planRemove,planSetProps,planStep,toItemSelector,useNodeOps}from"../chunk-D2BCHTQR.js";import{TrashIcon as n,EyeIcon as t,EyeOffIcon as o,RenameIcon as p,PasteIcon as l,CopyIcon as c,MoveDownIcon as s,MoveUpIcon as a,DuplicateIcon as i,AddIcon as r,LayersIcon as u}from"../chunk-6EIVTKZZ.js";import"../chunk-KRXQM65X.js";import"../chunk-NKB3KVAB.js";import{jsx as h}from"react/jsx-runtime";function d(){let e=null;const n=/* @__PURE__ */new Set;return{get:()=>e,set(t){e=t;for(const e of[...n])e()},subscribe:e=>(n.add(e),()=>n.delete(e))}}var m={layer:{icon:/* @__PURE__ */h(u,{})},addInside:{icon:/* @__PURE__ */h(r,{})},duplicate:{icon:/* @__PURE__ */h(i,{})},moveUp:{icon:/* @__PURE__ */h(a,{})},moveDown:{icon:/* @__PURE__ */h(s,{})},copy:{icon:/* @__PURE__ */h(c,{}),shortcut:"⌘C"},cut:{icon:/* @__PURE__ */h(c,{}),shortcut:"⌘X"},paste:{icon:/* @__PURE__ */h(l,{}),shortcut:"⌘V"},rename:{icon:/* @__PURE__ */h(p,{})},hide:{icon:/* @__PURE__ */h(o,{})},show:{icon:/* @__PURE__ */h(t,{})},upLevel:{},delete:{icon:/* @__PURE__ */h(n,{}),danger:!0}};function v(n){const{actions:t,rows:o={},extras:p={},words:l=e}=n,c=e=>({...m[e],label:l[e],...o[e]}),s=(e,n,t)=>{const o=c(e),p={label:t??o.label??e,onClick:n};return o.icon&&(p.icon=o.icon),o.shortcut&&(p.shortcut=o.shortcut),o.danger&&(p.danger=!0),p},a=()=>{const e=c("layer"),t={label:e.label??l.layer,onClick:()=>{},children:n.layer.items};return e.icon&&(t.icon=e.icon),t},i=[],r=[],u=[],h=[],d=!!n.layer&&n.layer.items.length>1;if(d&&"pick"===(n.layer.group??"pick")&&i.push(a()),t.addInside&&r.push(s("addInside",t.addInside)),t.duplicate&&r.push(s("duplicate",t.duplicate)),t.moveUp&&r.push(s("moveUp",t.moveUp)),t.moveDown&&r.push(s("moveDown",t.moveDown)),t.copy&&r.push(s("copy",t.copy)),t.cut&&r.push(s("cut",t.cut)),t.paste){const e=c("paste").label??l.paste;r.push(s("paste",t.paste.run,l.pasteNamed({paste:e,label:t.paste.label})))}t.rename&&u.push(s("rename",t.rename)),t.hide&&u.push(t.hide.hidden?s("show",t.hide.run):s("hide",t.hide.run)),d&&"change"===n.layer.group&&u.push(a()),t.upLevel&&u.push(s("upLevel",t.upLevel)),t.delete&&h.push(s("delete",t.delete)),i.push(...p.pick??[]),r.push(...p.make??[]),u.push(...p.change??[]),h.push(...p.remove??[]);const v=[i,r,n.style??[],u,h].filter(e=>e.length>0),g=i.length+r.length+u.length+h.length===0,k=[{label:n.title,header:!0,onClick:()=>{}}];return g&&n.pinnedLine&&k.push({label:n.pinnedLine,header:!0,onClick:()=>{}}),v.forEach((e,t)=>{(t>0||g&&n.pinnedLine)&&k.push({label:"—",onClick:()=>{}}),k.push(...e)}),k}export{d as createNodeClipboard,v as nodeMenuItems};
|
package/dist/panel/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { W as WordFit } from '../wordFit-D4S36PvB.js';
|
|
4
|
+
import { P as PanelWords } from '../words-CJjUKcV5.js';
|
|
4
5
|
export { a as SETTINGS_BUCKETS, S as SettingsBucket } from '../buckets-CuLM47h9.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -367,9 +368,10 @@ interface SettingsFieldProps {
|
|
|
367
368
|
/** Set when the control is a single form element, so the label points at it. */
|
|
368
369
|
htmlFor?: string;
|
|
369
370
|
/**
|
|
370
|
-
* A trailing control on the label row —
|
|
371
|
-
* of a boolean row (card 8801, Option A: a yes-or-no
|
|
372
|
-
* two, so the switch takes the spot a value like
|
|
371
|
+
* A trailing control on the label row — `SettingsDefaultReset` (Default /
|
|
372
|
+
* Reset), or the SWITCH of a boolean row (card 8801, Option A: a yes-or-no
|
|
373
|
+
* setting is one line, not two, so the switch takes the spot a value like
|
|
374
|
+
* "34 px" normally sits in).
|
|
373
375
|
*/
|
|
374
376
|
action?: ReactNode;
|
|
375
377
|
/**
|
|
@@ -384,6 +386,41 @@ interface SettingsFieldProps {
|
|
|
384
386
|
children?: ReactNode;
|
|
385
387
|
className?: string;
|
|
386
388
|
}
|
|
389
|
+
/**
|
|
390
|
+
* `SettingsDefaultReset` — the end of a label line that says whether the value
|
|
391
|
+
* follows its default (owner 2026-10-06: "Default", and a "Reset" button).
|
|
392
|
+
*/
|
|
393
|
+
interface SettingsDefaultResetProps {
|
|
394
|
+
/** The field's visible label. The Reset button is named after it. */
|
|
395
|
+
label: string;
|
|
396
|
+
/** Is the value changed from its default? The host's to say. */
|
|
397
|
+
changed: boolean;
|
|
398
|
+
/** Clear the value back to its default. The part writes nothing itself. */
|
|
399
|
+
onReset: () => void;
|
|
400
|
+
/**
|
|
401
|
+
* What the default IS, in the merchant's words — "Site block gap — 24px".
|
|
402
|
+
* Shown as the tooltip: on Default as it is, on Reset as "Back to default: …".
|
|
403
|
+
* Read it live; a baked value goes stale the moment the default changes.
|
|
404
|
+
*/
|
|
405
|
+
detail?: string;
|
|
406
|
+
/** Read-only (a sent campaign): Reset is drawn, and cannot be pressed. */
|
|
407
|
+
disabled?: boolean;
|
|
408
|
+
/**
|
|
409
|
+
* Rare: make "Default" a press of its own, for a row a merchant cannot step
|
|
410
|
+
* out of by hand. The website's type sizes are the case (GG 2026-10-06, Q3):
|
|
411
|
+
* their default can be a responsive `clamp()` no slider position stores, so
|
|
412
|
+
* one press keeps the section's own built-in size. `title` says what the
|
|
413
|
+
* press does; it is the tooltip and the accessible name, after "Default".
|
|
414
|
+
* Left out, Default presses nothing — the usual case.
|
|
415
|
+
*/
|
|
416
|
+
defaultAction?: {
|
|
417
|
+
onPress: () => void;
|
|
418
|
+
title: string;
|
|
419
|
+
};
|
|
420
|
+
/** The host's words, from `useWords('panel', …)`. English when left out. */
|
|
421
|
+
words?: Partial<Pick<PanelWords, 'followsDefault' | 'reset' | 'resetName' | 'backToDefault'>>;
|
|
422
|
+
className?: string;
|
|
423
|
+
}
|
|
387
424
|
/**
|
|
388
425
|
* A switch has no text of its own, so without one of these it announces
|
|
389
426
|
* "switch, on" and nothing else. The union makes an unnamed switch a COMPILE
|
|
@@ -901,6 +938,37 @@ declare function SettingsField({ label, labelId, icon, value, hint, changed, htm
|
|
|
901
938
|
*/
|
|
902
939
|
declare function SettingsSwitch({ checked, onChange, disabled, id, ariaLabel, ariaLabelledBy, className, }: SettingsSwitchProps): react.JSX.Element;
|
|
903
940
|
|
|
941
|
+
/**
|
|
942
|
+
* The end of a setting's label line: **Default** while the value follows its
|
|
943
|
+
* default, a **Reset** button once it does not. ONE part, both editors.
|
|
944
|
+
*
|
|
945
|
+
* Owner, 2026-10-06. Before this the two editors said the same thing five ways:
|
|
946
|
+
* the website's "Inherits · Site" chip and "Overridden ↺" pill, its blue reset
|
|
947
|
+
* dot, and the email's "Default" mark, "Reset" link and "Reset to default"
|
|
948
|
+
* text. He picked the word **Default** for the resting state and the **Reset
|
|
949
|
+
* button** for the way back, in the same place on the row. It rides
|
|
950
|
+
* `SettingsField`'s `action` slot; `SettingsField` leaves its name alone (only a
|
|
951
|
+
* switch is named after the field).
|
|
952
|
+
*
|
|
953
|
+
* ONE ELEMENT, BOTH STATES. It is the same <button> whether it says Default or
|
|
954
|
+
* Reset, and that is for the keyboard: a press turns Reset into Default, and if
|
|
955
|
+
* Default were a <span> React would swap the node and focus would fall to the
|
|
956
|
+
* page, so the next Tab would start from the top. Resting, the button is
|
|
957
|
+
* `aria-disabled` and out of the tab order (`tabIndex={-1}`) — there is nothing
|
|
958
|
+
* to do there — but it keeps focus when a press has just put it there.
|
|
959
|
+
* `aria-disabled` and not `disabled`, because a `disabled` button drops focus
|
|
960
|
+
* too. `disabled` is kept for read-only, where a real disabled button is right.
|
|
961
|
+
*
|
|
962
|
+
* `defaultAction` is the one exception to "Default presses nothing": a row a
|
|
963
|
+
* merchant cannot leave by hand gets a press on the word itself, named after
|
|
964
|
+
* what it does (the website's type sizes, whose default can be a `clamp()`).
|
|
965
|
+
*
|
|
966
|
+
* CONTROLLED and hook-free like the rest of the panel: `changed` is the host's,
|
|
967
|
+
* and a press only asks the host to clear. The words are handed in (`words`),
|
|
968
|
+
* because a leaf never reads the strings provider.
|
|
969
|
+
*/
|
|
970
|
+
declare function SettingsDefaultReset({ label, changed, onReset, detail, disabled, defaultAction, words, className, }: SettingsDefaultResetProps): react.JSX.Element;
|
|
971
|
+
|
|
904
972
|
/**
|
|
905
973
|
* A DRILL-IN ROW — a name, a one-line preview, and a chevron (card 8819).
|
|
906
974
|
*
|
|
@@ -1085,4 +1153,4 @@ declare function SettingsCorners({ label, value, corners, onChange, id, classNam
|
|
|
1085
1153
|
*/
|
|
1086
1154
|
declare function SettingsAction({ label, onPress, disabled, note, done, doneLabel, icon, id, className, }: SettingsActionProps): react.JSX.Element;
|
|
1087
1155
|
|
|
1088
|
-
export { SettingsAction, type SettingsActionBaseProps, type SettingsActionNote, type SettingsActionProps, type SettingsCornerPoint, SettingsCorners, type SettingsCornersProps, SettingsDrill, type SettingsDrillProps, SettingsField, type SettingsFieldProps, SettingsGroup, SettingsGroupHead, type SettingsGroupHeadProps, type SettingsGroupProps, SettingsGroupSub, type SettingsGroupSubProps, SettingsPlacement, type SettingsPlacementPoint, type SettingsPlacementProps, SettingsSegmented, type SettingsSegmentedFit, type SettingsSegmentedOption, type SettingsSegmentedProps, SettingsSwitch, type SettingsSwitchBaseProps, type SettingsSwitchLabel, type SettingsSwitchProps, type SettingsTabItem, SettingsTabs, type SettingsTabsProps };
|
|
1156
|
+
export { SettingsAction, type SettingsActionBaseProps, type SettingsActionNote, type SettingsActionProps, type SettingsCornerPoint, SettingsCorners, type SettingsCornersProps, SettingsDefaultReset, type SettingsDefaultResetProps, SettingsDrill, type SettingsDrillProps, SettingsField, type SettingsFieldProps, SettingsGroup, SettingsGroupHead, type SettingsGroupHeadProps, type SettingsGroupProps, SettingsGroupSub, type SettingsGroupSubProps, SettingsPlacement, type SettingsPlacementPoint, type SettingsPlacementProps, SettingsSegmented, type SettingsSegmentedFit, type SettingsSegmentedOption, type SettingsSegmentedProps, SettingsSwitch, type SettingsSwitchBaseProps, type SettingsSwitchLabel, type SettingsSwitchProps, type SettingsTabItem, SettingsTabs, type SettingsTabsProps };
|
package/dist/panel/index.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
export{SETTINGS_BUCKETS,SettingsField,SettingsGroup,
|
|
2
|
-
return
|
|
3
|
-
return
|
|
4
|
-
return a("button",{type:"button",
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
/* @__PURE__ */
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return a("div",{id:
|
|
11
|
-
|
|
12
|
-
return
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
/* @__PURE__ */
|
|
1
|
+
export{SETTINGS_BUCKETS,SettingsField,SettingsGroup,SettingsTabs}from"../chunk-DICUMYMG.js";import{DEFAULT_PANEL_WORDS as e}from"../chunk-TRVZ7C23.js";export{SettingsSegmented,SettingsSwitch}from"../chunk-P6O363EE.js";import"../chunk-BZ5ASXOL.js";import{jsx as a,jsxs as l}from"react/jsx-runtime";function s({title:e,level:l=3,id:s,className:i}){/* @__PURE__ */
|
|
2
|
+
return a(`h${l}`,{id:s,className:i?`es-group-head ${i}`:"es-group-head",children:e})}function i({title:e,level:l=4,id:s,className:i}){/* @__PURE__ */
|
|
3
|
+
return a(`h${l}`,{id:s,className:i?`es-group-sub ${i}`:"es-group-sub",children:e})}function n({label:l,changed:s,onReset:i,detail:n,disabled:t,defaultAction:r,words:c,className:o}){const d={...e,...c},u=["es-default-reset"];if(s&&u.push("is-changed"),o&&u.push(o),!s&&r){u.push("is-pressable");const e=`${d.followsDefault}. ${r.title}`;/* @__PURE__ */
|
|
4
|
+
return a("button",{type:"button",className:u.join(" "),"aria-label":e,title:n?`${n}. ${r.title}`:r.title,disabled:t,onClick:r.onPress,children:d.followsDefault})}/* @__PURE__ */
|
|
5
|
+
return a("button",s?{type:"button",className:u.join(" "),"aria-label":d.resetName({label:l}),title:n?d.backToDefault({detail:n}):void 0,disabled:t,onClick:i,children:d.reset}:{type:"button",className:u.join(" "),"aria-disabled":"true",tabIndex:-1,title:n||void 0,children:d.followsDefault})}function t({name:e,preview:s,offPage:i,status:n,onOpen:t,id:r,className:c}){const o=null!=s;/* @__PURE__ */
|
|
6
|
+
return l("button",{type:"button",id:r,className:c?`es-drill ${c}`:"es-drill",onClick:t,children:[
|
|
7
|
+
/* @__PURE__ */l("span",{className:"es-drill-text",children:[
|
|
8
|
+
/* @__PURE__ */a("span",{className:"es-drill-name",children:e}),o?/* @__PURE__ */a("span",{className:i?"es-drill-preview is-offpage":"es-drill-preview",children:s}):null]}),n?/* @__PURE__ */a("span",{className:"es-drill-status",children:n}):null,
|
|
9
|
+
/* @__PURE__ */a("span",{className:"es-drill-chev","aria-hidden":"true"})]})}function r({label:e,value:l,points:s,onChange:i,id:n,className:t}){/* @__PURE__ */
|
|
10
|
+
return a("div",{id:n,className:"es-placement"+(t?` ${t}`:""),role:"radiogroup","aria-label":e,children:s.map(e=>{const s=l===e.value,n=!0===e.disabled;/* @__PURE__ */
|
|
11
|
+
return a("button",{type:"button",role:"radio","aria-checked":s,"aria-label":e.label,"aria-disabled":n||void 0,disabled:n,title:e.title??e.label,className:`es-placement-cell${s?" is-active":""}${n?" is-off":""}`,onClick:()=>i(e.value),children:e.icon?/* @__PURE__ */a("span",{className:"es-placement-icon","aria-hidden":"true",children:e.icon}):/* @__PURE__ */a("span",{className:"es-placement-dot"})},e.value)})})}function c({label:e,value:s,corners:i,onChange:n,id:t,className:r}){/* @__PURE__ */
|
|
12
|
+
return l("div",{id:t,className:"es-corners"+(r?` ${r}`:""),role:"group","aria-label":e,children:[
|
|
13
|
+
/* @__PURE__ */a("span",{className:"es-corners-frame","aria-hidden":"true"}),i.map(e=>{const l=s.includes(e.value),i=!0===e.disabled;/* @__PURE__ */
|
|
14
|
+
return a("button",{type:"button","aria-pressed":l,"aria-label":e.label,"aria-disabled":i||void 0,disabled:i,title:e.title??e.label,className:`es-corners-cell es-corners-${e.value}${l?" is-on":""}${i?" is-off":""}`,onClick:()=>n(l?s.filter(a=>a!==e.value):[...s,e.value]),children:/* @__PURE__ */a("span",{className:"es-corners-mark","aria-hidden":"true"})},e.value)})]})}function o({label:e,onPress:s,disabled:i,note:n,done:t,doneLabel:r,icon:c,id:o,className:d}){const u=void 0===r?e:r,p=t?u:e,m=null!=n,b=m&&o?`${o}-note`:void 0;/* @__PURE__ */
|
|
15
|
+
return l("div",{className:d?`es-action-wrap ${d}`:"es-action-wrap",children:[
|
|
16
|
+
/* @__PURE__ */l("button",{type:"button",id:o,className:t?"es-action is-done":"es-action",disabled:i,"aria-describedby":b,onClick:s,children:[c?/* @__PURE__ */a("span",{className:"es-action-icon","aria-hidden":"true",children:c}):null,
|
|
17
|
+
/* @__PURE__ */a("span",{className:"es-action-label",children:p})]}),m?/* @__PURE__ */a("p",{id:b,className:"es-action-note",children:n}):null,
|
|
18
|
+
/* @__PURE__ */a("span",{className:"es-action-live",role:"status","aria-live":"polite",children:t?u:null})]})}export{o as SettingsAction,c as SettingsCorners,n as SettingsDefaultReset,t as SettingsDrill,s as SettingsGroupHead,i as SettingsGroupSub,r as SettingsPlacement};
|
package/dist/panel/panel.css
CHANGED
|
@@ -911,14 +911,69 @@ button.es-group-head:hover { background: var(--es-chip-bg); }
|
|
|
911
911
|
min-width: 0;
|
|
912
912
|
}
|
|
913
913
|
|
|
914
|
+
/* The hint sits FLUSH LEFT, on the same edge as the row's icon and control —
|
|
915
|
+
not indented under the label's words (owner ruling, 2026-10-06; it was
|
|
916
|
+
24px, glyph + gap, until then). `tests/panel-hint-flush-left` holds it. */
|
|
914
917
|
.es-field-hint {
|
|
915
918
|
margin: 8px 0 0;
|
|
916
|
-
padding-left: 24px;
|
|
917
919
|
font-size: 11px;
|
|
918
920
|
line-height: 1.45;
|
|
919
921
|
color: var(--es-muted);
|
|
920
922
|
}
|
|
921
923
|
|
|
924
|
+
/* ── Default / Reset: the end of a label line ─────────────────────────────────
|
|
925
|
+
Owner 2026-10-06: a value that follows its default says "Default", and a
|
|
926
|
+
changed one offers a "Reset" button, in the same place. ONE element in both
|
|
927
|
+
states (see SettingsDefaultReset.tsx), so both states share the box: same
|
|
928
|
+
font, same padding, same height. Only the colour and the press change, and
|
|
929
|
+
the row does not jump when a press turns one into the other.
|
|
930
|
+
|
|
931
|
+
It sits at the END of the label line. With a value on the row, the value's
|
|
932
|
+
own `margin-left: auto` has already pushed both right, and the gap between
|
|
933
|
+
them is the head's own 8px; with no value, the part pushes itself. */
|
|
934
|
+
.es-default-reset {
|
|
935
|
+
appearance: none;
|
|
936
|
+
-webkit-appearance: none;
|
|
937
|
+
flex: none;
|
|
938
|
+
margin: 0 -4px 0 0;
|
|
939
|
+
padding: 2px 4px;
|
|
940
|
+
border: 0;
|
|
941
|
+
border-radius: 4px;
|
|
942
|
+
background: transparent;
|
|
943
|
+
font: inherit;
|
|
944
|
+
font-size: 11px;
|
|
945
|
+
font-weight: 500;
|
|
946
|
+
line-height: 12px;
|
|
947
|
+
color: var(--es-muted);
|
|
948
|
+
white-space: nowrap;
|
|
949
|
+
cursor: default;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
.es-default-reset.is-changed {
|
|
953
|
+
color: var(--es-accent-text);
|
|
954
|
+
cursor: pointer;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
.es-default-reset.is-changed:hover { background: var(--es-hover); }
|
|
958
|
+
|
|
959
|
+
.es-default-reset.is-changed:disabled {
|
|
960
|
+
color: var(--es-disabled);
|
|
961
|
+
background: transparent;
|
|
962
|
+
cursor: default;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
.es-default-reset.is-pressable { cursor: pointer; }
|
|
966
|
+
.es-default-reset.is-pressable:hover { background: var(--es-hover); color: var(--es-text-secondary); }
|
|
967
|
+
.es-default-reset.is-pressable:disabled { color: var(--es-disabled); background: transparent; cursor: default; }
|
|
968
|
+
|
|
969
|
+
.es-default-reset:focus-visible {
|
|
970
|
+
outline: 2px solid var(--es-accent-ring);
|
|
971
|
+
outline-offset: 1px;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
.es-field-head > .es-default-reset { margin-left: auto; }
|
|
975
|
+
.es-field-value + .es-default-reset { margin-left: 0; }
|
|
976
|
+
|
|
922
977
|
/* A boolean row is ONE line (card 8801, Option A). The switch rides the label
|
|
923
978
|
line in the `action` slot, so there is no control row under it — and with no
|
|
924
979
|
control row, the label line's 7px bottom margin would be a gap to nothing.
|