@mieweb/ui 0.11.0-dev.25 → 0.11.0-dev.27

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 (32) hide show
  1. package/dist/{chunk-OCRUE3HI.cjs → chunk-7S3GBPRD.cjs} +10 -2
  2. package/dist/chunk-7S3GBPRD.cjs.map +1 -0
  3. package/dist/{chunk-3S5LK2EW.cjs → chunk-FA7STF2M.cjs} +139 -170
  4. package/dist/chunk-FA7STF2M.cjs.map +1 -0
  5. package/dist/{chunk-JTLOCREL.js → chunk-GTAHQBUK.js} +3 -3
  6. package/dist/{chunk-JTLOCREL.js.map → chunk-GTAHQBUK.js.map} +1 -1
  7. package/dist/{chunk-CY7ZRKLW.cjs → chunk-S2QKRL5S.cjs} +4 -4
  8. package/dist/{chunk-CY7ZRKLW.cjs.map → chunk-S2QKRL5S.cjs.map} +1 -1
  9. package/dist/{chunk-UY3PIO7F.js → chunk-STDGAFW2.js} +10 -2
  10. package/dist/chunk-STDGAFW2.js.map +1 -0
  11. package/dist/{chunk-UTSRPQ5B.js → chunk-UWHVSRSU.js} +140 -171
  12. package/dist/chunk-UWHVSRSU.js.map +1 -0
  13. package/dist/components/EmailEditor/index.cjs +11 -11
  14. package/dist/components/EmailEditor/index.js +2 -2
  15. package/dist/components/RichTextEditor/index.cjs +4 -4
  16. package/dist/components/RichTextEditor/index.d.cts +10 -5
  17. package/dist/components/RichTextEditor/index.d.ts +10 -5
  18. package/dist/components/RichTextEditor/index.js +1 -1
  19. package/dist/index.cjs +41 -18
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +32 -6
  22. package/dist/index.d.ts +32 -6
  23. package/dist/index.js +27 -4
  24. package/dist/index.js.map +1 -1
  25. package/dist/styles.css +1 -1
  26. package/dist/tailwind-preset.cjs +4 -4
  27. package/dist/tailwind-preset.js +1 -1
  28. package/package.json +2 -2
  29. package/dist/chunk-3S5LK2EW.cjs.map +0 -1
  30. package/dist/chunk-OCRUE3HI.cjs.map +0 -1
  31. package/dist/chunk-UTSRPQ5B.js.map +0 -1
  32. package/dist/chunk-UY3PIO7F.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { Separator } from './chunk-OQWZYKJM.js';
2
- import { Dropdown, DropdownSeparator, DropdownLabel, DropdownItem } from './chunk-OYUIEJPN.js';
2
+ import { Dropdown, DropdownSeparator, DropdownLabel, DropdownItem, DropdownSubmenu } from './chunk-OYUIEJPN.js';
3
3
  import { isHtmlEmpty } from './chunk-ERLPVXOV.js';
4
4
  import { Input } from './chunk-3IMM2PM4.js';
5
5
  import { X, Search, History, BookOpen, ChevronRight, ChevronDown, Plus } from './chunk-BKSPDTX4.js';
@@ -19,11 +19,7 @@ var DEFAULT_LABELS = {
19
19
  empty: "No macros available",
20
20
  emptyHint: "The application has not provided a macro list.",
21
21
  noResults: "No macros match",
22
- clearSearch: "Clear search",
23
- showFullContent: "Show full content",
24
- hideFullContent: "Hide full content",
25
- more: "more",
26
- less: "less"
22
+ clearSearch: "Clear search"
27
23
  };
28
24
  function matchesQuery(macro, query) {
29
25
  const q = query.toLowerCase();
@@ -37,131 +33,6 @@ function rankMacros(macros, query) {
37
33
  (a, b) => Number(isShortcutHit(b, query)) - Number(isShortcutHit(a, query))
38
34
  );
39
35
  }
