@seekalfred/primitives 0.6.0 → 0.7.1

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 (322) hide show
  1. package/README.md +19 -11
  2. package/dist/activity-heatmap/activity-heatmap.d.ts +92 -0
  3. package/dist/activity-heatmap/activity-heatmap.js +699 -0
  4. package/dist/activity-heatmap/activity-heatmap.js.map +1 -0
  5. package/dist/activity-heatmap/index.d.ts +2 -0
  6. package/dist/activity-heatmap/index.js +2 -0
  7. package/dist/alert/alert.d.ts +5 -1
  8. package/dist/alert/alert.js +68 -7
  9. package/dist/alert/alert.js.map +1 -1
  10. package/dist/alert-dialog/alert-dialog.d.ts +45 -18
  11. package/dist/alert-dialog/alert-dialog.js +128 -9
  12. package/dist/alert-dialog/alert-dialog.js.map +1 -1
  13. package/dist/alert-dialog/index.d.ts +2 -2
  14. package/dist/alert-dialog/index.js +2 -2
  15. package/dist/animated-counter/animated-counter.d.ts +38 -0
  16. package/dist/animated-counter/animated-counter.js +230 -0
  17. package/dist/animated-counter/animated-counter.js.map +1 -0
  18. package/dist/animated-counter/index.d.ts +2 -0
  19. package/dist/animated-counter/index.js +2 -0
  20. package/dist/announcement-bar/announcement-bar.d.ts +67 -0
  21. package/dist/announcement-bar/announcement-bar.js +364 -0
  22. package/dist/announcement-bar/announcement-bar.js.map +1 -0
  23. package/dist/announcement-bar/index.d.ts +2 -0
  24. package/dist/announcement-bar/index.js +2 -0
  25. package/dist/badge/badge.d.ts +1 -1
  26. package/dist/bar-chart/bar-chart.d.ts +51 -0
  27. package/dist/bar-chart/bar-chart.js +565 -0
  28. package/dist/bar-chart/bar-chart.js.map +1 -0
  29. package/dist/bar-chart/index.d.ts +2 -0
  30. package/dist/bar-chart/index.js +2 -0
  31. package/dist/breadcrumb/breadcrumb.d.ts +2 -2
  32. package/dist/breadcrumb/breadcrumb.js +12 -3
  33. package/dist/breadcrumb/breadcrumb.js.map +1 -1
  34. package/dist/brush-chart/brush-chart.d.ts +64 -0
  35. package/dist/brush-chart/brush-chart.js +1056 -0
  36. package/dist/brush-chart/brush-chart.js.map +1 -0
  37. package/dist/brush-chart/index.d.ts +2 -0
  38. package/dist/brush-chart/index.js +2 -0
  39. package/dist/button/button.d.ts +2 -2
  40. package/dist/button/button.js +31 -21
  41. package/dist/button/button.js.map +1 -1
  42. package/dist/button-group/button-group.js +3 -4
  43. package/dist/button-group/button-group.js.map +1 -1
  44. package/dist/card/card.js +1 -1
  45. package/dist/card/card.js.map +1 -1
  46. package/dist/card-stack/card-stack.d.ts +54 -0
  47. package/dist/card-stack/card-stack.js +492 -0
  48. package/dist/card-stack/card-stack.js.map +1 -0
  49. package/dist/card-stack/index.d.ts +2 -0
  50. package/dist/card-stack/index.js +2 -0
  51. package/dist/carousel/carousel.d.ts +56 -0
  52. package/dist/carousel/carousel.js +389 -0
  53. package/dist/carousel/carousel.js.map +1 -0
  54. package/dist/carousel/index.d.ts +2 -0
  55. package/dist/carousel/index.js +2 -0
  56. package/dist/checkbox/checkbox.js +4 -4
  57. package/dist/checkbox/checkbox.js.map +1 -1
  58. package/dist/chip-group/chip-group.d.ts +37 -0
  59. package/dist/chip-group/chip-group.js +148 -0
  60. package/dist/chip-group/chip-group.js.map +1 -0
  61. package/dist/chip-group/index.d.ts +2 -0
  62. package/dist/chip-group/index.js +2 -0
  63. package/dist/countdown/countdown.d.ts +45 -0
  64. package/dist/countdown/countdown.js +298 -0
  65. package/dist/countdown/countdown.js.map +1 -0
  66. package/dist/countdown/index.d.ts +2 -0
  67. package/dist/countdown/index.js +2 -0
  68. package/dist/date-picker/picker-parts.js +1 -1
  69. package/dist/date-picker/picker-parts.js.map +1 -1
  70. package/dist/dialog/dialog.js +1 -1
  71. package/dist/dialog/dialog.js.map +1 -1
  72. package/dist/donut-chart/donut-chart.d.ts +64 -0
  73. package/dist/donut-chart/donut-chart.js +576 -0
  74. package/dist/donut-chart/donut-chart.js.map +1 -0
  75. package/dist/donut-chart/index.d.ts +2 -0
  76. package/dist/donut-chart/index.js +2 -0
  77. package/dist/empty-state/empty-state.d.ts +21 -10
  78. package/dist/empty-state/empty-state.js +32 -26
  79. package/dist/empty-state/empty-state.js.map +1 -1
  80. package/dist/empty-state/index.d.ts +2 -2
  81. package/dist/empty-state/index.js +2 -2
  82. package/dist/file-drop-zone/file-drop-zone.js +1 -1
  83. package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
  84. package/dist/filter-bar/filter-bar.d.ts +47 -26
  85. package/dist/filter-bar/filter-bar.js +272 -156
  86. package/dist/filter-bar/filter-bar.js.map +1 -1
  87. package/dist/gauge/gauge.d.ts +45 -0
  88. package/dist/gauge/gauge.js +219 -0
  89. package/dist/gauge/gauge.js.map +1 -0
  90. package/dist/gauge/index.d.ts +2 -0
  91. package/dist/gauge/index.js +2 -0
  92. package/dist/in-view-title/in-view-title.d.ts +33 -0
  93. package/dist/in-view-title/in-view-title.js +119 -0
  94. package/dist/in-view-title/in-view-title.js.map +1 -0
  95. package/dist/in-view-title/index.d.ts +2 -0
  96. package/dist/in-view-title/index.js +2 -0
  97. package/dist/index.d.ts +49 -6
  98. package/dist/index.js +48 -8
  99. package/dist/lib/cn.js +11 -1
  100. package/dist/lib/cn.js.map +1 -1
  101. package/dist/lib/field-message.js +28 -10
  102. package/dist/lib/field-message.js.map +1 -1
  103. package/dist/lib/field-surface.js +9 -8
  104. package/dist/lib/field-surface.js.map +1 -1
  105. package/dist/lib/flag-emoji.js +50 -0
  106. package/dist/lib/flag-emoji.js.map +1 -0
  107. package/dist/lib/flip.js +11 -3
  108. package/dist/lib/flip.js.map +1 -1
  109. package/dist/lib/menu-highlight.js +64 -0
  110. package/dist/lib/menu-highlight.js.map +1 -0
  111. package/dist/lib/motion.js +9 -2
  112. package/dist/lib/motion.js.map +1 -1
  113. package/dist/lib/rolling-value.js +1 -1
  114. package/dist/lib/rolling-value.js.map +1 -1
  115. package/dist/lib/spring-tween.js +500 -0
  116. package/dist/lib/spring-tween.js.map +1 -0
  117. package/dist/lib/swap-text.js +3 -1
  118. package/dist/lib/swap-text.js.map +1 -1
  119. package/dist/line-chart/index.d.ts +2 -0
  120. package/dist/line-chart/index.js +2 -0
  121. package/dist/line-chart/line-chart.d.ts +76 -0
  122. package/dist/line-chart/line-chart.js +611 -0
  123. package/dist/line-chart/line-chart.js.map +1 -0
  124. package/dist/mention-input/index.d.ts +2 -0
  125. package/dist/mention-input/index.js +2 -0
  126. package/dist/mention-input/mention-input.d.ts +98 -0
  127. package/dist/mention-input/mention-input.js +576 -0
  128. package/dist/mention-input/mention-input.js.map +1 -0
  129. package/dist/menu/menu.d.ts +2 -0
  130. package/dist/menu/menu.js +23 -12
  131. package/dist/menu/menu.js.map +1 -1
  132. package/dist/money-input/index.d.ts +2 -0
  133. package/dist/money-input/index.js +2 -0
  134. package/dist/money-input/money-input.d.ts +65 -0
  135. package/dist/money-input/money-input.js +607 -0
  136. package/dist/money-input/money-input.js.map +1 -0
  137. package/dist/morph-select/index.d.ts +2 -0
  138. package/dist/morph-select/index.js +2 -0
  139. package/dist/morph-select/morph-select.d.ts +71 -0
  140. package/dist/morph-select/morph-select.js +693 -0
  141. package/dist/morph-select/morph-select.js.map +1 -0
  142. package/dist/multi-select/index.d.ts +2 -0
  143. package/dist/multi-select/index.js +2 -0
  144. package/dist/multi-select/multi-select.d.ts +36 -0
  145. package/dist/multi-select/multi-select.js +152 -0
  146. package/dist/multi-select/multi-select.js.map +1 -0
  147. package/dist/number-field/index.d.ts +2 -0
  148. package/dist/number-field/index.js +2 -0
  149. package/dist/number-field/number-field.d.ts +42 -0
  150. package/dist/number-field/number-field.js +199 -0
  151. package/dist/number-field/number-field.js.map +1 -0
  152. package/dist/onboarding-checklist/index.d.ts +2 -0
  153. package/dist/onboarding-checklist/index.js +2 -0
  154. package/dist/onboarding-checklist/onboarding-checklist.d.ts +55 -0
  155. package/dist/onboarding-checklist/onboarding-checklist.js +577 -0
  156. package/dist/onboarding-checklist/onboarding-checklist.js.map +1 -0
  157. package/dist/password-field/password-field.js +2 -2
  158. package/dist/password-field/password-field.js.map +1 -1
  159. package/dist/phone-input/index.d.ts +3 -0
  160. package/dist/phone-input/index.js +3 -0
  161. package/dist/phone-input/phone-countries.d.ts +29 -0
  162. package/dist/phone-input/phone-countries.js +170 -0
  163. package/dist/phone-input/phone-countries.js.map +1 -0
  164. package/dist/phone-input/phone-input.d.ts +66 -0
  165. package/dist/phone-input/phone-input.js +440 -0
  166. package/dist/phone-input/phone-input.js.map +1 -0
  167. package/dist/popover/popover.d.ts +12 -0
  168. package/dist/popover/popover.js +31 -7
  169. package/dist/popover/popover.js.map +1 -1
  170. package/dist/progress/progress.js +1 -1
  171. package/dist/radio-group/radio-group.js +1 -1
  172. package/dist/radio-group/radio-group.js.map +1 -1
  173. package/dist/reactions/index.d.ts +2 -0
  174. package/dist/reactions/index.js +2 -0
  175. package/dist/reactions/reactions.d.ts +50 -0
  176. package/dist/reactions/reactions.js +315 -0
  177. package/dist/reactions/reactions.js.map +1 -0
  178. package/dist/rich-text-editor/index.d.ts +3 -0
  179. package/dist/rich-text-editor/index.js +2 -0
  180. package/dist/rich-text-editor/rich-text-editor.d.ts +70 -0
  181. package/dist/rich-text-editor/rich-text-editor.js +909 -0
  182. package/dist/rich-text-editor/rich-text-editor.js.map +1 -0
  183. package/dist/rich-text-editor/rich-text-engine.d.ts +16 -0
  184. package/dist/rich-text-editor/rich-text-engine.js +366 -0
  185. package/dist/rich-text-editor/rich-text-engine.js.map +1 -0
  186. package/dist/rich-text-editor/rich-text-html.js +174 -0
  187. package/dist/rich-text-editor/rich-text-html.js.map +1 -0
  188. package/dist/rich-text-editor/rich-text-icons.js +43 -0
  189. package/dist/rich-text-editor/rich-text-icons.js.map +1 -0
  190. package/dist/ridgeline/index.d.ts +2 -0
  191. package/dist/ridgeline/index.js +2 -0
  192. package/dist/ridgeline/ridgeline.d.ts +55 -0
  193. package/dist/ridgeline/ridgeline.js +701 -0
  194. package/dist/ridgeline/ridgeline.js.map +1 -0
  195. package/dist/scroll-area/index.d.ts +2 -2
  196. package/dist/scroll-area/index.js +2 -2
  197. package/dist/scroll-area/scroll-area.d.ts +68 -31
  198. package/dist/scroll-area/scroll-area.js +208 -59
  199. package/dist/scroll-area/scroll-area.js.map +1 -1
  200. package/dist/scrub-input/evaluate.d.ts +14 -0
  201. package/dist/scrub-input/evaluate.js +72 -0
  202. package/dist/scrub-input/evaluate.js.map +1 -0
  203. package/dist/scrub-input/index.d.ts +3 -0
  204. package/dist/scrub-input/index.js +3 -0
  205. package/dist/scrub-input/scrub-input.d.ts +74 -0
  206. package/dist/scrub-input/scrub-input.js +486 -0
  207. package/dist/scrub-input/scrub-input.js.map +1 -0
  208. package/dist/search-field/search-field.d.ts +2 -1
  209. package/dist/search-field/search-field.js +4 -4
  210. package/dist/search-field/search-field.js.map +1 -1
  211. package/dist/select/select.d.ts +3 -1
  212. package/dist/select/select.js +20 -8
  213. package/dist/select/select.js.map +1 -1
  214. package/dist/separator/separator.d.ts +1 -1
  215. package/dist/sheet/sheet.js +2 -1
  216. package/dist/sheet/sheet.js.map +1 -1
  217. package/dist/shortcut-recorder/index.d.ts +3 -0
  218. package/dist/shortcut-recorder/index.js +3 -0
  219. package/dist/shortcut-recorder/shortcut-model.d.ts +27 -0
  220. package/dist/shortcut-recorder/shortcut-model.js +206 -0
  221. package/dist/shortcut-recorder/shortcut-model.js.map +1 -0
  222. package/dist/shortcut-recorder/shortcut-recorder.d.ts +93 -0
  223. package/dist/shortcut-recorder/shortcut-recorder.js +489 -0
  224. package/dist/shortcut-recorder/shortcut-recorder.js.map +1 -0
  225. package/dist/slider/index.d.ts +2 -2
  226. package/dist/slider/slider.d.ts +46 -14
  227. package/dist/slider/slider.js +151 -67
  228. package/dist/slider/slider.js.map +1 -1
  229. package/dist/slope-chart/index.d.ts +2 -0
  230. package/dist/slope-chart/index.js +2 -0
  231. package/dist/slope-chart/slope-chart.d.ts +54 -0
  232. package/dist/slope-chart/slope-chart.js +568 -0
  233. package/dist/slope-chart/slope-chart.js.map +1 -0
  234. package/dist/sparkline/index.d.ts +2 -0
  235. package/dist/sparkline/index.js +2 -0
  236. package/dist/sparkline/sparkline.d.ts +46 -0
  237. package/dist/sparkline/sparkline.js +390 -0
  238. package/dist/sparkline/sparkline.js.map +1 -0
  239. package/dist/split-bar/index.d.ts +2 -0
  240. package/dist/split-bar/index.js +2 -0
  241. package/dist/split-bar/split-bar.d.ts +62 -0
  242. package/dist/split-bar/split-bar.js +513 -0
  243. package/dist/split-bar/split-bar.js.map +1 -0
  244. package/dist/split-button/split-button.d.ts +1 -1
  245. package/dist/split-button/split-button.js +12 -11
  246. package/dist/split-button/split-button.js.map +1 -1
  247. package/dist/stat-card/index.d.ts +2 -2
  248. package/dist/stat-card/index.js +2 -2
  249. package/dist/stat-card/stat-card.d.ts +24 -4
  250. package/dist/stat-card/stat-card.js +114 -17
  251. package/dist/stat-card/stat-card.js.map +1 -1
  252. package/dist/stepper/index.d.ts +2 -0
  253. package/dist/stepper/index.js +2 -0
  254. package/dist/stepper/stepper.d.ts +51 -0
  255. package/dist/stepper/stepper.js +215 -0
  256. package/dist/stepper/stepper.js.map +1 -0
  257. package/dist/streamgraph/index.d.ts +2 -0
  258. package/dist/streamgraph/index.js +2 -0
  259. package/dist/streamgraph/streamgraph.d.ts +70 -0
  260. package/dist/streamgraph/streamgraph.js +613 -0
  261. package/dist/streamgraph/streamgraph.js.map +1 -0
  262. package/dist/styles.css +1 -1
  263. package/dist/swipe-actions/index.d.ts +2 -0
  264. package/dist/swipe-actions/index.js +2 -0
  265. package/dist/swipe-actions/swipe-actions.d.ts +67 -0
  266. package/dist/swipe-actions/swipe-actions.js +552 -0
  267. package/dist/swipe-actions/swipe-actions.js.map +1 -0
  268. package/dist/switch/switch.js +2 -2
  269. package/dist/switch/switch.js.map +1 -1
  270. package/dist/table/index.d.ts +2 -2
  271. package/dist/table/index.js +2 -2
  272. package/dist/table/table.d.ts +56 -4
  273. package/dist/table/table.js +340 -165
  274. package/dist/table/table.js.map +1 -1
  275. package/dist/tabs/tabs.js +2 -2
  276. package/dist/tabs/tabs.js.map +1 -1
  277. package/dist/tag-input/index.d.ts +2 -0
  278. package/dist/tag-input/index.js +2 -0
  279. package/dist/tag-input/tag-input.d.ts +40 -0
  280. package/dist/tag-input/tag-input.js +269 -0
  281. package/dist/tag-input/tag-input.js.map +1 -0
  282. package/dist/text-morph/index.d.ts +2 -0
  283. package/dist/text-morph/index.js +2 -0
  284. package/dist/text-morph/text-morph.d.ts +23 -0
  285. package/dist/text-morph/text-morph.js +21 -0
  286. package/dist/text-morph/text-morph.js.map +1 -0
  287. package/dist/text-reveal/index.d.ts +2 -0
  288. package/dist/text-reveal/index.js +2 -0
  289. package/dist/text-reveal/text-reveal.d.ts +27 -0
  290. package/dist/text-reveal/text-reveal.js +51 -0
  291. package/dist/text-reveal/text-reveal.js.map +1 -0
  292. package/dist/text-shimmer/index.d.ts +2 -0
  293. package/dist/text-shimmer/index.js +2 -0
  294. package/dist/text-shimmer/text-shimmer.d.ts +29 -0
  295. package/dist/text-shimmer/text-shimmer.js +29 -0
  296. package/dist/text-shimmer/text-shimmer.js.map +1 -0
  297. package/dist/time-picker/time-picker.js +2 -2
  298. package/dist/time-picker/time-picker.js.map +1 -1
  299. package/dist/toast/index.d.ts +2 -2
  300. package/dist/toast/index.js +2 -2
  301. package/dist/toast/toast.d.ts +6 -0
  302. package/dist/toast/toast.js +82 -40
  303. package/dist/toast/toast.js.map +1 -1
  304. package/dist/tooltip/tooltip.d.ts +9 -2
  305. package/dist/tooltip/tooltip.js +7 -7
  306. package/dist/tooltip/tooltip.js.map +1 -1
  307. package/dist/treemap/index.d.ts +2 -0
  308. package/dist/treemap/index.js +2 -0
  309. package/dist/treemap/treemap.d.ts +53 -0
  310. package/dist/treemap/treemap.js +699 -0
  311. package/dist/treemap/treemap.js.map +1 -0
  312. package/dist/usage-meter/index.d.ts +2 -0
  313. package/dist/usage-meter/index.js +2 -0
  314. package/dist/usage-meter/usage-meter.d.ts +50 -0
  315. package/dist/usage-meter/usage-meter.js +440 -0
  316. package/dist/usage-meter/usage-meter.js.map +1 -0
  317. package/dist/waffle-chart/index.d.ts +2 -0
  318. package/dist/waffle-chart/index.js +2 -0
  319. package/dist/waffle-chart/waffle-chart.d.ts +57 -0
  320. package/dist/waffle-chart/waffle-chart.js +426 -0
  321. package/dist/waffle-chart/waffle-chart.js.map +1 -0
  322. package/package.json +165 -3
