efficient-easel 0.99.1 → 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.
Files changed (110) hide show
  1. package/README.md +281 -36
  2. package/dist/autosave/index.d.ts +12 -6
  3. package/dist/blocks/index.d.ts +2 -2
  4. package/dist/canvas/index.d.ts +3 -3
  5. package/dist/canvas/index.js +1 -1
  6. package/dist/chrome/chrome.css +96 -29
  7. package/dist/chrome/index.d.ts +72 -19
  8. package/dist/chrome/index.js +17 -21
  9. package/dist/chunk-3EDSDPIP.js +1 -0
  10. package/dist/chunk-67GNT6PF.js +1 -0
  11. package/dist/{chunk-PI26YXYD.js → chunk-6EIVTKZZ.js} +21 -14
  12. package/dist/chunk-A5UK76FC.js +6 -0
  13. package/dist/chunk-AZT7V4UM.js +10 -0
  14. package/dist/chunk-DICUMYMG.js +12 -0
  15. package/dist/chunk-HDVD27VB.js +1 -0
  16. package/dist/chunk-JQM3V7XQ.js +1 -0
  17. package/dist/{chunk-TDA4HH54.js → chunk-JUQ6YJCG.js} +1 -1
  18. package/dist/chunk-LA54QLP5.js +1 -0
  19. package/dist/chunk-LGKQXSKO.js +16 -0
  20. package/dist/chunk-M6F6LS3H.js +1 -0
  21. package/dist/chunk-MH5GUZFQ.js +9 -0
  22. package/dist/chunk-P6O363EE.js +6 -0
  23. package/dist/chunk-PAELDYYS.js +1 -0
  24. package/dist/{chunk-JYPOOHII.js → chunk-QKVR3K73.js} +3 -3
  25. package/dist/chunk-SAJUCHOX.js +1 -0
  26. package/dist/chunk-TRVZ7C23.js +1 -0
  27. package/dist/chunk-V7GIIXFB.js +16 -0
  28. package/dist/{chunk-IBOFSS56.js → chunk-WZ7TGAAT.js} +1 -1
  29. package/dist/chunk-ZJEBF4NB.js +1 -0
  30. package/dist/copy/copy.css +388 -5
  31. package/dist/copy/index.d.ts +271 -8
  32. package/dist/copy/index.js +31 -10
  33. package/dist/data/index.d.ts +4 -4
  34. package/dist/drag/drag.css +10 -1
  35. package/dist/drag/index.d.ts +102 -11
  36. package/dist/drag/index.js +18 -17
  37. package/dist/{editor/editor.css → easel/easel.css} +16 -1
  38. package/dist/{editor → easel}/index.d.ts +146 -51
  39. package/dist/easel/index.js +9 -0
  40. package/dist/edit-lock/index.d.ts +3 -3
  41. package/dist/edit-lock/index.js +1 -1
  42. package/dist/fields/fields.css +103 -26
  43. package/dist/fields/index.d.ts +51 -13
  44. package/dist/fields/index.js +30 -46
  45. package/dist/gold/index.js +1 -1
  46. package/dist/icons/index.d.ts +10 -1
  47. package/dist/icons/index.js +1 -1
  48. package/dist/index.d.ts +3 -3
  49. package/dist/index.js +1 -1
  50. package/dist/markup/index.d.ts +94 -10
  51. package/dist/markup/index.js +1 -1
  52. package/dist/menu/index.d.ts +61 -6
  53. package/dist/menu/index.js +15 -15
  54. package/dist/menu/menu.css +17 -6
  55. package/dist/node-ops/index.d.ts +9 -9
  56. package/dist/node-ops/index.js +1 -1
  57. package/dist/panel/index.d.ts +73 -5
  58. package/dist/panel/index.js +18 -16
  59. package/dist/panel/panel.css +56 -1
  60. package/dist/pick/index.d.ts +42 -4
  61. package/dist/pick/index.js +7 -14
  62. package/dist/pick/pick.css +14 -0
  63. package/dist/rail/index.d.ts +6 -6
  64. package/dist/rail/index.js +1 -1
  65. package/dist/rail/rail.css +1 -1
  66. package/dist/strings/catalogue.d.ts +24 -40
  67. package/dist/strings/catalogue.js +1 -1
  68. package/dist/strings/index.d.ts +25 -23
  69. package/dist/strings/index.js +2 -2
  70. package/dist/style-clipboard/index.d.ts +5 -5
  71. package/dist/style-clipboard/index.js +1 -1
  72. package/dist/text-styles/index.d.ts +328 -0
  73. package/dist/text-styles/index.js +38 -0
  74. package/dist/text-styles/text-styles.css +309 -0
  75. package/dist/tree/index.d.ts +44 -3
  76. package/dist/tree/index.js +1 -1
  77. package/dist/tree/tree.css +71 -0
  78. package/dist/{types-CnRzp9oc.d.ts → types-DGcCsxs8.d.ts} +79 -12
  79. package/dist/{types-DNrn_w4V.d.ts → types-DWS-s9z-.d.ts} +10 -10
  80. package/dist/{types-pGBXevBU.d.ts → types-Djdq6Xkd.d.ts} +15 -8
  81. package/dist/{types-Dy6oR6Z2.d.ts → types-E3HrTPg2.d.ts} +20 -14
  82. package/dist/{types-C7HFvdtk.d.ts → types-VqL6t5HH.d.ts} +82 -5
  83. package/dist/{watch-DmasQA61.d.ts → watch-BTYOJpQU.d.ts} +19 -2
  84. package/dist/{wordFit-9B9nqyf3.d.ts → wordFit-D4S36PvB.d.ts} +1 -1
  85. package/dist/{words-DagJdwIA.d.ts → words-AHHfiaRY.d.ts} +31 -2
  86. package/dist/{words-D9ChvbNC.d.ts → words-BDignMl_.d.ts} +1 -1
  87. package/dist/{words-Cqmyey0s.d.ts → words-BMzV04b1.d.ts} +2 -2
  88. package/dist/words-CJjUKcV5.d.ts +37 -0
  89. package/dist/words-CRYvAiHX.d.ts +105 -0
  90. package/dist/{words-B2YLaKEr.d.ts → words-CnnHb9jo.d.ts} +1 -1
  91. package/dist/{words-CD3WADyb.d.ts → words-CzoyDNHm.d.ts} +4 -0
  92. package/dist/{words-bOMsUJk9.d.ts → words-DBq7vfHy.d.ts} +59 -6
  93. package/dist/{words-DOhF-4D4.d.ts → words-DMG4dYq2.d.ts} +1 -1
  94. package/dist/{words-BK-npta-.d.ts → words-DgHcY5qB.d.ts} +10 -4
  95. package/dist/{words-B87GIFWH.d.ts → words-is_Vy6hn.d.ts} +1 -1
  96. package/package.json +14 -8
  97. package/dist/chunk-66CKSTXP.js +0 -14
  98. package/dist/chunk-67VZRQLG.js +0 -13
  99. package/dist/chunk-6PS6STGA.js +0 -1
  100. package/dist/chunk-7A7O2JWQ.js +0 -1
  101. package/dist/chunk-H4B72XCJ.js +0 -1
  102. package/dist/chunk-JE63JHKO.js +0 -1
  103. package/dist/chunk-KDIVA6WD.js +0 -1
  104. package/dist/chunk-MCLBHENY.js +0 -1
  105. package/dist/chunk-QB3BZIRA.js +0 -5
  106. package/dist/chunk-SJQG5ENJ.js +0 -1
  107. package/dist/chunk-SUO5627P.js +0 -1
  108. package/dist/chunk-ZT7M24XD.js +0 -1
  109. package/dist/editor/index.js +0 -9
  110. /package/dist/{chunk-GGTPNGFJ.js → chunk-AELNNAFM.js} +0 -0