40
- function MacroRow({
41
- macro,
42
- isPrevious,
43
- isPeeked,
44
- peekId,
45
- labels,
46
- onSelect,
47
- onTogglePeek,
48
- onRowKeyDown
49
- }) {
50
- const [truncated, setTruncated] = React2.useState(false);
51
- const rowRef = React2.useRef(null);
52
- const lessRef = React2.useRef(null);
53
- const focusAfterToggle = React2.useRef(false);
54
- React2.useEffect(() => {
55
- if (!focusAfterToggle.current) return;
56
- focusAfterToggle.current = false;
57
- (isPeeked ? lessRef.current : rowRef.current)?.focus();
58
- }, [isPeeked]);
59
- const handleToggle = () => {
60
- focusAfterToggle.current = true;
61
- onTogglePeek();
62
- };
63
- const previewObserver = React2.useRef(null);
64
- const measurePreview = (el) => {
65
- previewObserver.current?.disconnect();
66
- previewObserver.current = null;
67
- if (!el) return;
68
- const measure = () => setTruncated(el.scrollHeight > el.clientHeight);
69
- measure();
70
- if (typeof ResizeObserver !== "undefined") {
71
- previewObserver.current = new ResizeObserver(measure);
72
- previewObserver.current.observe(el);
73
- }
74
- };
75
- return /* @__PURE__ */ jsxs(
76
- "div",
77
- {
78
- "data-slot": "macro-library-item",
79
- className: "group/macro hover:bg-muted/60 relative rounded-md",
80
- children: [
81
- /* @__PURE__ */ jsxs(
82
- "button",
83
- {
84
- type: "button",
85
- "data-macro-row": true,
86
- ref: rowRef,
87
- className: "focus-visible:ring-ring text-foreground hover:text-primary flex w-full cursor-pointer flex-col items-stretch gap-0.5 rounded-md px-2 py-1.5 text-start focus-visible:ring-2 focus-visible:outline-none",
88
- onClick: () => onSelect(macro),
89
- onKeyDown: onRowKeyDown,
90
- children: [
91
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
92
- macro.shortcut && /* @__PURE__ */ jsx(
93
- "span",
94
- {
95
- dir: "ltr",
96
- className: cn(
97
- "border-border bg-background text-muted-foreground rounded border px-1 font-mono text-[11px]",
98
- isPrevious && "border-warning-300 bg-warning-50 text-warning-800 dark:border-warning-700 dark:bg-warning-950 dark:text-warning-300"
99
- ),
100
- children: macro.shortcut
101
- }
102
- ),
103
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: macro.label })
104
- ] }),
105
- !isPeeked && /* @__PURE__ */ jsx(
106
- "span",
107
- {
108
- ref: measurePreview,
109
- className: "text-muted-foreground line-clamp-2 text-xs break-words",
110
- children: macro.content
111
- }
112
- )
113
- ]
114
- }
115
- ),
116
- isPeeked && /* @__PURE__ */ jsx(
117
- "div",
118
- {
119
- id: peekId,
120
- "data-slot": "macro-library-peek",
121
- className: "text-muted-foreground px-2 pb-1 text-xs break-words whitespace-pre-line",
122
- children: macro.content
123
- }
124
- ),
125
- truncated && !isPeeked && // Overlays the end of the clamped second line; revealed on row
126
- // hover or keyboard focus so short rows stay entirely clean. Always
127
- // visible on devices without hover (touch).
128
- /* @__PURE__ */ jsxs(
129
- "button",
130
- {
131
- type: "button",
132
- className: "bg-muted text-muted-foreground hover:text-primary focus-visible:ring-ring invisible absolute end-1.5 bottom-1.5 flex cursor-pointer items-center rounded-sm px-1 text-[11px] leading-4 group-focus-within/macro:visible group-hover/macro:visible hover:underline focus-visible:ring-2 focus-visible:outline-none [@media(hover:none)]:visible",
133
- "aria-expanded": false,
134
- "aria-label": `${labels.showFullContent}: ${macro.label}`,
135
- onClick: handleToggle,
136
- children: [
137
- labels.more,
138
- /* @__PURE__ */ jsx(
139
- ChevronRight,
140
- {
141
- "aria-hidden": "true",
142
- className: "h-3 w-3 rtl:-scale-x-100"
143
- }
144
- )
145
- ]
146
- }
147
- ),
148
- isPeeked && /* @__PURE__ */ jsx("span", { className: "-mt-1 flex justify-end px-2 pb-1", children: /* @__PURE__ */ jsx(
149
- "button",
150
- {
151
- type: "button",
152
- ref: lessRef,
153
- className: "text-muted-foreground hover:text-primary focus-visible:ring-ring cursor-pointer rounded text-[11px] hover:underline focus-visible:ring-2 focus-visible:outline-none",
154
- "aria-expanded": true,
155
- "aria-controls": peekId,
156
- "aria-label": `${labels.hideFullContent}: ${macro.label}`,
157
- onClick: handleToggle,
158
- children: labels.less
159
- }
160
- ) })
161
- ]
162
- }
163
- );
164
- }
165
36
  var MacroLibrary = React2.forwardRef(
166
37
  function MacroLibrary2({
167
38
  groups,
@@ -179,7 +50,6 @@ var MacroLibrary = React2.forwardRef(
179
50
  const [collapsed, setCollapsed] = React2.useState(
180
51
  {}
181
52
  );
182
- const [peeked, setPeeked] = React2.useState(null);
183
53
  const listRef = React2.useRef(null);
184
54
  const searchRef = React2.useRef(null);
185
55
  const panelId = React2.useId();
@@ -381,23 +251,32 @@ var MacroLibrary = React2.forwardRef(
381
251
  ]
382
252
  }
383
253
  ),
384
- !isCollapsed && group.macros.map((macro) => {
385
- const isPeeked = peeked === macro.id;
386
- return /* @__PURE__ */ jsx(
387
- MacroRow,
388
- {
389
- macro,
390
- isPrevious,
391
- isPeeked,
392
- peekId: `${panelId}-peek-${macro.id}`,
393
- labels,
394
- onSelect,
395
- onTogglePeek: () => setPeeked(isPeeked ? null : macro.id),
396
- onRowKeyDown: handleRowKeyDown
397
- },
398
- macro.id
399
- );
400
- })
254
+ !isCollapsed && group.macros.map((macro) => /* @__PURE__ */ jsxs(
255
+ "button",
256
+ {
257
+ type: "button",
258
+ "data-macro-row": true,
259
+ "data-slot": "macro-library-item",
260
+ className: "focus-visible:ring-ring text-foreground hover:bg-muted/60 hover:text-primary flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-start focus-visible:ring-2 focus-visible:outline-none",
261
+ onClick: () => onSelect(macro),
262
+ onKeyDown: handleRowKeyDown,
263
+ children: [
264
+ macro.shortcut && /* @__PURE__ */ jsx(
265
+ "span",
266
+ {
267
+ dir: "ltr",
268
+ className: cn(
269
+ "border-border bg-background text-muted-foreground rounded border px-1 font-mono text-[11px]",
270
+ isPrevious && "border-warning-300 bg-warning-50 text-warning-800 dark:border-warning-700 dark:bg-warning-950 dark:text-warning-300"
271
+ ),
272
+ children: macro.shortcut
273
+ }
274
+ ),
275
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: macro.label })
276
+ ]
277
+ },
278
+ macro.id
279
+ ))
401
280
  ]