@@ -0,0 +1,909 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.js";
3
+ import { useIsomorphicLayoutEffect } from "../lib/motion.js";
4
+ import { Button } from "../button/button.js";
5
+ import { Tooltip, TooltipTrigger } from "../tooltip/tooltip.js";
6
+ import { Label } from "../label/label.js";
7
+ import { FieldShell, useFieldWiring } from "../lib/field-shell.js";
8
+ import { isSafeUrl, normalizeUrl } from "./rich-text-html.js";
9
+ import { $isDocumentEmpty, $readFormats, NO_FORMATS, applyLink, beginLink, createRichTextEditor, loadHtml, loadMarkdown, readValue, setBlock, syncDividers, toggleMark } from "./rich-text-engine.js";
10
+ import { BackIcon, BoldIcon, CheckIcon, CodeBlockIcon, CodeIcon, DividerIcon, Heading1Icon, Heading2Icon, Heading3Icon, ItalicIcon, LinkIcon, ListIcon, ListOrderedIcon, ParagraphIcon, QuoteIcon, StrikeIcon, UnderlineIcon, UnlinkIcon } from "./rich-text-icons.js";
11
+ import { forwardRef, useEffect, useId, useImperativeHandle, useMemo, useRef, useState } from "react";
12
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
13
+ import { $getNodeByKey, $getSelection, $isRangeSelection, $isTextNode, $setSelection, CAN_REDO_COMMAND, CAN_UNDO_COMMAND, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_LOW, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, KEY_DOWN_COMMAND, KEY_ENTER_COMMAND, KEY_ESCAPE_COMMAND, KEY_TAB_COMMAND, REDO_COMMAND, UNDO_COMMAND } from "lexical";
14
+ //#region src/rich-text-editor/rich-text-editor.tsx
15
+ const markTool = (mark, id, label, shortcut, icon, key) => ({
16
+ id,
17
+ label,
18
+ shortcut,
19
+ icon,
20
+ pressed: (formats) => !!formats[key],
21
+ run: (editor) => toggleMark(editor, mark)
22
+ });
23
+ const blockTool = (type, label, icon) => ({
24
+ id: type,
25
+ label,
26
+ icon,
27
+ pressed: (formats) => formats.block === type,
28
+ run: (editor) => setBlock(editor, type)
29
+ });
30
+ const FORMAT_TOOLS = [
31
+ markTool("bold", "bold", "Bold", "B", /* @__PURE__ */ jsx(BoldIcon, {}), "bold"),
32
+ markTool("italic", "italic", "Italic", "I", /* @__PURE__ */ jsx(ItalicIcon, {}), "italic"),
33
+ markTool("underline", "underline", "Underline", "U", /* @__PURE__ */ jsx(UnderlineIcon, {}), "underline"),
34
+ markTool("strikethrough", "strike", "Strikethrough", "⇧X", /* @__PURE__ */ jsx(StrikeIcon, {}), "strike"),
35
+ markTool("code", "code", "Inline code", "E", /* @__PURE__ */ jsx(CodeIcon, {}), "code"),
36
+ {
37
+ id: "link",
38
+ label: "Link",
39
+ shortcut: "K",
40
+ icon: /* @__PURE__ */ jsx(LinkIcon, {}),
41
+ pressed: (formats) => formats.link !== null,
42
+ run: (_editor, openLink) => openLink()
43
+ }
44
+ ];
45
+ const BLOCK_TOOLS = [
46
+ blockTool("h1", "Heading 1", /* @__PURE__ */ jsx(Heading1Icon, {})),
47
+ blockTool("h2", "Heading 2", /* @__PURE__ */ jsx(Heading2Icon, {})),
48
+ blockTool("blockquote", "Quote", /* @__PURE__ */ jsx(QuoteIcon, {}))
49
+ ];
50
+ const SLASH_ITEMS = [
51
+ {
52
+ type: "p",
53
+ label: "Text",
54
+ hint: "",
55
+ keywords: "paragraph plain body",
56
+ icon: /* @__PURE__ */ jsx(ParagraphIcon, {})
57
+ },
58
+ {
59
+ type: "h1",
60
+ label: "Heading 1",
61
+ hint: "#",
62
+ keywords: "title h1 large",
63
+ icon: /* @__PURE__ */ jsx(Heading1Icon, {})
64
+ },
65
+ {
66
+ type: "h2",
67
+ label: "Heading 2",
68
+ hint: "##",
69
+ keywords: "subtitle h2 section",
70
+ icon: /* @__PURE__ */ jsx(Heading2Icon, {})
71
+ },
72
+ {
73
+ type: "h3",
74
+ label: "Heading 3",
75
+ hint: "###",
76
+ keywords: "h3 small",
77
+ icon: /* @__PURE__ */ jsx(Heading3Icon, {})
78
+ },
79
+ {
80
+ type: "ul",
81
+ label: "Bulleted list",
82
+ hint: "-",
83
+ keywords: "bullet unordered ul points",
84
+ icon: /* @__PURE__ */ jsx(ListIcon, {})
85
+ },
86
+ {
87
+ type: "ol",
88
+ label: "Numbered list",
89
+ hint: "1.",
90
+ keywords: "ordered ol steps numbers",
91
+ icon: /* @__PURE__ */ jsx(ListOrderedIcon, {})
92
+ },
93
+ {
94
+ type: "blockquote",
95
+ label: "Quote",
96
+ hint: ">",
97
+ keywords: "blockquote citation callout",
98
+ icon: /* @__PURE__ */ jsx(QuoteIcon, {})
99
+ },
100
+ {
101
+ type: "pre",
102
+ label: "Code block",
103
+ hint: "```",
104
+ keywords: "pre snippet monospace",
105
+ icon: /* @__PURE__ */ jsx(CodeBlockIcon, {})
106
+ },
107
+ {
108
+ type: "hr",
109
+ label: "Divider",
110
+ hint: "---",
111
+ keywords: "rule hr line separator",
112
+ icon: /* @__PURE__ */ jsx(DividerIcon, {})
113
+ }
114
+ ];
115
+ /** ⌘ on Apple platforms, Ctrl elsewhere. Read when a tooltip opens, never during the first render. */
116
+ function Hint({ label, shortcut }) {
117
+ const apple = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.userAgent);
118
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [label, shortcut ? /* @__PURE__ */ jsx("kbd", {
119
+ className: "ms-2 font-sans opacity-80",
120
+ children: `${apple ? "⌘" : "Ctrl+"}${shortcut}`
121
+ }) : null] });
122
+ }
123
+ const toolButton = "size-8 rounded-md p-0 aria-pressed:bg-primary-subtle aria-pressed:text-primary-strong data-[hovered]:aria-pressed:bg-primary-subtle";
124
+ /** The visible band of the nearest scrolling or clipping ancestor, so floating layers flip before they get cut off. */
125
+ function clipBand(element) {
126
+ let top = 0;
127
+ let bottom = window.innerHeight;
128
+ for (let node = element.parentElement; node; node = node.parentElement) {
129
+ const style = getComputedStyle(node);
130
+ if (style.overflowY !== "visible" || style.overflowX !== "visible") {
131
+ const rect = node.getBoundingClientRect();
132
+ top = Math.max(top, rect.top);
133
+ bottom = Math.min(bottom, rect.bottom);
134
+ }
135
+ }
136
+ return {
137
+ top,
138
+ bottom
139
+ };
140
+ }
141
+ const BLOCK_SELECTOR = "p, h1, h2, h3, li, blockquote";
142
+ const RichTextEditor = forwardRef(function RichTextEditor({ value, defaultValue, defaultMarkdown, onChange, onHistoryChange, placeholder = "Start writing", blockHint = "Type / for blocks", isDisabled = false, isReadOnly = false, autoFocus = false, label, description, error, "aria-label": ariaLabel = "Editor", id, className }, ref) {
143
+ const uid = useId();
144
+ const labelId = `${uid}-label`;
145
+ const field = useFieldWiring({
146
+ enabled: true,
147
+ id,
148
+ describedBy: void 0,
149
+ invalid: void 0,
150
+ description,
151
+ error
152
+ });
153
+ const editable = !isDisabled && !isReadOnly;
154
+ const rootRef = useRef(null);
155
+ const surfaceRef = useRef(null);
156
+ const editorRef = useRef(null);
157
+ const lastEmitted = useRef(null);
158
+ const dragging = useRef(false);
159
+ const typedSlash = useRef(false);
160
+ const hinted = useRef(null);
161
+ const callbacks = useRef({
162
+ onChange,
163
+ onHistoryChange
164
+ });
165
+ const initial = useRef({
166
+ value,
167
+ defaultValue,
168
+ defaultMarkdown,
169
+ autoFocus,
170
+ editable
171
+ });
172
+ useIsomorphicLayoutEffect(() => {
173
+ callbacks.current = {
174
+ onChange,
175
+ onHistoryChange
176
+ };
177
+ });
178
+ const [empty, setEmpty] = useState(true);
179
+ const [formats, setFormats] = useState(NO_FORMATS);
180
+ const [bar, setBar] = useState(null);
181
+ const [placed, setPlaced] = useState(false);
182
+ const [face, setFace] = useState("format");
183
+ const [size, setSize] = useState({
184
+ width: 0,
185
+ height: 0
186
+ });
187
+ const [linkDraft, setLinkDraft] = useState("");
188
+ const [stop, setStop] = useState(0);
189
+ const [slash, setSlash] = useState(null);
190
+ const [slashActive, setSlashActive] = useState(0);
191
+ const [panel, setPanel] = useState({
192
+ height: 0,
193
+ sized: false
194
+ });
195
+ const [highlight, setHighlight] = useState({
196
+ y: 0,
197
+ h: 0,
198
+ placed: false
199
+ });
200
+ const [announce, setAnnounce] = useState("");
201
+ const faceRef = useRef(face);
202
+ const slashRef = useRef(slash);
203
+ const sizeRef = useRef(size);
204
+ const savedLink = useRef(null);
205
+ const toolbarRef = useRef(null);
206
+ const formatFaceRef = useRef(null);
207
+ const linkFaceRef = useRef(null);
208
+ const linkInputRef = useRef(null);
209
+ const slashPanelRef = useRef(null);
210
+ const slashBodyRef = useRef(null);
211
+ const listRef = useRef(null);
212
+ useIsomorphicLayoutEffect(() => {
213
+ faceRef.current = face;
214
+ slashRef.current = slash;
215
+ sizeRef.current = size;
216
+ });
217
+ const slashResults = useMemo(() => {
218
+ const query = slash?.query.trim().toLowerCase() ?? "";
219
+ if (!query) return SLASH_ITEMS;
220
+ return SLASH_ITEMS.filter((item) => `${item.label} ${item.keywords}`.toLowerCase().split(/\s+/).some((word) => word.startsWith(query)) || item.label.toLowerCase().includes(query));
221
+ }, [slash?.query]);
222
+ const slashIndex = Math.min(slashActive, Math.max(0, slashResults.length - 1));
223
+ const slashOpen = !!slash && editable && slashResults.length > 0;
224
+ const slashLive = useRef({
225
+ open: slashOpen,
226
+ results: slashResults,
227
+ index: slashIndex
228
+ });
229
+ useIsomorphicLayoutEffect(() => {
230
+ slashLive.current = {
231
+ open: slashOpen,
232
+ results: slashResults,
233
+ index: slashIndex
234
+ };
235
+ });
236
+ const [shownSlash, setShownSlash] = useState(null);
237
+ if (slash && slash !== shownSlash && slashOpen) setShownSlash(slash);
238
+ const panelSlash = slash ?? shownSlash;
239
+ const open = !!bar && editable;
240
+ const sync = () => {
241
+ const editor = editorRef.current;
242
+ const surface = surfaceRef.current;
243
+ const root = rootRef.current;
244
+ if (!editor || !surface || !root) return;
245
+ const selection = window.getSelection();
246
+ const range = !!selection?.rangeCount && surface.contains(selection.anchorNode) ? selection.getRangeAt(0) : null;
247
+ const next = editor.getEditorState().read($readFormats);
248
+ setFormats((current) => Object.keys(next).every((key) => current[key] === next[key]) ? current : next);
249
+ syncDividers(editor);
250
+ const block = (range ? range.startContainer instanceof Element ? range.startContainer : range.startContainer.parentElement : null)?.closest(BLOCK_SELECTOR);
251
+ const blockInEditor = block && surface.contains(block) ? block : null;
252
+ const hint = range?.collapsed && blockInEditor && !blockInEditor.textContent && !blockInEditor.querySelector("li, hr") ? blockInEditor : null;
253
+ if (hinted.current !== hint) {
254
+ hinted.current?.removeAttribute("data-hint");
255
+ hint?.setAttribute("data-hint", "");
256
+ hinted.current = hint;
257
+ }
258
+ if (!range) {
259
+ if (faceRef.current !== "link") setBar(null);
260
+ setSlash(null);
261
+ return;
262
+ }
263
+ const open = slashRef.current;
264
+ if (open) {
265
+ const text = editor.getEditorState().read(() => {
266
+ const sel = $getSelection();
267
+ const node = $getNodeByKey(open.key);
268
+ if (!$isRangeSelection(sel) || !$isTextNode(node) || !sel.isCollapsed() || sel.anchor.key !== open.key) return null;
269
+ const caret = sel.anchor.offset;
270
+ const content = node.getTextContent();
271
+ return caret > open.offset && content[open.offset] === "/" ? content.slice(open.offset + 1, caret) : null;
272
+ });
273
+ if (text === null || text.length > 24 || /^\s/.test(text) || /\s\s/.test(text)) setSlash(null);
274
+ else if (text !== open.query) setSlash({
275
+ ...open,
276
+ query: text
277
+ });
278
+ }
279
+ const inCode = next.block === "pre";
280
+ if (range.collapsed || dragging.current || inCode || slashRef.current || !editable) {
281
+ if (faceRef.current !== "link") setBar(null);
282
+ return;
283
+ }
284
+ const rects = range.getClientRects();
285
+ const rect = rects.length ? rects[0] : range.getBoundingClientRect();
286
+ const whole = range.getBoundingClientRect();
287
+ const box = root.getBoundingClientRect();
288
+ const below = rect.top - clipBand(root).top < 60;
289
+ const half = (sizeRef.current.width || 280) / 2;
290
+ const x = Math.max(half + 4, Math.min(whole.left + whole.width / 2 - box.left, box.width - half - 4));
291
+ const y = below ? whole.bottom - box.top + 8 : rect.top - box.top - 8;
292
+ setBar((current) => current && current.x === x && current.y === y && current.below === below ? current : {
293
+ x,
294
+ y,
295
+ below
296
+ });
297
+ };
298
+ const syncRef = useRef(sync);
299
+ useIsomorphicLayoutEffect(() => {
300
+ syncRef.current = sync;
301
+ });
302
+ const focusSurface = () => surfaceRef.current?.focus({ preventScroll: true });
303
+ const focusEditor = () => {
304
+ editorRef.current?.focus();
305
+ focusSurface();
306
+ };
307
+ const closeLink = (refocus = true) => {
308
+ const editor = editorRef.current;
309
+ const saved = savedLink.current;
310
+ savedLink.current = null;
311
+ faceRef.current = "format";
312
+ setFace("format");
313
+ if (refocus && editor) {
314
+ if (saved) editor.update(() => $setSelection(saved.selection.clone()));
315
+ focusSurface();
316
+ }
317
+ };
318
+ const openLink = () => {
319
+ const editor = editorRef.current;
320
+ if (!editor || !editable) return;
321
+ const link = beginLink(editor);
322
+ if (!link) return;
323
+ savedLink.current = link;
324
+ faceRef.current = "link";
325
+ setLinkDraft(link.url ?? "");
326
+ setFace("link");
327
+ syncRef.current();
328
+ };
329
+ const openLinkRef = useRef(openLink);
330
+ useIsomorphicLayoutEffect(() => {
331
+ openLinkRef.current = openLink;
332
+ });
333
+ const submitLink = (remove = false) => {
334
+ const editor = editorRef.current;
335
+ const saved = savedLink.current;
336
+ if (!editor || !saved) return closeLink();
337
+ const url = remove ? "" : normalizeUrl(linkDraft);
338
+ if (url && !isSafeUrl(url)) {
339
+ setAnnounce("That address cannot be linked");
340
+ return;
341
+ }
342
+ savedLink.current = null;
343
+ faceRef.current = "format";
344
+ setFace("format");
345
+ setBar(null);
346
+ focusSurface();
347
+ applyLink(editor, saved.selection, url || null);
348
+ setAnnounce(url ? "Link added" : "Link removed");
349
+ };
350
+ const chooseSlash = (item) => {
351
+ const editor = editorRef.current;
352
+ const open = slashRef.current;
353
+ if (!editor || !open || !item) return;
354
+ setSlash(null);
355
+ editor.update(() => {
356
+ const node = $getNodeByKey(open.key);
357
+ if (!$isTextNode(node)) return;
358
+ const selection = $getSelection();
359
+ const caret = $isRangeSelection(selection) && selection.anchor.key === open.key ? selection.anchor.offset : open.offset + 1 + open.query.length;
360
+ node.spliceText(open.offset, Math.max(1, caret - open.offset), "", true);
361
+ }, { discrete: true });
362
+ setBlock(editor, item.type);
363
+ setAnnounce(`${item.label} added`);
364
+ };
365
+ const chooseRef = useRef(chooseSlash);
366
+ useIsomorphicLayoutEffect(() => {
367
+ chooseRef.current = chooseSlash;
368
+ });
369
+ useIsomorphicLayoutEffect(() => {
370
+ const surface = surfaceRef.current;
371
+ if (!surface) return;
372
+ const { editor, destroy } = createRichTextEditor(surface);
373
+ editorRef.current = editor;
374
+ const start = initial.current;
375
+ editor.setEditable(start.editable);
376
+ const html = start.value ?? start.defaultValue;
377
+ if (html !== void 0) loadHtml(editor, html, true, true);
378
+ else if (start.defaultMarkdown !== void 0) loadMarkdown(editor, start.defaultMarkdown, true, true);
379
+ else loadHtml(editor, "", true, true);
380
+ lastEmitted.current = start.value ?? null;
381
+ setEmpty(editor.getEditorState().read($isDocumentEmpty));
382
+ let canUndo = false;
383
+ let canRedo = false;
384
+ const reportHistory = () => callbacks.current.onHistoryChange?.({
385
+ canUndo,
386
+ canRedo
387
+ });
388
+ reportHistory();
389
+ const onBeforeInput = (event) => {
390
+ typedSlash.current = event.inputType === "insertText" && event.data === "/";
391
+ };
392
+ surface.addEventListener("beforeinput", onBeforeInput);
393
+ const openSlash = () => {
394
+ const root = rootRef.current;
395
+ const selection = window.getSelection();
396
+ const dom = selection?.anchorNode;
397
+ if (!root || !selection || !dom || dom.nodeType !== Node.TEXT_NODE || !selection.isCollapsed) return;
398
+ const caret = selection.anchorOffset;
399
+ const data = dom.data;
400
+ if (data[caret - 1] !== "/" || caret > 1 && !/\s/.test(data[caret - 2])) return;
401
+ const target = editor.getEditorState().read(() => {
402
+ const sel = $getSelection();
403
+ if (!$isRangeSelection(sel) || !sel.isCollapsed() || !$isTextNode(sel.anchor.getNode())) return null;
404
+ return $readFormats().block === "pre" ? null : {
405
+ key: sel.anchor.key,
406
+ offset: sel.anchor.offset - 1
407
+ };
408
+ });
409
+ if (!target) return;
410
+ const mark = document.createRange();
411
+ mark.setStart(dom, caret - 1);
412
+ mark.setEnd(dom, caret);
413
+ const rect = mark.getBoundingClientRect();
414
+ const box = root.getBoundingClientRect();
415
+ const band = clipBand(root);
416
+ const above = band.bottom - rect.bottom < 330 && rect.top - band.top > band.bottom - rect.bottom;
417
+ const room = above ? rect.top - band.top - 12 : band.bottom - rect.bottom - 12;
418
+ setSlash({
419
+ ...target,
420
+ query: "",
421
+ x: Math.max(0, Math.min(rect.left - box.left - 8, box.width - 244)),
422
+ y: above ? rect.top - box.top - 6 : rect.bottom - box.top + 6,
423
+ above,
424
+ max: Math.max(150, room)
425
+ });
426
+ setSlashActive(0);
427
+ setBar(null);
428
+ };
429
+ function moveSlash(event, step) {
430
+ const live = slashLive.current;
431
+ if (!live.open || !event) return false;
432
+ event.preventDefault();
433
+ setSlashActive((live.index + step + live.results.length) % live.results.length);
434
+ return true;
435
+ }
436
+ function pickSlash(event) {
437
+ const live = slashLive.current;
438
+ if (!live.open || !event) return false;
439
+ event.preventDefault();
440
+ const item = live.results[live.index];
441
+ queueMicrotask(() => chooseRef.current(item));
442
+ return true;
443
+ }
444
+ const off = [
445
+ editor.registerUpdateListener(({ editorState, dirtyElements, dirtyLeaves, tags }) => {
446
+ if (dirtyElements.size > 0 || dirtyLeaves.size > 0) {
447
+ setEmpty(editorState.read($isDocumentEmpty));
448
+ if (!tags.has("rich-text-load")) {
449
+ const next = readValue(editor);
450
+ lastEmitted.current = next.html;
451
+ callbacks.current.onChange?.(next);
452
+ }
453
+ }
454
+ if (typedSlash.current) {
455
+ typedSlash.current = false;
456
+ openSlash();
457
+ }
458
+ syncRef.current();
459
+ }),
460
+ editor.registerCommand(CAN_UNDO_COMMAND, (payload) => {
461
+ canUndo = payload;
462
+ reportHistory();
463
+ return false;
464
+ }, COMMAND_PRIORITY_LOW),
465
+ editor.registerCommand(CAN_REDO_COMMAND, (payload) => {
466
+ canRedo = payload;
467
+ reportHistory();
468
+ return false;
469
+ }, COMMAND_PRIORITY_LOW),
470
+ editor.registerCommand(KEY_ARROW_DOWN_COMMAND, (event) => moveSlash(event, 1), COMMAND_PRIORITY_HIGH),
471
+ editor.registerCommand(KEY_ARROW_UP_COMMAND, (event) => moveSlash(event, -1), COMMAND_PRIORITY_HIGH),
472
+ editor.registerCommand(KEY_ENTER_COMMAND, (event) => pickSlash(event), COMMAND_PRIORITY_HIGH),
473
+ editor.registerCommand(KEY_TAB_COMMAND, (event) => pickSlash(event), COMMAND_PRIORITY_HIGH),
474
+ editor.registerCommand(KEY_ESCAPE_COMMAND, (event) => {
475
+ if (!slashLive.current.open) return false;
476
+ event.preventDefault();
477
+ event.stopPropagation();
478
+ setSlash(null);
479
+ return true;
480
+ }, COMMAND_PRIORITY_HIGH),
481
+ editor.registerCommand(KEY_DOWN_COMMAND, (event) => {
482
+ if (!(event.metaKey || event.ctrlKey) || event.altKey || event.shiftKey || event.key.toLowerCase() !== "k") return false;
483
+ event.preventDefault();
484
+ queueMicrotask(() => openLinkRef.current());
485
+ return true;
486
+ }, COMMAND_PRIORITY_LOW)
487
+ ];
488
+ if (start.autoFocus && start.editable) {
489
+ editor.focus();
490
+ surface.focus({ preventScroll: true });
491
+ }
492
+ return () => {
493
+ off.forEach((unregister) => unregister());
494
+ surface.removeEventListener("beforeinput", onBeforeInput);
495
+ destroy();
496
+ editorRef.current = null;
497
+ };
498
+ }, []);
499
+ useEffect(() => {
500
+ editorRef.current?.setEditable(editable);
501
+ }, [editable]);
502
+ useEffect(() => {
503
+ const editor = editorRef.current;
504
+ if (!editor || value === void 0 || value === lastEmitted.current) return;
505
+ lastEmitted.current = value;
506
+ loadHtml(editor, value, true);
507
+ }, [value]);
508
+ useImperativeHandle(ref, () => ({
509
+ focus: () => {
510
+ editorRef.current?.focus();
511
+ surfaceRef.current?.focus({ preventScroll: true });
512
+ },
513
+ getHTML: () => editorRef.current ? readValue(editorRef.current).html : "",
514
+ getMarkdown: () => editorRef.current ? readValue(editorRef.current).markdown : "",
515
+ getText: () => editorRef.current ? readValue(editorRef.current).text : "",
516
+ setHTML: (html) => editorRef.current && loadHtml(editorRef.current, html, false),
517
+ setMarkdown: (markdown) => editorRef.current && loadMarkdown(editorRef.current, markdown, false),
518
+ clear: () => editorRef.current && loadHtml(editorRef.current, "", false),
519
+ undo: () => {
520
+ editorRef.current?.dispatchCommand(UNDO_COMMAND, void 0);
521
+ setAnnounce("Undone");
522
+ },
523
+ redo: () => {
524
+ editorRef.current?.dispatchCommand(REDO_COMMAND, void 0);
525
+ setAnnounce("Redone");
526
+ },
527
+ get element() {
528
+ return surfaceRef.current;
529
+ }
530
+ }), []);
531
+ useEffect(() => {
532
+ const up = () => {
533
+ if (!dragging.current) return;
534
+ dragging.current = false;
535
+ syncRef.current();
536
+ };
537
+ const down = (event) => {
538
+ if (faceRef.current !== "link" || toolbarRef.current?.contains(event.target)) return;
539
+ savedLink.current = null;
540
+ faceRef.current = "format";
541
+ setFace("format");
542
+ setBar(null);
543
+ };
544
+ let frame = 0;
545
+ const changed = () => {
546
+ cancelAnimationFrame(frame);
547
+ frame = requestAnimationFrame(() => syncRef.current());
548
+ };
549
+ document.addEventListener("pointerup", up);
550
+ document.addEventListener("pointerdown", down);
551
+ document.addEventListener("selectionchange", changed);
552
+ return () => {
553
+ cancelAnimationFrame(frame);
554
+ document.removeEventListener("pointerup", up);
555
+ document.removeEventListener("pointerdown", down);
556
+ document.removeEventListener("selectionchange", changed);
557
+ };
558
+ }, []);
559
+ useEffect(() => {
560
+ if (face !== "link") return;
561
+ const frame = requestAnimationFrame(() => linkInputRef.current?.focus({ preventScroll: true }));
562
+ return () => cancelAnimationFrame(frame);
563
+ }, [face]);
564
+ useEffect(() => {
565
+ if (!open) return;
566
+ let second = 0;
567
+ const first = requestAnimationFrame(() => {
568
+ second = requestAnimationFrame(() => setPlaced(true));
569
+ });
570
+ return () => {
571
+ cancelAnimationFrame(first);
572
+ cancelAnimationFrame(second);
573
+ setPlaced(false);
574
+ };
575
+ }, [open]);
576
+ useIsomorphicLayoutEffect(() => {
577
+ const active = face === "format" ? formatFaceRef.current : linkFaceRef.current;
578
+ if (toolbarRef.current) toolbarRef.current.inert = !open;
579
+ if (formatFaceRef.current) formatFaceRef.current.inert = face !== "format";
580
+ if (linkFaceRef.current) linkFaceRef.current.inert = face !== "link";
581
+ if (!active) return;
582
+ const report = () => setSize((current) => current.width === active.offsetWidth && current.height === active.offsetHeight ? current : {
583
+ width: active.offsetWidth,
584
+ height: active.offsetHeight
585
+ });
586
+ report();
587
+ if (typeof ResizeObserver === "undefined") return;
588
+ const observer = new ResizeObserver(report);
589
+ observer.observe(active);
590
+ return () => observer.disconnect();
591
+ }, [face, open]);
592
+ useIsomorphicLayoutEffect(() => {
593
+ if (!slashOpen) {
594
+ setHighlight((current) => current.placed ? {
595
+ ...current,
596
+ placed: false
597
+ } : current);
598
+ setPanel((current) => current.sized ? {
599
+ ...current,
600
+ sized: false
601
+ } : current);
602
+ return;
603
+ }
604
+ const row = listRef.current?.querySelector(`[data-index="${slashIndex}"]`);
605
+ if (row) {
606
+ setHighlight((current) => ({
607
+ y: row.offsetTop,
608
+ h: row.offsetHeight,
609
+ placed: current.placed || current.h > 0
610
+ }));
611
+ const panelEl = slashPanelRef.current;
612
+ if (panelEl) {
613
+ const top = row.offsetTop + 6;
614
+ const bottom = top + row.offsetHeight;
615
+ if (top - 6 < panelEl.scrollTop) panelEl.scrollTop = top - 6;
616
+ else if (bottom + 6 > panelEl.scrollTop + panelEl.clientHeight) panelEl.scrollTop = bottom + 6 - panelEl.clientHeight;
617
+ }
618
+ }
619
+ const body = slashBodyRef.current;
620
+ if (!body) return;
621
+ const max = panelSlash?.max ?? 320;
622
+ const fit = () => setPanel((current) => {
623
+ const height = Math.min(body.offsetHeight, max);
624
+ return current.height === height ? {
625
+ ...current,
626
+ sized: true
627
+ } : {
628
+ height,
629
+ sized: current.height > 0
630
+ };
631
+ });
632
+ fit();
633
+ if (typeof ResizeObserver === "undefined") return;
634
+ const observer = new ResizeObserver(fit);
635
+ observer.observe(body);
636
+ return () => observer.disconnect();
637
+ }, [
638
+ slashOpen,
639
+ slashIndex,
640
+ slashResults,
641
+ panelSlash?.max
642
+ ]);
643
+ const focusToolbar = () => formatFaceRef.current?.querySelectorAll("button")[stop]?.focus();
644
+ const onSurfaceKeyDown = (event) => {
645
+ if (event.altKey && event.key === "F10" && open) {
646
+ event.preventDefault();
647
+ focusToolbar();
648
+ }
649
+ };
650
+ const onToolbarKeyDown = (event) => {
651
+ if (face === "link") return;
652
+ const buttons = Array.from(formatFaceRef.current?.querySelectorAll("button:not([disabled])") ?? []);
653
+ const at = buttons.indexOf(document.activeElement);
654
+ if (at >= 0 && (event.key === "ArrowRight" || event.key === "ArrowLeft" || event.key === "Home" || event.key === "End")) {
655
+ event.preventDefault();
656
+ buttons[event.key === "Home" ? 0 : event.key === "End" ? buttons.length - 1 : (at + (event.key === "ArrowRight" ? 1 : -1) + buttons.length) % buttons.length]?.focus();
657
+ } else if (event.key === "Escape") {
658
+ event.preventDefault();
659
+ event.stopPropagation();
660
+ focusSurface();
661
+ }
662
+ };
663
+ const tools = [...FORMAT_TOOLS, ...BLOCK_TOOLS];
664
+ const renderTool = (tool) => {
665
+ const index = tools.indexOf(tool);
666
+ return /* @__PURE__ */ jsxs(TooltipTrigger, { children: [/* @__PURE__ */ jsx(Button, {
667
+ variant: "ghost",
668
+ size: "icon",
669
+ className: toolButton,
670
+ "aria-label": tool.label === "Link" && formats.link ? "Edit link" : tool.label,
671
+ "aria-pressed": tool.pressed(formats),
672
+ excludeFromTabOrder: stop !== index,
673
+ preventFocusOnPress: true,
674
+ onFocus: () => setStop(index),
675
+ onPress: () => {
676
+ const editor = editorRef.current;
677
+ if (editor) tool.run(editor, openLink);
678
+ },
679
+ children: tool.icon
680
+ }), /* @__PURE__ */ jsx(Tooltip, {
681
+ placement: "top",
682
+ children: /* @__PURE__ */ jsx(Hint, {
683
+ label: tool.label,
684
+ shortcut: tool.shortcut
685
+ })
686
+ })] }, tool.id);
687
+ };
688
+ const optionId = (index) => `${uid}-slash-${index}`;
689
+ const cssVars = {
690
+ "--rich-text-placeholder": JSON.stringify(placeholder),
691
+ "--rich-text-hint": JSON.stringify(blockHint)
692
+ };
693
+ const toolbarStyle = {
694
+ left: bar?.x ?? 0,
695
+ top: bar?.y ?? 0,
696
+ width: size.width,
697
+ height: size.height
698
+ };
699
+ const surface = /* @__PURE__ */ jsxs("div", {
700
+ ref: rootRef,
701
+ "data-slot": "rich-text-editor-root",
702
+ className: "rich-text",
703
+ children: [
704
+ /* @__PURE__ */ jsx("div", {
705
+ ref: surfaceRef,
706
+ ...field.controlProps,
707
+ "data-slot": "rich-text-editor",
708
+ "data-empty": empty || void 0,
709
+ role: "textbox",
710
+ "aria-multiline": "true",
711
+ "aria-label": label == null ? ariaLabel : void 0,
712
+ "aria-labelledby": label == null ? void 0 : labelId,
713
+ "aria-placeholder": placeholder,
714
+ "aria-readonly": isReadOnly || void 0,
715
+ "aria-disabled": isDisabled || void 0,
716
+ "aria-controls": slashOpen ? `${uid}-slash` : void 0,
717
+ "aria-activedescendant": slashOpen ? optionId(slashIndex) : void 0,
718
+ contentEditable: editable,
719
+ suppressContentEditableWarning: true,
720
+ tabIndex: isReadOnly && !isDisabled ? 0 : void 0,
721
+ spellCheck: true,
722
+ className: "rich-text-surface",
723
+ style: cssVars,
724
+ onKeyDown: onSurfaceKeyDown,
725
+ onPointerDown: () => {
726
+ dragging.current = true;
727
+ }
728
+ }),
729
+ /* @__PURE__ */ jsx("span", {
730
+ role: "status",
731
+ "aria-live": "polite",
732
+ "data-slot": "rich-text-editor-status",
733
+ className: "sr-only",
734
+ children: announce
735
+ }),
736
+ /* @__PURE__ */ jsxs("div", {
737
+ ref: toolbarRef,
738
+ "data-slot": "rich-text-editor-toolbar",
739
+ role: "toolbar",
740
+ "aria-label": "Formatting",
741
+ "aria-keyshortcuts": "Alt+F10",
742
+ "data-open": open || void 0,
743
+ "data-below": bar?.below || void 0,
744
+ "data-placed": open && placed || void 0,
745
+ className: "rich-text-toolbar",
746
+ style: toolbarStyle,
747
+ onKeyDown: onToolbarKeyDown,
748
+ children: [/* @__PURE__ */ jsx("div", {
749
+ ref: formatFaceRef,
750
+ "data-face": "format",
751
+ "data-active": face === "format" || void 0,
752
+ className: "rich-text-face",
753
+ children: /* @__PURE__ */ jsxs("div", {
754
+ className: "rich-text-row",
755
+ children: [FORMAT_TOOLS.map(renderTool), /* @__PURE__ */ jsxs("span", {
756
+ className: "rich-text-block-tools",
757
+ children: [/* @__PURE__ */ jsx("span", {
758
+ "aria-hidden": "true",
759
+ className: "rich-text-divider"
760
+ }), BLOCK_TOOLS.map(renderTool)]
761
+ })]
762
+ })
763
+ }), /* @__PURE__ */ jsx("form", {
764
+ ref: linkFaceRef,
765
+ "data-face": "link",
766
+ "data-active": face === "link" || void 0,
767
+ className: "rich-text-face",
768
+ onSubmit: (event) => {
769
+ event.preventDefault();
770
+ submitLink();
771
+ },
772
+ children: /* @__PURE__ */ jsxs("div", {
773
+ className: "rich-text-row",
774
+ children: [
775
+ /* @__PURE__ */ jsx(Button, {
776
+ variant: "ghost",
777
+ size: "icon",
778
+ className: toolButton,
779
+ type: "button",
780
+ "aria-label": "Back to formatting",
781
+ preventFocusOnPress: true,
782
+ onPress: () => closeLink(),
783
+ children: /* @__PURE__ */ jsx(BackIcon, {})
784
+ }),
785
+ /* @__PURE__ */ jsx("input", {
786
+ ref: linkInputRef,
787
+ className: "rich-text-link-input",
788
+ value: linkDraft,
789
+ onChange: (event) => setLinkDraft(event.target.value),
790
+ onKeyDown: (event) => {
791
+ if (event.key !== "Escape") return;
792
+ event.preventDefault();
793
+ event.stopPropagation();
794
+ closeLink();
795
+ },
796
+ placeholder: "Paste or type a link",
797
+ "aria-label": "Link address",
798
+ inputMode: "url",
799
+ autoComplete: "off",
800
+ spellCheck: false
801
+ }),
802
+ formats.link ? /* @__PURE__ */ jsx(Button, {
803
+ variant: "ghost",
804
+ size: "icon",
805
+ className: toolButton,
806
+ type: "button",
807
+ "aria-label": "Remove link",
808
+ preventFocusOnPress: true,
809
+ onPress: () => submitLink(true),
810
+ children: /* @__PURE__ */ jsx(UnlinkIcon, {})
811
+ }) : null,
812
+ /* @__PURE__ */ jsx(Button, {
813
+ variant: linkDraft.trim() ? "primary" : "secondary",
814
+ size: "icon",
815
+ className: "size-8 rounded-md p-0",
816
+ type: "submit",
817
+ "aria-label": "Apply link",
818
+ children: /* @__PURE__ */ jsx(CheckIcon, {})
819
+ })
820
+ ]
821
+ })
822
+ })]
823
+ }),
824
+ panelSlash ? /* @__PURE__ */ jsx("div", {
825
+ ref: slashPanelRef,
826
+ "data-slot": "rich-text-editor-slash",
827
+ "data-open": slashOpen || void 0,
828
+ "data-above": panelSlash.above || void 0,
829
+ "data-sized": panel.sized || void 0,
830
+ className: "rich-text-slash",
831
+ style: {
832
+ left: panelSlash.x,
833
+ top: panelSlash.y,
834
+ height: panel.height || void 0,
835
+ maxHeight: panelSlash.max
836
+ },
837
+ onMouseDown: (event) => event.preventDefault(),
838
+ children: /* @__PURE__ */ jsx("div", {
839
+ ref: slashBodyRef,
840
+ className: "rich-text-slash-body",
841
+ children: /* @__PURE__ */ jsxs("ul", {
842
+ ref: listRef,
843
+ id: `${uid}-slash`,
844
+ role: "listbox",
845
+ "aria-label": "Blocks",
846
+ className: "rich-text-slash-list",
847
+ children: [/* @__PURE__ */ jsx("li", {
848
+ "aria-hidden": "true",
849
+ "data-placed": highlight.placed || void 0,
850
+ className: "rich-text-slash-highlight",
851
+ style: {
852
+ "--rich-text-y": `${highlight.y}px`,
853
+ "--rich-text-h": `${highlight.h}px`
854
+ }
855
+ }), slashResults.map((item, index) => /* @__PURE__ */ jsxs("li", {
856
+ id: optionId(index),
857
+ role: "option",
858
+ "aria-selected": index === slashIndex,
859
+ "data-index": index,
860
+ className: "rich-text-slash-option",
861
+ onPointerMove: () => {
862
+ if (index !== slashIndex) setSlashActive(index);
863
+ },
864
+ onClick: () => chooseSlash(item),
865
+ children: [
866
+ /* @__PURE__ */ jsx("span", {
867
+ className: "grid w-5 flex-none place-items-center",
868
+ children: item.icon
869
+ }),
870
+ /* @__PURE__ */ jsx("span", {
871
+ className: "min-w-0 flex-1 truncate font-medium",
872
+ children: item.label
873
+ }),
874
+ item.hint ? /* @__PURE__ */ jsx("kbd", {
875
+ className: "flex-none font-mono text-xs text-muted-foreground",
876
+ children: item.hint
877
+ }) : null
878
+ ]
879
+ }, item.type))]
880
+ })
881
+ })
882
+ }) : null
883
+ ]
884
+ });
885
+ return /* @__PURE__ */ jsx("div", {
886
+ "data-slot": "rich-text-editor-field",
887
+ className: cn("w-full min-w-0 font-sans", className),
888
+ children: /* @__PURE__ */ jsxs(FieldShell, {
889
+ label: null,
890
+ controlId: field.controlId,
891
+ description,
892
+ hintId: field.hintId,
893
+ error,
894
+ errorId: field.errorId,
895
+ wordExit: "quick",
896
+ children: [label == null ? null : /* @__PURE__ */ jsx(Label, {
897
+ id: labelId,
898
+ className: "mb-1.5",
899
+ onClick: focusEditor,
900
+ children: label
901
+ }), surface]
902
+ })
903
+ });
904
+ });
905
+ RichTextEditor.displayName = "RichTextEditor";
906
+ //#endregion
907
+ export { RichTextEditor };
908
+
909
+ //# sourceMappingURL=rich-text-editor.js.map