@@ -0,0 +1,328 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { T as TextStylesWords } from '../words-CRYvAiHX.js';
4
+ export { D as DEFAULT_TEXT_STYLES_WORDS } from '../words-CRYvAiHX.js';
5
+
6
+ /**
7
+ * The text styles as data (plan step E6): what a style is, the list both
8
+ * editors start from, the design's ranges, and the pure changes the list
9
+ * makes. No React and no browser global, so P3 and S2 can use it where they
10
+ * read and write their own storage.
11
+ *
12
+ * What this does NOT know is where a host keeps its styles, or what its old
13
+ * values were called. Turning an email's "Label" into Body 5, or the website's
14
+ * "Tight" into 95 % / −0.01 em, is the host's (P3, S2). The units here are the
15
+ * panel's: px, and % for line spacing.
16
+ */
17
+
18
+ type TextStyleKind = 'heading' | 'body';
19
+ /** As typed · Uppercase · Lowercase. No value = Auto where the host offers it, As typed where not. */
20
+ type TextStyleCase = 'none' | 'upper' | 'lower';
21
+ /**
22
+ * One style's own settings. A missing key has nothing of its own: it is Auto
23
+ * where the host offers Auto for that setting, and otherwise shows the host's
24
+ * `fallback`, grey.
25
+ */
26
+ interface TextStyleValues {
27
+ /** A font's `value`, from the host's list. */
28
+ font?: string;
29
+ /** A theme colour's `key`, or a `#rrggbb`. */
30
+ color?: string;
31
+ /** px. */
32
+ size?: number;
33
+ /** 100–900. */
34
+ weight?: number;
35
+ italic?: boolean;
36
+ /** % of the font size; below 100 is tighter than the type. */
37
+ lineSpacing?: number;
38
+ /** px; may be negative. */
39
+ letterSpacing?: number;
40
+ /** px. */
41
+ paragraphSpacing?: number;
42
+ letterCase?: TextStyleCase;
43
+ /** px; headings only — the largest a heading is drawn anywhere on the site. */
44
+ largestSize?: number;
45
+ }
46
+ interface TextStyle {
47
+ /** The host's, stable: the list never renames an id. */
48
+ id: string;
49
+ kind: TextStyleKind;
50
+ /** Stored, so the host's words: "Heading 1", "Body 5". */
51
+ name: string;
52
+ values: TextStyleValues;
53
+ }
54
+ /** A font the host offers. */
55
+ interface TextStyleFont {
56
+ value: string;
57
+ label: string;
58
+ /** The CSS `font-family` the list draws the name and the preview in. */
59
+ family: string;
60
+ /** `false` when this font has no italic loaded: the Italic button is off. */
61
+ italic?: boolean;
62
+ }
63
+ /** A theme colour the host offers, stored by its key. */
64
+ interface TextStyleColour {
65
+ key: string;
66
+ name: string;
67
+ /** `#rrggbb`. */
68
+ value: string;
69
+ }
70
+ /** Every setting an open style can show, in the order it shows them. */
71
+ declare const TEXT_STYLE_SETTINGS: readonly ["font", "color", "size", "weight", "emphasis", "lineSpacing", "letterSpacing", "paragraphSpacing", "letterCase", "largestSize"];
72
+ type TextStyleSetting = (typeof TEXT_STYLE_SETTINGS)[number];
73
+ /** The settings an Email styles list shows — the campaign design page. */
74
+ declare const CAMPAIGN_TEXT_STYLE_SETTINGS: readonly TextStyleSetting[];
75
+ /** The settings that may be Auto, when the host says so (Site styles). */
76
+ type TextStyleAuto = 'size' | 'lineSpacing' | 'letterSpacing' | 'letterCase';
77
+ /** The kinds, in the order the list draws them. */
78
+ declare const TEXT_STYLE_KINDS: readonly TextStyleKind[];
79
+ /** At most this many of EACH kind (Lewis, 2026-10-02). */
80
+ declare const TEXT_STYLE_MAX = 7;
81
+ /** How many of each the list starts with: Heading 1–3, Body 1–5. */
82
+ declare const TEXT_STYLE_DEFAULT_COUNT: Readonly<Record<TextStyleKind, number>>;
83
+ /** A number setting's slider: its travel, the typed limits, the steps and the unit. */
84
+ interface TextStyleRange {
85
+ /** The lowest a typed value may be; `undefined` = no limit. */
86
+ min: number | undefined;
87
+ /** The highest a typed value may be; `undefined` = no limit. */
88
+ max: number | undefined;
89
+ sliderMin: number;
90
+ sliderMax: number;
91
+ step: number;
92
+ shiftStep: number;
93
+ unit: string;
94
+ }
95
+ /**
96
+ * The design's numbers (campaign page, "the three boxes' rules" and the slider
97
+ * ranges; website page §5). A typed value may go past the slider; only `min`
98
+ * and `max` hold it. Line spacing down to 50 % and letter spacing down to
99
+ * −10 px are Lewis's (2026-10-06).
100
+ */
101
+ declare const TEXT_STYLE_RANGES: {
102
+ readonly size: TextStyleRange;
103
+ readonly weight: {
104
+ readonly min: 100;
105
+ readonly max: 900;
106
+ readonly sliderMin: 300;
107
+ readonly sliderMax: 900;
108
+ readonly step: 100;
109
+ readonly shiftStep: 100;
110
+ readonly unit: "";
111
+ };
112
+ readonly lineSpacing: {
113
+ readonly min: 50;
114
+ readonly max: 250;
115
+ readonly sliderMin: 50;
116
+ readonly sliderMax: 250;
117
+ readonly step: 1;
118
+ readonly shiftStep: 10;
119
+ readonly unit: "%";
120
+ };
121
+ readonly letterSpacing: {
122
+ readonly min: -10;
123
+ readonly max: undefined;
124
+ readonly sliderMin: -5;
125
+ readonly sliderMax: 10;
126
+ readonly step: 0.1;
127
+ readonly shiftStep: 1;
128
+ readonly unit: "px";
129
+ };
130
+ readonly paragraphSpacing: {
131
+ readonly min: 0;
132
+ readonly max: undefined;
133
+ readonly sliderMin: 0;
134
+ readonly sliderMax: 48;
135
+ readonly step: 1;
136
+ readonly shiftStep: 10;
137
+ readonly unit: "px";
138
+ };
139
+ readonly largestSize: TextStyleRange;
140
+ };
141
+ type TextStyleNumber = keyof typeof TEXT_STYLE_RANGES;
142
+ /** Heading 1–3 then Body 1–5, ids `heading-1` … `body-5`, no values: the host fills them. */
143
+ declare function defaultTextStyles(name?: (v: {
144
+ kind: TextStyleKind;
145
+ n: number;
146
+ }) => string): TextStyle[];
147
+ /** Whether another style of this kind fits under the cap. */
148
+ declare function canAddTextStyle(styles: readonly TextStyle[], kind: TextStyleKind, max?: number): boolean;
149
+ /**
150
+ * Whether a style is one the list starts with (Heading 1–3, Body 1–5), which
151
+ * can never be deleted. Decided by the fixed id (`heading-1` … `body-5`),
152
+ * never by the name: a host's words name the styles.
153
+ */
154
+ declare function isDefaultTextStyle(style: TextStyle): boolean;
155
+ /**
156
+ * A new style of `kind`, after the last one of that kind: a copy of that
157
+ * one's values, named the next free number ("Heading 4"). `null` at the cap.
158
+ * The list given is not changed.
159
+ *
160
+ * The name and the id are chosen apart. The name is the next number after
161
+ * the styles of its kind, skipping a name already taken, so after a delete
162
+ * the names have no gap. The id is above every id of its kind
163
+ * (`heading-7` when `heading-6` is there), and the name's number when that
164
+ * is higher. So an id freed by a delete is used again only when it was the
165
+ * highest of its kind, and then the new style is a copy of the one that
166
+ * id's text was already drawn as (`resolveTextStyle`): nothing on the page
167
+ * jumps.
168
+ *
169
+ * `make` lets a host build the new style itself (its own ids); it gets the
170
+ * kind and the number the new style is called.
171
+ */
172
+ declare function addTextStyle(styles: readonly TextStyle[], kind: TextStyleKind, options?: {
173
+ max?: number;
174
+ name?: (v: {
175
+ kind: TextStyleKind;
176
+ n: number;
177
+ }) => string;
178
+ make?: (kind: TextStyleKind, n: number) => TextStyle;
179
+ }): {
180
+ styles: TextStyle[];
181
+ added: TextStyle;
182
+ } | null;
183
+ /**
184
+ * The list without one ADDED style. `null` for a default (they are never
185
+ * deleted) or an id not in the list. The list given is not changed.
186
+ *
187
+ * The styles of its kind after it close the gap by NAME — "Heading 5" becomes
188
+ * "Heading 4" — when their name is still the number of their place. Their
189
+ * ids never change, so text on them stays on them. Text on the deleted one
190
+ * is not rewritten: `resolveTextStyle` draws it as the nearest earlier style
191
+ * of its kind.
192
+ *
193
+ * `name` is the host's words for a style's name, as `addTextStyle` takes it.
194
+ */
195
+ declare function removeTextStyle(styles: readonly TextStyle[], id: string, options?: {
196
+ name?: (v: {
197
+ kind: TextStyleKind;
198
+ n: number;
199
+ }) => string;
200
+ }): {
201
+ styles: TextStyle[];
202
+ removed: TextStyle;
203
+ } | null;
204
+ /**
205
+ * The style text on `id` draws as: that style, or — when it was deleted —
206
+ * the nearest EARLIER style of its kind still in the list (`heading-5` gone:
207
+ * Heading 4's id, else `heading-3`). The defaults are never deleted, so an
208
+ * id the model made always finds one. `undefined` for an id the model did
209
+ * not make (a host's own, through `make`): that host resolves its own.
210
+ *
211
+ * Read every stored style id through this, so a deleted style's text keeps
212
+ * drawing — in a saved page and in a sent email alike — and nothing stored
213
+ * is rewritten.
214
+ */
215
+ declare function resolveTextStyle(styles: readonly TextStyle[], id: string): TextStyle | undefined;
216
+ /**
217
+ * The list with one style's values changed. A key given as `undefined` is
218
+ * removed: the setting has nothing of its own again. Nothing is changed in
219
+ * place, and the other styles are the same objects.
220
+ */
221
+ declare function updateTextStyle(styles: readonly TextStyle[], id: string, patch: Partial<TextStyleValues>): TextStyle[];
222
+ /** The size a style's NAME is drawn at in the list: half its size plus 6, kept within 12–20 px. */
223
+ declare function textStyleRowSize(px: number): number;
224
+ /** A weight's name, by the hundred it is nearest. */
225
+ declare function weightWord(weight: number, words: TextStylesWords): string;
226
+ /**
227
+ * A row's grey line, "Playfair Display · 28 px · Semibold". `autoSize`: a
228
+ * size with no number reads the Auto word. A weight with none is Regular.
229
+ */
230
+ declare function describeTextStyle(values: TextStyleValues, fonts: readonly TextStyleFont[], words: TextStylesWords, options?: {
231
+ autoSize?: boolean;
232
+ }): string;
233
+
234
+ interface TextStylesListProps {
235
+ /** Headings and bodies, in the host's order; the list draws headings first. */
236
+ styles: readonly TextStyle[];
237
+ /** The whole list, after one change: a value set, a style added. */
238
+ onChange: (next: TextStyle[]) => void;
239
+ /** The fonts the Font row offers. */
240
+ fonts: readonly TextStyleFont[];
241
+ /** The theme's colours, offered in the Color row before Any color. */
242
+ colours?: readonly TextStyleColour[];
243
+ /**
244
+ * Which settings an open style shows. They are always drawn in the
245
+ * design's order (`TEXT_STYLE_SETTINGS`). Left out: the campaign's set.
246
+ * Site styles adds `emphasis` (B · I) and `largestSize` (headings only).
247
+ */
248
+ settings?: readonly TextStyleSetting[];
249
+ /** Which settings may be Auto: "no number, each section draws its own" (Site styles). */
250
+ auto?: readonly TextStyleAuto[];
251
+ /** The numbers a style shows, grey, for a setting with nothing of its own. */
252
+ fallback?: (style: TextStyle) => TextStyleValues;
253
+ /** The line the open style's preview is drawn in. */
254
+ sample?: string;
255
+ /** Which style is open — the host's, when given (one at a time). */
256
+ openId?: string | null;
257
+ /** Which style is open at first, when the host does not hold it. */
258
+ defaultOpenId?: string | null;
259
+ onOpenChange?: (id: string | null) => void;
260
+ /** The host's own new style (its own id). `n` is the number it would be called. */
261
+ onAdd?: (kind: TextStyleKind, n: number) => TextStyle;
262
+ /**
263
+ * Buttons the host puts at the end of a style's row, OUTSIDE the button that
264
+ * opens it (a button may not hold a button). They lie over the row's end and
265
+ * show on hover and on focus, as the Layers row's do, after Delete.
266
+ */
267
+ rowActions?: (style: TextStyle) => ReactNode;
268
+ /**
269
+ * Given: an ADDED style's row has Delete (the trash, red, as the Layers
270
+ * delete). Heading 1–3 and Body 1–5 never have it. A press deletes at once
271
+ * with no confirm — the host's undo brings it back, so a host passes this
272
+ * only once its undo covers the styles. The new list still comes through
273
+ * `onChange`, as one change; this only says which style went.
274
+ */
275
+ onRemove?: (removed: TextStyle) => void;
276
+ /** At most this many of each kind. */
277
+ max?: number;
278
+ readOnly?: boolean;
279
+ }
280
+ /**
281
+ * ONE TEXT-STYLES LIST FOR BOTH EDITORS (plan step E6): Email styles → Text
282
+ * and Site styles → Text, drawn once. Design final 2026-10-06, on the
283
+ * campaign page (the shared list, Email styles option C) and the website page
284
+ * (§3, Site styles).
285
+ *
286
+ * Headings a group heading, then its styles
287
+ * ▸ Heading 1 the name, in its own face (≤ 20 px here)
288
+ * Playfair Display · 40 px · … the grey line under it
289
+ * + Add heading 3 / 7 grey at 7
290
+ *
291
+ * A style opens to a one-line preview, Font, Color (the theme's colours and
292
+ * Any color, which opens the pickers' window), sliders (E4's FieldSlider) and
293
+ * Letter case. What is on offer is the host's: `settings`, `auto`.
294
+ *
295
+ * The data and the words are the host's. The list does not know where an
296
+ * email or a storefront keeps its styles, nor what they were once called.
297
+ */
298
+ declare function TextStylesList({ styles, onChange, fonts, colours, settings, auto, fallback, sample, openId, defaultOpenId, onOpenChange, onAdd, rowActions, onRemove, max, readOnly, }: TextStylesListProps): react.JSX.Element;
299
+
300
+ interface ColourSwatchRowProps {
301
+ /** The stored colour: a theme colour's `key`, a `#rrggbb`, or `undefined` for none of its own. */
302
+ value: string | undefined;
303
+ /** The `#rrggbb` the row reads out, or `undefined` while there is none (the host resolves a key to its value). */
304
+ hex: string | undefined;
305
+ /** The theme's colours, drawn as swatches in this order. */
306
+ colours: readonly TextStyleColour[];
307
+ /** The row's name ("Color"): its caption and the swatch group's accessible name. */
308
+ label: string;
309
+ /** What the readout says with no colour of its own ("Follows the style"). */
310
+ followsWord: string;
311
+ /** The last swatch's name ("Any color"). */
312
+ anyWord: string;
313
+ /** A swatch writes its `key`; the colour window writes a `#rrggbb`. */
314
+ onChange: (next: string) => void;
315
+ readOnly?: boolean;
316
+ }
317
+ /**
318
+ * THE COLOR ROW — the theme's colours in a row, then Any color, which opens
319
+ * the one colour window (`ColourWindow`). Exported so a second host draws the
320
+ * same row instead of a copy (plan S2: the website's part panel; design final
321
+ * 2026-10-06). The text-styles list draws it through this export, unchanged.
322
+ *
323
+ * What a press means is the host's: a swatch writes its key, the window a
324
+ * hex. Clearing, and what "nothing chosen" reads, are the host's too.
325
+ */
326
+ declare function ColourSwatchRow({ value, hex, colours, label, followsWord, anyWord, onChange, readOnly }: ColourSwatchRowProps): react.JSX.Element;
327
+
328
+ export { CAMPAIGN_TEXT_STYLE_SETTINGS, ColourSwatchRow, type ColourSwatchRowProps, TEXT_STYLE_DEFAULT_COUNT, TEXT_STYLE_KINDS, TEXT_STYLE_MAX, TEXT_STYLE_RANGES, TEXT_STYLE_SETTINGS, type TextStyle, type TextStyleAuto, type TextStyleCase, type TextStyleColour, type TextStyleFont, type TextStyleKind, type TextStyleNumber, type TextStyleRange, type TextStyleSetting, type TextStyleValues, TextStylesList, type TextStylesListProps, TextStylesWords, addTextStyle, canAddTextStyle, defaultTextStyles, describeTextStyle, isDefaultTextStyle, removeTextStyle, resolveTextStyle, textStyleRowSize, updateTextStyle, weightWord };
@@ -0,0 +1,38 @@
1
+ import{DEFAULT_TEXT_STYLES_WORDS as e}from"../chunk-M6F6LS3H.js";export{DEFAULT_TEXT_STYLES_WORDS}from"../chunk-M6F6LS3H.js";import{FieldSelect as t,FieldSlider as n}from"../chunk-LGKQXSKO.js";import{hexReadout as s,ColourWindow as i,typedHex as a}from"../chunk-AZT7V4UM.js";import{DEFAULT_PICK_WORDS as l}from"../chunk-ZAOM6PFH.js";import"../chunk-A5UK76FC.js";import"../chunk-LA54QLP5.js";import{useWords as r}from"../chunk-MFIRYGHW.js";import{ChevronIcon as o,TrashIcon as d,BoldIcon as c,ItalicIcon as u,CaseLowerIcon as m,CaseUpperIcon as p,CaseNormalIcon as f}from"../chunk-6EIVTKZZ.js";import{useState as h,useRef as v,useId as g,useEffect as y,Fragment as k}from"react";import{jsxs as b,jsx as w,Fragment as x}from"react/jsx-runtime";var S=["font","color","size","weight","emphasis","lineSpacing","letterSpacing","paragraphSpacing","letterCase","largestSize"],N=["font","color","size","weight","lineSpacing","letterSpacing","paragraphSpacing","letterCase"],C=["heading","body"],M=7,O={heading:3,body:5},z={min:5,max:void 0,sliderMin:12,sliderMax:72,step:1,shiftStep:10,unit:"px"},$={size:z,weight:{min:100,max:900,sliderMin:300,sliderMax:900,step:100,shiftStep:100,unit:""},lineSpacing:{min:50,max:250,sliderMin:50,sliderMax:250,step:1,shiftStep:10,unit:"%"},letterSpacing:{min:-10,max:void 0,sliderMin:-5,sliderMax:10,step:.1,shiftStep:1,unit:"px"},paragraphSpacing:{min:0,max:void 0,sliderMin:0,sliderMax:48,step:1,shiftStep:10,unit:"px"},largestSize:z},A=(e,t)=>`${"heading"===e?"Heading":"Body"} ${t}`;function L(e=({kind:e,n:t})=>A(e,t)){return C.flatMap(t=>Array.from({length:O[t]},(n,s)=>({id:`${t}-${s+1}`,kind:t,name:e({kind:t,n:s+1}),values:{}})))}function j(e,t,n=M){return e.filter(e=>e.kind===t).length<n}function F(e,t){const n=new RegExp(`^${t}-([1-9]\\d*)$`).exec(e);return n?Number(n[1]):void 0}function E(e){const t=F(e.id,e.kind);return void 0!==t&&t<=O[e.kind]}function T(e,t,n={}){const{max:s=M,name:i=({kind:e,n:t})=>A(e,t),make:a}=n;if(!j(e,t,s))return null;const l=e.filter(e=>e.kind===t);let r=l.length+1;const o=new Set(e.map(e=>e.name));for(;o.has(i({kind:t,n:r}));)r+=1;const d=new Set(e.map(e=>e.id));let c=Math.max(r,...l.map(e=>(F(e.id,t)??0)+1));for(;d.has(`${t}-${c}`);)c+=1;const u=l.at(-1),m=a?a(t,r):{id:`${t}-${c}`,kind:t,name:i({kind:t,n:r}),values:{...u?.values??{}}},p=u?e.indexOf(u)+1:"heading"===t?0:e.length;return{styles:[...e.slice(0,p),m,...e.slice(p)],added:m}}function W(e,t,n={}){const{name:s=({kind:e,n:t})=>A(e,t)}=n,i=e.find(e=>e.id===t);if(!i||E(i))return null;const a=e.filter(e=>e.kind===i.kind),l=a.indexOf(i),r=/* @__PURE__ */new Map;return a.forEach((e,t)=>{t>l&&e.name===s({kind:e.kind,n:t+1})&&r.set(e,s({kind:e.kind,n:t}))}),{styles:e.filter(e=>e!==i).map(e=>r.has(e)?{...e,name:r.get(e)}:e),removed:i}}function I(e,t){const n=e.find(e=>e.id===t);if(n)return n;for(const n of C){const s=F(t,n);if(void 0===s)continue;let i;for(const t of e){const e=t.kind===n?F(t.id,n):void 0;void 0!==e&&e<s&&(!i||e>i.n)&&(i={n:e,style:t})}return i?.style}}function D(e,t,n){return e.map(e=>{if(e.id!==t)return e;const s={...e.values};for(const[e,t]of Object.entries(n))void 0===t?delete s[e]:s[e]=t;return{...e,values:s}})}function H(e){return Math.min(20,Math.max(12,Math.round(.5*e+6)))}function B(e,t){const n=t.weights;return[n.thin,n.extraLight,n.light,n.regular,n.medium,n.semibold,n.bold,n.extraBold,n.black][Math.min(8,Math.max(0,Math.round(e/100)-1))]}function K(e,t,n,s={}){const i=t.find(t=>t.value===e.font)?.label??e.font??"",a=void 0===e.size?s.autoSize?n.auto:"":n.px({n:e.size});return n.summary({font:i,size:a,weight:B(e.weight??400,n)})}function R({value:e,hex:t,colours:n,label:a,followsWord:o,anyWord:d,onChange:c,readOnly:u=!1}){const m=r("pick",l),[p,f]=h(!1),k=v(null),x=v(null),S=g(),N=void 0!==e&&n.some(t=>t.key===e),C=void 0!==e&&!N;return y(()=>{if(!p)return;const e=k.current?.ownerDocument;if(!e)return;const t=e=>{k.current&&!k.current.contains(e.target)&&f(!1)};return e.addEventListener("click",t,!0),()=>e.removeEventListener("click",t,!0)},[p]),/* @__PURE__ */b("div",{ref:k,className:"es-ts-set","data-ts-setting":"color",children:[
2
+ /* @__PURE__ */b("div",{className:"es-ts-top",children:[
3
+ /* @__PURE__ */w("span",{className:"es-ts-sub",children:a}),
4
+ /* @__PURE__ */w("span",{className:"es-ts-colour-read",children:t?s(t):o})]}),
5
+ /* @__PURE__ */b("div",{className:"es-ts-swatches",role:"group","aria-label":a,children:[n.map(t=>/* @__PURE__ */w("button",{type:"button",className:"es-ts-swatch",style:{background:t.value},"aria-label":t.name,title:`${t.name} · ${s(t.value)}`,"aria-pressed":e===t.key,disabled:u,onClick:()=>c(t.key)},t.key)),
6
+ /* @__PURE__ */w("button",{ref:x,type:"button",className:"es-ts-swatch es-ts-swatch--any","aria-label":d,title:d,"aria-pressed":C,"aria-expanded":p,"aria-controls":p?S:void 0,disabled:u,onClick:()=>f(e=>!e)})]}),p?/* @__PURE__ */w(i,{id:S,value:t??"",onChange:e=>c(e),label:a,words:m.colour,readOnly:u,onClose:()=>{f(!1),x.current?.focus()}}):null]})}function U({styles:t,onChange:n,fonts:s,colours:i=[],settings:a=N,auto:l=[],fallback:c,sample:u,openId:m,defaultOpenId:p=null,onOpenChange:f,onAdd:x,rowActions:O,onRemove:z,max:$=M,readOnly:A=!1}){const L=r("textStyles",e),F=g(),[I,B]=h(p),R=void 0!==m?m:I,U=e=>{void 0===m&&B(e),f?.(e)},Z=new Set(l),q=S.filter(e=>a.includes(e)),_=v(null),[G,P]=h(null);y(()=>{if(!G||!_.current)return;const e=[..._.current.querySelectorAll(".es-ts-item")].find(e=>e.dataset.tsId===G.id),t=e?e.querySelector(".es-ts-row"):_.current.querySelector(`.es-ts-add[data-ts-kind="${G.kind}"]`);t?.focus(),P(null)},[G]);/* @__PURE__ */
7
+ return w("div",{className:"es-ts",ref:_,children:C.map(e=>{const a=t.filter(t=>t.kind===e),l=`${F}-${e}`,r=!A&&j(t,e,$);/* @__PURE__ */
8
+ return b("section",{className:"es-ts-section","aria-labelledby":l,children:[
9
+ /* @__PURE__ */w("div",{className:"es-ts-group",id:l,children:L.groups[e]}),a.map(e=>{const a=R===e.id,l={...c?.(e)??{},...e.values},r=s.find(e=>e.value===l.font),m=`${F}-open-${e.id}`,p=O?.(e),f=null!=p&&!1!==p,h=Boolean(z)&&!A&&!E(e),v={fontFamily:r?.family,fontSize:`${H(l.size??16)}px`,fontWeight:l.weight,fontStyle:l.italic?"italic":void 0};/* @__PURE__ */
10
+ return b(k,{children:[
11
+ /* @__PURE__ */b("div",{className:"es-ts-item","data-ts-id":e.id,children:[
12
+ /* @__PURE__ */b("button",{type:"button",className:"es-ts-row","aria-expanded":a,"aria-controls":a?m:void 0,onClick:()=>U(a?null:e.id),children:[
13
+ /* @__PURE__ */w("span",{className:"es-ts-chev","aria-hidden":"true",children:/* @__PURE__ */w(o,{size:12})}),
14
+ /* @__PURE__ */w("span",{className:"es-ts-name",style:v,children:e.name}),
15
+ /* @__PURE__ */w("span",{className:"es-ts-meta",children:K(l,s,L,{autoSize:Z.has("size")&&void 0===e.values.size})})]}),h||f?/* @__PURE__ */b("div",{className:"es-ts-actions",children:[h?/* @__PURE__ */w("button",{type:"button",className:"es-ts-del",title:L.remove,"aria-label":L.removeNamed({name:e.name}),onClick:()=>(e=>{if(A||!z)return;const s=W(t,e.id,{name:L.styleName});if(!s)return;const i=t.filter(t=>t.kind===e.kind),a=i[i.indexOf(e)+1];R===e.id&&U(null),n(s.styles),z(s.removed),P({id:a?.id??null,kind:e.kind})})(e),children:/* @__PURE__ */w(d,{size:14,"aria-hidden":"true"})}):null,f?p:null]}):null]}),a?/* @__PURE__ */w("div",{className:"es-ts-open",id:m,children:/* @__PURE__ */w(V,{style:e,values:l,fonts:s,colours:i,settings:q,autoSet:Z,sample:u??L.sample,words:L,readOnly:A,set:s=>n(D(t,e.id,s))})}):null]},e.id)}),
16
+ /* @__PURE__ */b("button",{type:"button",className:"es-ts-add","data-ts-kind":e,disabled:!r,title:r?void 0:L.full({kind:e,max:$}),onClick:()=>(e=>{if(A)return;const s=T(t,e,{max:$,name:L.styleName,make:x});s&&n(s.styles)})(e),children:[
17
+ /* @__PURE__ */w("span",{className:"es-ts-add-word",children:L.add[e]}),
18
+ /* @__PURE__ */w("span",{className:"es-ts-count",children:L.count({n:a.length,max:$})})]})]},e)})})}var Z={none:f,upper:p,lower:m},q={none:"none",upper:"uppercase",lower:"lowercase"};function V({style:e,values:t,fonts:s,colours:i,settings:l,autoSet:r,sample:o,words:d,readOnly:m,set:p}){const f=e.values,h=s.find(e=>e.value===t.font),v=function(e,t){return e?t.find(t=>t.key===e)?.value??a(e)??void 0:void 0}(t.color,i),g=e=>"largestSize"===e||r.has(e),y=e=>g(e)?f[e]:t[e],k=y("size"),S=y("lineSpacing"),N=y("letterSpacing"),C=g("letterCase")?f.letterCase:t.letterCase,M={fontFamily:h?.family,fontSize:void 0===k?void 0:`${k}px`,fontWeight:t.weight,fontStyle:t.italic?"italic":void 0,lineHeight:void 0===S?void 0:String(S/100),letterSpacing:void 0===N?void 0:`${N}px`,color:v,textTransform:C?q[C]:void 0},O=e=>{const s=$[e],i=g(e);/* @__PURE__ */
19
+ return w("div",{className:"es-ts-slider","data-ts-setting":e,children:/* @__PURE__ */w(n,{label:d.settings[e],value:f[e],onChange:t=>p({[e]:t}),min:s.min,max:s.max,step:s.step,shiftStep:s.shiftStep,sliderMin:s.sliderMin,sliderMax:s.sliderMax,unit:s.unit||void 0,styleValue:i?void 0:t[e],auto:i?d.auto:void 0,readOnly:m})},e)};/* @__PURE__ */
20
+ return b(x,{children:[
21
+ /* @__PURE__ */w("div",{className:"es-ts-preview",style:M,"aria-label":d.preview,children:o}),l.map(n=>{switch(n){case"font":/* @__PURE__ */
22
+ return w(_,{value:f.font,fonts:s,words:d,readOnly:m,set:p},n);case"color":/* @__PURE__ */
23
+ return w(G,{value:f.color,hex:v,colours:i,words:d,readOnly:m,set:p},n);case"emphasis":{const e=(t.weight??400)>=600,s=!1!==h?.italic;/* @__PURE__ */
24
+ return b("div",{className:"es-ts-set","data-ts-setting":n,children:[
25
+ /* @__PURE__ */w("span",{className:"es-ts-sub",children:d.settings.emphasis}),
26
+ /* @__PURE__ */b("div",{className:"es-ts-icons",children:[
27
+ /* @__PURE__ */w("button",{type:"button",className:"es-ts-icon","aria-label":d.bold,title:d.bold,"aria-pressed":e,disabled:m,onClick:()=>p({weight:e?400:700}),children:/* @__PURE__ */w(c,{size:16})}),
28
+ /* @__PURE__ */w("button",{type:"button",className:"es-ts-icon","aria-label":d.italic,title:s?d.italic:d.italicMissing,"aria-pressed":Boolean(t.italic),disabled:m||!s,onClick:()=>p({italic:!t.italic||void 0}),children:/* @__PURE__ */w(u,{size:16})})]})]},n)}case"letterCase":/* @__PURE__ */
29
+ return w(P,{value:f.letterCase??t.letterCase,auto:g("letterCase"),words:d,readOnly:m,set:p},n);case"largestSize":return"heading"===e.kind?O(n):null;default:return O(n)}})]})}function _({value:e,fonts:n,words:s,readOnly:i,set:a}){const l=g();/* @__PURE__ */
30
+ return b("div",{className:"es-ts-set","data-ts-setting":"font",children:[
31
+ /* @__PURE__ */w("span",{className:"es-ts-sub",id:l,children:s.settings.font}),
32
+ /* @__PURE__ */w(t,{value:e,options:n.map(e=>({label:e.label,value:e.value})),onChange:e=>a({font:e}),ariaLabelledBy:l,readOnly:i})]})}function G({value:e,hex:t,colours:n,words:s,readOnly:i,set:a}){/* @__PURE__ */
33
+ return w(R,{value:e,hex:t,colours:n,label:s.settings.color,followsWord:s.colour.follows,anyWord:s.colour.any,readOnly:i,onChange:e=>a({color:e})})}function P({value:e,auto:t,words:n,readOnly:s,set:i}){const a=t?["auto","none","upper","lower"]:["none","upper","lower"],l=e??(t?"auto":"none"),r=v([]),o=e=>i({letterCase:"auto"===e?void 0:e});/* @__PURE__ */
34
+ return b("div",{className:"es-ts-set","data-ts-setting":"letterCase",children:[
35
+ /* @__PURE__ */w("span",{className:"es-ts-sub",children:n.settings.letterCase}),
36
+ /* @__PURE__ */w("div",{className:"es-ts-icons",role:"radiogroup","aria-label":n.settings.letterCase,onKeyDown:e=>{const t="ArrowRight"===e.key||"ArrowDown"===e.key?1:"ArrowLeft"===e.key||"ArrowUp"===e.key?-1:0;if(!t||s)return;e.preventDefault();const n=(a.indexOf(l)+t+a.length)%a.length;o(a[n]),r.current[n]?.focus()},children:a.map((e,t)=>{const i=e===l;if("auto"===e)/* @__PURE__ */
37
+ return w("button",{ref:e=>{r.current[t]=e},type:"button",role:"radio",className:"es-ts-icon es-ts-icon--word","aria-checked":i,tabIndex:i?0:-1,title:n.case.autoTitle,disabled:s,onClick:()=>o(e),children:n.case.auto},e);const a=Z[e];/* @__PURE__ */
38
+ return w("button",{ref:e=>{r.current[t]=e},type:"button",role:"radio",className:"es-ts-icon","aria-checked":i,"aria-label":n.case[e],title:n.case[e],tabIndex:i?0:-1,disabled:s,onClick:()=>o(e),children:/* @__PURE__ */w(a,{size:16})},e)})})]})}export{N as CAMPAIGN_TEXT_STYLE_SETTINGS,R as ColourSwatchRow,O as TEXT_STYLE_DEFAULT_COUNT,C as TEXT_STYLE_KINDS,M as TEXT_STYLE_MAX,$ as TEXT_STYLE_RANGES,S as TEXT_STYLE_SETTINGS,U as TextStylesList,T as addTextStyle,j as canAddTextStyle,L as defaultTextStyles,K as describeTextStyle,E as isDefaultTextStyle,W as removeTextStyle,I as resolveTextStyle,H as textStyleRowSize,D as updateTextStyle,B as weightWord};