402
281
  },
403
282
  group.id
@@ -583,6 +462,7 @@ var RichTextEditor = React2.forwardRef(
583
462
  blockMenu = false,
584
463
  blockMenuLabels,
585
464
  markdownShortcuts = false,
465
+ chromeless = false,
586
466
  "aria-label": ariaLabel
587
467
  }, ref) => {
588
468
  const editorRef = React2.useRef(null);
@@ -596,6 +476,9 @@ var RichTextEditor = React2.forwardRef(
596
476
  const macroPanelId = React2.useId();
597
477
  const hasVariables = Boolean(variableGroups && variableGroups.length > 0);
598
478
  const hasMacros = Boolean(macros);
479
+ const hasMacroEntries = Boolean(
480
+ macros?.some((group) => group.macros.length > 0)
481
+ );
599
482
  const isEmpty = isHtmlEmpty(value);
600
483
  const [uncontrolledToolbar, setUncontrolledToolbar] = React2.useState(defaultToolbar);
601
484
  const toolbarVisible = (toolbar ?? uncontrolledToolbar) !== "hidden";
@@ -605,9 +488,36 @@ var RichTextEditor = React2.forwardRef(
605
488
  };
606
489
  const toolbarVisibleRef = React2.useRef(toolbarVisible);
607
490
  const [blockMenuOpen, setBlockMenuOpen] = React2.useState(false);
608
- const [gutterTop, setGutterTop] = React2.useState(16);
491
+ const [gutterTop, setGutterTop] = React2.useState(chromeless ? 7 : 16);
609
492
  const gutterWrapperRef = React2.useRef(null);
493
+ const measureRestGutterTop = React2.useCallback(() => {
494
+ const fallback = chromeless ? 7 : 16;
495
+ if (!editorRef.current) return fallback;
496
+ const padding = parseFloat(
497
+ getComputedStyle(editorRef.current).paddingTop
498
+ );
499
+ return Number.isNaN(padding) ? fallback : Math.round(padding);
500
+ }, [chromeless]);
501
+ React2.useLayoutEffect(() => {
502
+ if (!blockMenu || !editorRef.current) return;
503
+ setGutterTop((current) => {
504
+ const next = measureRestGutterTop();
505
+ return current === next ? current : next;
506
+ });
507
+ }, [blockMenu, measureRestGutterTop]);
610
508
  const hoverBlockNodeRef = React2.useRef(null);
509
+ const resetGutter = React2.useCallback(() => {
510
+ hoverBlockNodeRef.current = null;
511
+ const next = measureRestGutterTop();
512
+ setGutterTop((current) => current === next ? current : next);
513
+ }, [measureRestGutterTop]);
514
+ const reconcileGutterWithContent = React2.useCallback(() => {
515
+ if (!blockMenu || !editorRef.current) return;
516
+ const hovered = hoverBlockNodeRef.current;
517
+ if (hovered && !editorRef.current.contains(hovered) || !editorRef.current.hasChildNodes()) {
518
+ resetGutter();
519
+ }
520
+ }, [blockMenu, resetGutter]);
611
521
  const blockTargetRangeRef = React2.useRef(null);
612
522
  const firstBlockItemRef = React2.useRef(null);
613
523
  React2.useImperativeHandle(ref, () => editorRef.current);
@@ -636,10 +546,12 @@ var RichTextEditor = React2.forwardRef(
636
546
  React2.useEffect(() => {
637
547
  if (editorRef.current && editorRef.current.innerHTML !== value) {
638
548
  editorRef.current.innerHTML = value;
549
+ reconcileGutterWithContent();
639
550
  }
640
- }, [value]);
551
+ }, [value, reconcileGutterWithContent]);
641
552
  const updateContent = () => {
642
553
  if (editorRef.current) onChange(editorRef.current.innerHTML);
554
+ reconcileGutterWithContent();
643
555
  };
644
556
  const insertSanitized = (html) => {
645
557
  if (!sanitizeHtml) return false;
@@ -882,7 +794,10 @@ var RichTextEditor = React2.forwardRef(
882
794
  top = rect.top;
883
795
  }
884
796
  }
885
- if (!target || top === null) return;
797
+ if (!target || top === null) {
798
+ resetGutter();
799
+ return;
800
+ }
886
801
  hoverBlockNodeRef.current = target;
887
802
  const wrapperTop = gutterWrapperRef.current.getBoundingClientRect().top;
888
803
  const next = Math.max(Math.round(top - wrapperTop), 4);
@@ -1093,8 +1008,10 @@ var RichTextEditor = React2.forwardRef(
1093
1008
  "div",
1094
1009
  {
1095
1010
  "data-slot": "rich-text-editor",
1011
+ "data-chromeless": chromeless || void 0,
1096
1012
  className: cn(
1097
- "border-border bg-background flex flex-col overflow-hidden rounded-lg border",
1013
+ "bg-background flex flex-col",
1014
+ !chromeless && "border-border overflow-hidden rounded-lg border",
1098
1015
  className
1099
1016
  ),
1100
1017
  children: [
@@ -1378,20 +1295,62 @@ var RichTextEditor = React2.forwardRef(
1378
1295
  item.key
1379
1296
  )),
1380
1297
  /* @__PURE__ */ jsx(DropdownSeparator, {}),
1381
- hasMacros && /* @__PURE__ */ jsx(
1382
- DropdownItem,
1383
- {
1384
- onClick: () => {
1385
- setBlockMenuOpen(false);
1386
- if (blockTargetRangeRef.current) {
1387
- savedRangeRef.current = blockTargetRangeRef.current;
1388
- }
1389
- setMacrosOpen(true);
1390
- focusRestoringSelection();
1391
- },
1392
- children: blockMenuLabels?.insertMacro ?? "Insert macro\u2026"
1393
- }
1394
- ),
1298
+ hasMacros && macros && /* @__PURE__ */ jsxs(Fragment, { children: [
1299
+ /* @__PURE__ */ jsx(
1300
+ DropdownSubmenu,
1301
+ {
1302
+ label: blockMenuLabels?.insertMacro ?? "Insert macro",
1303
+ disabled: !hasMacroEntries,
1304
+ children: blockMenuOpen && macros.flatMap((group) => [
1305
+ // Group headings only earn their row when there
1306
+ // is more than one group to tell apart.
1307
+ ...macros.length > 1 ? [
1308
+ /* @__PURE__ */ jsx(DropdownLabel, { children: group.label }, `h:${group.id}`)
1309
+ ] : [],
1310
+ ...group.macros.map((macro) => /* @__PURE__ */ jsx(
1311
+ DropdownItem,
1312
+ {
1313
+ className: "[&>[data-slot=dropdown-item-label]]:min-w-0 [&>[data-slot=dropdown-item-label]]:flex-1",
1314
+ onClick: () => {
1315
+ setBlockMenuOpen(false);
1316
+ if (blockTargetRangeRef.current) {
1317
+ savedRangeRef.current = blockTargetRangeRef.current;
1318
+ }
1319
+ insertMacro(macro);
1320
+ },
1321
+ children: /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [
1322
+ macro.shortcut && /* @__PURE__ */ jsx(
1323
+ "span",
1324
+ {
1325
+ dir: "ltr",
1326
+ className: "border-border bg-background text-muted-foreground rounded border px-1 font-mono text-[11px]",
1327
+ children: macro.shortcut
1328
+ }
1329
+ ),
1330
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: macro.label })
1331
+ ] })
1332
+ },
1333
+ `m:${JSON.stringify([group.id, macro.id])}`
1334
+ ))
1335
+ ])
1336
+ }
1337
+ ),
1338
+ /* @__PURE__ */ jsx(
1339
+ DropdownItem,
1340
+ {
1341
+ onClick: () => {
1342
+ setBlockMenuOpen(false);
1343
+ if (blockTargetRangeRef.current) {
1344
+ savedRangeRef.current = blockTargetRangeRef.current;
1345
+ }
1346
+ setMacrosOpen(true);
1347
+ focusRestoringSelection();
1348
+ },
1349
+ children: blockMenuLabels?.showMacroLibrary ?? "Show macro library"
1350
+ }
1351
+ ),
1352
+ /* @__PURE__ */ jsx(DropdownSeparator, {})
1353
+ ] }),
1395
1354
  /* @__PURE__ */ jsx(
1396
1355
  DropdownItem,
1397
1356
  {
@@ -1415,7 +1374,12 @@ var RichTextEditor = React2.forwardRef(
1415
1374
  {
1416
1375
  "aria-hidden": "true",
1417
1376
  className: cn(
1418
- "text-muted-foreground pointer-events-none absolute start-0 top-0 p-4",
1377
+ "text-muted-foreground pointer-events-none absolute start-0 top-0",
1378
+ // Chromeless hosts drop the wrapper's clipping, so a long
1379
+ // placeholder must stay a single ellipsized line (as a
1380
+ // native textarea clips its own) instead of wrapping out
1381
+ // of the one-row field.
1382
+ chromeless ? "end-0 truncate px-3 py-[7px] text-base" : "p-4",
1419
1383
  blockMenu && "ps-10"
1420
1384
  ),
1421
1385
  children: placeholder
@@ -1429,6 +1393,7 @@ var RichTextEditor = React2.forwardRef(
1429
1393
  "aria-multiline": "true",
1430
1394
  "aria-label": ariaLabel ?? placeholder,
1431
1395
  contentEditable: !disabled,
1396
+ "data-macros-open": hasMacros && macrosOpen ? "" : void 0,
1432
1397
  tabIndex: disabled ? -1 : 0,
1433
1398
  onInput: updateContent,
1434
1399
  onPaste: handlePaste,
@@ -1438,7 +1403,11 @@ var RichTextEditor = React2.forwardRef(
1438
1403
  onKeyUp: hasMacros ? saveSelection : void 0,
1439
1404
  onMouseUp: hasMacros ? saveSelection : void 0,
1440
1405
  className: cn(
1441
- "min-h-[250px] overflow-y-auto p-4 focus:outline-none",
1406
+ "overflow-y-auto focus:outline-none",
1407
+ // Chromeless surfaces start one Input row tall (Textarea md
1408
+ // metrics: 24px line + 7px padding + the host's 1px borders
1409
+ // = h-10) and auto-grow with content.
1410
+ chromeless ? "min-h-[38px] px-3 py-[7px] text-base" : "min-h-[250px] p-4",
1442
1411
  "prose prose-sm max-w-none",
1443
1412
  // Baseline block styles: Tailwind preflight strips heading,
1444
1413
  // list and quote styling, and the `prose` classes above only
@@ -1485,5 +1454,5 @@ var RichTextEditor = React2.forwardRef(
1485
1454
  RichTextEditor.displayName = "RichTextEditor";
1486
1455
 
1487
1456
  export { MacroLibrary, RichTextEditor, convertAngleBracketsToMustache, processDictation };
1488
- //# sourceMappingURL=chunk-UTSRPQ5B.js.map
1489
- //# sourceMappingURL=chunk-UTSRPQ5B.js.map
1457
+ //# sourceMappingURL=chunk-UWHVSRSU.js.map
1458
+ //# sourceMappingURL=chunk-UWHVSRSU.js.map