@k2b/ui 0.17.0 → 0.19.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 (228) hide show
  1. package/README.md +3 -2
  2. package/dist/browser/actions/ContextMenu.js +3 -1
  3. package/dist/browser/actions/ContextMenu.js.map +1 -1
  4. package/dist/browser/actions/Dropdown.js +91 -46
  5. package/dist/browser/actions/Dropdown.js.map +1 -1
  6. package/dist/browser/actions/FilterChip.js +4 -1
  7. package/dist/browser/actions/FilterChip.js.map +1 -1
  8. package/dist/browser/chat/ChatComposer.js +1311 -565
  9. package/dist/browser/chat/ChatComposer.js.map +1 -1
  10. package/dist/browser/chat/ChatPrimitives.js +9 -6
  11. package/dist/browser/chat/ChatPrimitives.js.map +1 -1
  12. package/dist/browser/chat/chat-behavior.js +9 -0
  13. package/dist/browser/chat/chat-behavior.js.map +1 -1
  14. package/dist/browser/chat/index.js.map +1 -1
  15. package/dist/browser/chat/types.js.map +1 -1
  16. package/dist/browser/content/Calendar.js +579 -533
  17. package/dist/browser/content/Calendar.js.map +1 -1
  18. package/dist/browser/content/FileView.js +62 -36
  19. package/dist/browser/content/FileView.js.map +1 -1
  20. package/dist/browser/content/Lightbox.js +76 -40
  21. package/dist/browser/content/Lightbox.js.map +1 -1
  22. package/dist/browser/content/MarkdownView.js +9 -3
  23. package/dist/browser/content/MarkdownView.js.map +1 -1
  24. package/dist/browser/content/PdfPreview.js +146 -79
  25. package/dist/browser/content/PdfPreview.js.map +1 -1
  26. package/dist/browser/content/Timeline.js +1038 -0
  27. package/dist/browser/content/Timeline.js.map +1 -0
  28. package/dist/browser/content/VideoPlayer.js +299 -0
  29. package/dist/browser/content/VideoPlayer.js.map +1 -0
  30. package/dist/browser/content/chart-inspection.js +7 -2
  31. package/dist/browser/content/chart-inspection.js.map +1 -1
  32. package/dist/browser/content/file-view-preview.js +2 -2
  33. package/dist/browser/content/file-view-preview.js.map +1 -1
  34. package/dist/browser/content/index.js +3 -0
  35. package/dist/browser/content/index.js.map +1 -1
  36. package/dist/browser/content/markdown-links.js +178 -0
  37. package/dist/browser/content/markdown-links.js.map +1 -0
  38. package/dist/browser/content/timeline-model.js +579 -0
  39. package/dist/browser/content/timeline-model.js.map +1 -0
  40. package/dist/browser/feedback/dialog-core.js +26 -14
  41. package/dist/browser/feedback/dialog-core.js.map +1 -1
  42. package/dist/browser/inputs/ChoiceChips.js +66 -0
  43. package/dist/browser/inputs/ChoiceChips.js.map +1 -0
  44. package/dist/browser/inputs/ChoiceGroups.js +53 -12
  45. package/dist/browser/inputs/ChoiceGroups.js.map +1 -1
  46. package/dist/browser/inputs/FileDropTarget.js +129 -0
  47. package/dist/browser/inputs/FileDropTarget.js.map +1 -0
  48. package/dist/browser/inputs/FileInputs.js +84 -43
  49. package/dist/browser/inputs/FileInputs.js.map +1 -1
  50. package/dist/browser/inputs/SignatureInput.js +558 -0
  51. package/dist/browser/inputs/SignatureInput.js.map +1 -0
  52. package/dist/browser/inputs/emoji/EmojiPicker.js +841 -0
  53. package/dist/browser/inputs/emoji/EmojiPicker.js.map +1 -0
  54. package/dist/browser/inputs/emoji/emoji-data.js +1903 -0
  55. package/dist/browser/inputs/emoji/emoji-data.js.map +1 -0
  56. package/dist/browser/inputs/emoji/emoji-index.js +209 -0
  57. package/dist/browser/inputs/emoji/emoji-index.js.map +1 -0
  58. package/dist/browser/inputs/emoji/emoji-messages.js +64 -0
  59. package/dist/browser/inputs/emoji/emoji-messages.js.map +1 -0
  60. package/dist/browser/inputs/file-drop.js +338 -0
  61. package/dist/browser/inputs/file-drop.js.map +1 -0
  62. package/dist/browser/inputs/index.js +6 -0
  63. package/dist/browser/inputs/index.js.map +1 -1
  64. package/dist/browser/inputs/markdown/MarkdownEditor.js +72 -78
  65. package/dist/browser/inputs/markdown/MarkdownEditor.js.map +1 -1
  66. package/dist/browser/inputs/markdown/actions.js +48 -2
  67. package/dist/browser/inputs/markdown/actions.js.map +1 -1
  68. package/dist/browser/inputs/markdown/code-zone.js +38 -2
  69. package/dist/browser/inputs/markdown/code-zone.js.map +1 -1
  70. package/dist/browser/inputs/signature-messages.js +39 -0
  71. package/dist/browser/inputs/signature-messages.js.map +1 -0
  72. package/dist/browser/inputs/signature.js +208 -0
  73. package/dist/browser/inputs/signature.js.map +1 -0
  74. package/dist/browser/internal/render-error.js +49 -0
  75. package/dist/browser/internal/render-error.js.map +1 -0
  76. package/dist/browser/intl/messages.js +205 -4
  77. package/dist/browser/intl/messages.js.map +1 -1
  78. package/dist/browser/layout/AppWorkspace.js +483 -218
  79. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  80. package/dist/browser/layout/FloatingWindow.js +4 -1
  81. package/dist/browser/layout/FloatingWindow.js.map +1 -1
  82. package/dist/browser/layout/Navigation.js +274 -197
  83. package/dist/browser/layout/Navigation.js.map +1 -1
  84. package/dist/browser/layout/SidebarItemPreview.js +5 -1
  85. package/dist/browser/layout/SidebarItemPreview.js.map +1 -1
  86. package/dist/browser/layout/index.js.map +1 -1
  87. package/dist/browser/layout/navigation-model.js +2 -0
  88. package/dist/browser/layout/navigation-model.js.map +1 -1
  89. package/dist/browser/surfaces/NoticeCard.js +47 -44
  90. package/dist/browser/surfaces/NoticeCard.js.map +1 -1
  91. package/dist/browser/surfaces/ResourceCard.js +212 -0
  92. package/dist/browser/surfaces/ResourceCard.js.map +1 -0
  93. package/dist/browser/surfaces/index.js +1 -0
  94. package/dist/browser/surfaces/index.js.map +1 -1
  95. package/dist/licenses/emojibase-data.txt +21 -0
  96. package/dist/ssr/actions/ContextMenu.js +3 -1
  97. package/dist/ssr/actions/ContextMenu.js.map +1 -1
  98. package/dist/ssr/actions/Dropdown.js +49 -12
  99. package/dist/ssr/actions/Dropdown.js.map +1 -1
  100. package/dist/ssr/actions/FilterChip.js +4 -1
  101. package/dist/ssr/actions/FilterChip.js.map +1 -1
  102. package/dist/ssr/chat/ChatComposer.js +823 -221
  103. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  104. package/dist/ssr/chat/ChatPrimitives.js +9 -6
  105. package/dist/ssr/chat/ChatPrimitives.js.map +1 -1
  106. package/dist/ssr/chat/chat-behavior.js +9 -0
  107. package/dist/ssr/chat/chat-behavior.js.map +1 -1
  108. package/dist/ssr/chat/index.js.map +1 -1
  109. package/dist/ssr/chat/types.js.map +1 -1
  110. package/dist/ssr/content/Calendar.js +183 -142
  111. package/dist/ssr/content/Calendar.js.map +1 -1
  112. package/dist/ssr/content/FileView.js +30 -6
  113. package/dist/ssr/content/FileView.js.map +1 -1
  114. package/dist/ssr/content/Lightbox.js +19 -5
  115. package/dist/ssr/content/Lightbox.js.map +1 -1
  116. package/dist/ssr/content/MarkdownView.js +9 -3
  117. package/dist/ssr/content/MarkdownView.js.map +1 -1
  118. package/dist/ssr/content/PdfPreview.js +77 -36
  119. package/dist/ssr/content/PdfPreview.js.map +1 -1
  120. package/dist/ssr/content/Timeline.js +804 -0
  121. package/dist/ssr/content/Timeline.js.map +1 -0
  122. package/dist/ssr/content/VideoPlayer.js +228 -0
  123. package/dist/ssr/content/VideoPlayer.js.map +1 -0
  124. package/dist/ssr/content/chart-inspection.js +7 -2
  125. package/dist/ssr/content/chart-inspection.js.map +1 -1
  126. package/dist/ssr/content/file-view-preview.js +2 -2
  127. package/dist/ssr/content/file-view-preview.js.map +1 -1
  128. package/dist/ssr/content/index.js +3 -0
  129. package/dist/ssr/content/index.js.map +1 -1
  130. package/dist/ssr/content/markdown-links.js +178 -0
  131. package/dist/ssr/content/markdown-links.js.map +1 -0
  132. package/dist/ssr/content/timeline-model.js +579 -0
  133. package/dist/ssr/content/timeline-model.js.map +1 -0
  134. package/dist/ssr/feedback/dialog-core.js +26 -14
  135. package/dist/ssr/feedback/dialog-core.js.map +1 -1
  136. package/dist/ssr/inputs/ChoiceChips.js +66 -0
  137. package/dist/ssr/inputs/ChoiceChips.js.map +1 -0
  138. package/dist/ssr/inputs/ChoiceGroups.js +12 -4
  139. package/dist/ssr/inputs/ChoiceGroups.js.map +1 -1
  140. package/dist/ssr/inputs/FileDropTarget.js +103 -0
  141. package/dist/ssr/inputs/FileDropTarget.js.map +1 -0
  142. package/dist/ssr/inputs/FileInputs.js +49 -12
  143. package/dist/ssr/inputs/FileInputs.js.map +1 -1
  144. package/dist/ssr/inputs/SignatureInput.js +404 -0
  145. package/dist/ssr/inputs/SignatureInput.js.map +1 -0
  146. package/dist/ssr/inputs/emoji/EmojiPicker.js +551 -0
  147. package/dist/ssr/inputs/emoji/EmojiPicker.js.map +1 -0
  148. package/dist/ssr/inputs/emoji/emoji-data.js +1903 -0
  149. package/dist/ssr/inputs/emoji/emoji-data.js.map +1 -0
  150. package/dist/ssr/inputs/emoji/emoji-index.js +209 -0
  151. package/dist/ssr/inputs/emoji/emoji-index.js.map +1 -0
  152. package/dist/ssr/inputs/emoji/emoji-messages.js +64 -0
  153. package/dist/ssr/inputs/emoji/emoji-messages.js.map +1 -0
  154. package/dist/ssr/inputs/file-drop.js +338 -0
  155. package/dist/ssr/inputs/file-drop.js.map +1 -0
  156. package/dist/ssr/inputs/index.js +6 -0
  157. package/dist/ssr/inputs/index.js.map +1 -1
  158. package/dist/ssr/inputs/markdown/MarkdownEditor.js +14 -2
  159. package/dist/ssr/inputs/markdown/MarkdownEditor.js.map +1 -1
  160. package/dist/ssr/inputs/markdown/actions.js +48 -2
  161. package/dist/ssr/inputs/markdown/actions.js.map +1 -1
  162. package/dist/ssr/inputs/markdown/code-zone.js +38 -2
  163. package/dist/ssr/inputs/markdown/code-zone.js.map +1 -1
  164. package/dist/ssr/inputs/signature-messages.js +39 -0
  165. package/dist/ssr/inputs/signature-messages.js.map +1 -0
  166. package/dist/ssr/inputs/signature.js +208 -0
  167. package/dist/ssr/inputs/signature.js.map +1 -0
  168. package/dist/ssr/internal/render-error.js +49 -0
  169. package/dist/ssr/internal/render-error.js.map +1 -0
  170. package/dist/ssr/intl/messages.js +205 -4
  171. package/dist/ssr/intl/messages.js.map +1 -1
  172. package/dist/ssr/layout/AppWorkspace.js +264 -47
  173. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  174. package/dist/ssr/layout/FloatingWindow.js +4 -1
  175. package/dist/ssr/layout/FloatingWindow.js.map +1 -1
  176. package/dist/ssr/layout/Navigation.js +169 -122
  177. package/dist/ssr/layout/Navigation.js.map +1 -1
  178. package/dist/ssr/layout/SidebarItemPreview.js +5 -1
  179. package/dist/ssr/layout/SidebarItemPreview.js.map +1 -1
  180. package/dist/ssr/layout/index.js.map +1 -1
  181. package/dist/ssr/layout/navigation-model.js +2 -0
  182. package/dist/ssr/layout/navigation-model.js.map +1 -1
  183. package/dist/ssr/surfaces/NoticeCard.js +17 -12
  184. package/dist/ssr/surfaces/NoticeCard.js.map +1 -1
  185. package/dist/ssr/surfaces/ResourceCard.js +115 -0
  186. package/dist/ssr/surfaces/ResourceCard.js.map +1 -0
  187. package/dist/ssr/surfaces/index.js +1 -0
  188. package/dist/ssr/surfaces/index.js.map +1 -1
  189. package/dist/styles.css +1 -1
  190. package/dist/types/actions/Dropdown.d.ts +13 -1
  191. package/dist/types/actions/FilterChip.d.ts +5 -0
  192. package/dist/types/chat/ChatComposer.d.ts +24 -1
  193. package/dist/types/chat/ChatPrimitives.d.ts +1 -1
  194. package/dist/types/chat/chat-behavior.d.ts +15 -0
  195. package/dist/types/chat/index.d.ts +1 -1
  196. package/dist/types/chat/types.d.ts +34 -0
  197. package/dist/types/content/Calendar.d.ts +28 -8
  198. package/dist/types/content/Lightbox.d.ts +8 -0
  199. package/dist/types/content/Timeline.d.ts +49 -0
  200. package/dist/types/content/VideoPlayer.d.ts +41 -0
  201. package/dist/types/content/index.d.ts +6 -2
  202. package/dist/types/content/markdown-links.d.ts +66 -0
  203. package/dist/types/content/timeline-model.d.ts +148 -0
  204. package/dist/types/feedback/dialog-core.d.ts +1 -1
  205. package/dist/types/inputs/ChoiceChips.d.ts +20 -0
  206. package/dist/types/inputs/ChoiceGroups.d.ts +11 -2
  207. package/dist/types/inputs/FileDropTarget.d.ts +23 -0
  208. package/dist/types/inputs/FileInputs.d.ts +8 -0
  209. package/dist/types/inputs/SignatureInput.d.ts +22 -0
  210. package/dist/types/inputs/emoji/EmojiPicker.d.ts +42 -0
  211. package/dist/types/inputs/emoji/emoji-data.d.ts +2 -0
  212. package/dist/types/inputs/emoji/emoji-index.d.ts +109 -0
  213. package/dist/types/inputs/emoji/emoji-messages.d.ts +30 -0
  214. package/dist/types/inputs/file-drop.d.ts +80 -0
  215. package/dist/types/inputs/index.d.ts +12 -0
  216. package/dist/types/inputs/markdown/actions.d.ts +4 -0
  217. package/dist/types/inputs/markdown/code-zone.d.ts +7 -0
  218. package/dist/types/inputs/signature-messages.d.ts +11 -0
  219. package/dist/types/inputs/signature.d.ts +72 -0
  220. package/dist/types/internal/render-error.d.ts +13 -0
  221. package/dist/types/intl/messages.d.ts +101 -0
  222. package/dist/types/layout/AppWorkspace.d.ts +38 -0
  223. package/dist/types/layout/SidebarItemPreview.d.ts +2 -0
  224. package/dist/types/layout/index.d.ts +2 -2
  225. package/dist/types/layout/navigation-model.d.ts +18 -0
  226. package/dist/types/surfaces/ResourceCard.d.ts +31 -0
  227. package/dist/types/surfaces/index.d.ts +2 -0
  228. package/package.json +5 -4
@@ -0,0 +1,39 @@
1
+ import { i18n } from "@k2b/stdlib";
2
+ import { useLocale } from "../intl/locale";
3
+
4
+ /**
5
+ * SignatureInput's own strings. They live beside the component instead of in
6
+ * the shared catalog, so applications that never render a signature field do
7
+ * not ship them.
8
+ */
9
+ const signatureMessages = i18n.define({
10
+ baseLocale: "en",
11
+ messages: {
12
+ en: {
13
+ method: "Signature method",
14
+ draw: "Draw",
15
+ type: "Type",
16
+ signHere: "Sign here",
17
+ drawArea: "Signature area. Draw with a finger, pen, or mouse.",
18
+ drawn: "Drawn signature",
19
+ typeName: "Type your name",
20
+ undo: "Undo"
21
+ },
22
+ de: {
23
+ method: "Art der Unterschrift",
24
+ draw: "Zeichnen",
25
+ type: "Tippen",
26
+ signHere: "Hier unterschreiben",
27
+ drawArea: "Unterschriftsfeld. Zeichne mit Finger, Stift oder Maus.",
28
+ drawn: "Gezeichnete Unterschrift",
29
+ typeName: "Namen eintippen",
30
+ undo: "Rückgängig"
31
+ }
32
+ }
33
+ });
34
+ export const useSignatureMessages = () => {
35
+ const locale = useLocale();
36
+ return () => signatureMessages.resolve([locale()]).t;
37
+ };
38
+ export const checkSignatureMessages = () => signatureMessages.check();
39
+ //# sourceMappingURL=signature-messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["inputs/signature-messages.ts"],"sourcesContent":["import { i18n } from \"@k2b/stdlib\";\nimport { useLocale } from \"../intl/locale\";\n\n/**\n * SignatureInput's own strings. They live beside the component instead of in\n * the shared catalog, so applications that never render a signature field do\n * not ship them.\n */\nconst signatureMessages = i18n.define({\n baseLocale: \"en\",\n messages: {\n en: {\n method: \"Signature method\",\n draw: \"Draw\",\n type: \"Type\",\n signHere: \"Sign here\",\n drawArea: \"Signature area. Draw with a finger, pen, or mouse.\",\n drawn: \"Drawn signature\",\n typeName: \"Type your name\",\n undo: \"Undo\",\n },\n de: {\n method: \"Art der Unterschrift\",\n draw: \"Zeichnen\",\n type: \"Tippen\",\n signHere: \"Hier unterschreiben\",\n drawArea: \"Unterschriftsfeld. Zeichne mit Finger, Stift oder Maus.\",\n drawn: \"Gezeichnete Unterschrift\",\n typeName: \"Namen eintippen\",\n undo: \"Rückgängig\",\n },\n },\n});\n\nexport const useSignatureMessages = () => {\n const locale = useLocale();\n return () => signatureMessages.resolve([locale()]).t;\n};\n\nexport const checkSignatureMessages = () => signatureMessages.check();\n"],"mappings":"AAAA,SAAS,IAAI,QAAQ,aAAa;AAClC,SAAS,SAAS,QAAQ,gBAAgB;;AAE1C;AACA;AACA;AACA;AACA;AACA,MAAM,iBAAiB,GAAG,IAAI,CAAC,MAAM,CAAC;EACpC,UAAU,EAAE,IAAI;EAChB,QAAQ,EAAE;IACR,EAAE,EAAE;MACF,MAAM,EAAE,kBAAkB;MAC1B,IAAI,EAAE,MAAM;MACZ,IAAI,EAAE,MAAM;MACZ,QAAQ,EAAE,WAAW;MACrB,QAAQ,EAAE,oDAAoD;MAC9D,KAAK,EAAE,iBAAiB;MACxB,QAAQ,EAAE,gBAAgB;MAC1B,IAAI,EAAE;IACR,CAAC;IACD,EAAE,EAAE;MACF,MAAM,EAAE,sBAAsB;MAC9B,IAAI,EAAE,UAAU;MAChB,IAAI,EAAE,QAAQ;MACd,QAAQ,EAAE,qBAAqB;MAC/B,QAAQ,EAAE,yDAAyD;MACnE,KAAK,EAAE,0BAA0B;MACjC,QAAQ,EAAE,iBAAiB;MAC3B,IAAI,EAAE;IACR;EACF;AACF,CAAC,CAAC;AAEF,OAAO,MAAM,oBAAoB,GAAG,MAAM;EACxC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC;EAC1B,OAAO,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,OAAO,MAAM,sBAAsB,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,CAAC","ignoreList":[]}
@@ -0,0 +1,208 @@
1
+ /**
2
+ * A signature as the field reports it. `kind` records how it was given, so the
3
+ * application can keep that fact next to the image. `svg` is a standalone SVG
4
+ * document whose ink uses `currentColor`, so it renders black as an image. A
5
+ * value that arrives from a browser is untrusted input: read it with
6
+ * `parseSignature` before storing or embedding it.
7
+ */
8
+
9
+ /** One sampled pointer position in drawing units, with a pressure from 0 to 1. */
10
+
11
+ /** The drawing space as an SVG viewBox and one closed outline path per stroke. */
12
+
13
+ const SVG_NS = "http://www.w3.org/2000/svg";
14
+ /** Typed names are laid out at this size; the SVG scales as a whole. */
15
+ const TYPED_FONT_SIZE = 48;
16
+ const TYPED_PADDING = 12;
17
+ const round = value => Math.round(value * 10) / 10;
18
+ const point = (x, y) => `${round(x)} ${round(y)}`;
19
+
20
+ /** Control characters, lone surrogates, and the two noncharacters XML forbids, which a strict XML parser rejects. */
21
+ const NOT_XML_TEXT = /[\p{Cc}\p{Cs}\uFFFE\uFFFF]/gu;
22
+
23
+ /** Escapes text for XML content or attributes and drops characters XML cannot hold, so the document stays well-formed. */
24
+ export const escapeXml = text => text.replace(NOT_XML_TEXT, "").replace(/[&<>"']/g, char => ({
25
+ "&": "&amp;",
26
+ "<": "&lt;",
27
+ ">": "&gt;",
28
+ '"': "&quot;",
29
+ "'": "&apos;"
30
+ })[char] ?? char);
31
+ const unescapeXml = text => text.replace(/&(amp|lt|gt|quot|apos);/g, (_, name) => ({
32
+ amp: "&",
33
+ lt: "<",
34
+ gt: ">",
35
+ quot: '"',
36
+ apos: "'"
37
+ })[name] ?? "");
38
+
39
+ /** The name as a typed signature stores it: without characters an SVG cannot hold and without surrounding space. */
40
+ export const signatureName = name => name.replace(NOT_XML_TEXT, "").trim();
41
+
42
+ /** Stroke width in drawing units at a pressure: light pressure still leaves a readable line. */
43
+ export const strokeWidth = (size, pressure) => size * (0.35 + 0.65 * Math.min(1, Math.max(0, pressure)));
44
+
45
+ /**
46
+ * Turns sampled points into one filled outline: both edges follow the points at
47
+ * half the local stroke width and are smoothed through their midpoints, and
48
+ * round caps close the ends. A filled outline, unlike a stroked path, can change
49
+ * width along the line, so pen pressure and drawing speed show in the ink.
50
+ */
51
+ export const strokeOutline = (points, size) => {
52
+ const first = points[0];
53
+ if (!first) return "";
54
+ if (points.length === 1) {
55
+ const radius = round(strokeWidth(size, first.pressure) / 2);
56
+ const arc = x => `A${radius} ${radius} 0 1 0 ${point(x, first.y)}`;
57
+ return `M${point(first.x - radius, first.y)}${arc(first.x + radius)}${arc(first.x - radius)}Z`;
58
+ }
59
+ const left = [];
60
+ const right = [];
61
+ points.forEach((current, index) => {
62
+ const before = points[index - 1] ?? current;
63
+ const after = points[index + 1] ?? current;
64
+ const dx = after.x - before.x;
65
+ const dy = after.y - before.y;
66
+ const length = Math.hypot(dx, dy) || 1;
67
+ const radius = strokeWidth(size, current.pressure) / 2;
68
+ const nx = -dy / length * radius;
69
+ const ny = dx / length * radius;
70
+ left.push({
71
+ x: current.x + nx,
72
+ y: current.y + ny,
73
+ radius
74
+ });
75
+ right.push({
76
+ x: current.x - nx,
77
+ y: current.y - ny,
78
+ radius
79
+ });
80
+ });
81
+ right.reverse();
82
+ // Quadratic segments through the midpoints of neighbouring edge points keep the outline smooth without overshoot.
83
+ const edge = side => side.map((current, index) => {
84
+ if (index === 0) return "";
85
+ const next = side[index + 1];
86
+ if (!next) return `L${point(current.x, current.y)}`;
87
+ return `Q${point(current.x, current.y)} ${point((current.x + next.x) / 2, (current.y + next.y) / 2)}`;
88
+ }).join("");
89
+ const cap = (from, to) => `A${round(from.radius)} ${round(from.radius)} 0 0 0 ${point(to.x, to.y)}`;
90
+ const start = left[0] ?? right[0];
91
+ const end = left[left.length - 1];
92
+ const back = right[0];
93
+ const backEnd = right[right.length - 1];
94
+ if (!start || !end || !back || !backEnd) return "";
95
+ return `M${point(start.x, start.y)}${edge(left)}${cap(end, back)}${edge(right)}${cap(backEnd, start)}Z`;
96
+ };
97
+
98
+ /** Serializes a drawing as a standalone SVG document; an empty drawing has no value. */
99
+ export const drawingToSvg = drawing => {
100
+ if (drawing.strokes.length === 0) return null;
101
+ const width = round(drawing.width);
102
+ const height = round(drawing.height);
103
+ const paths = drawing.strokes.map(stroke => `<path d="${escapeXml(stroke)}"/>`).join("");
104
+ return `<svg xmlns="${SVG_NS}" viewBox="${round(drawing.x)} ${round(drawing.y)} ${width} ${height}" width="${width}" height="${height}"><g fill="currentColor">${paths}</g></svg>`;
105
+ };
106
+ const NUMBER = String.raw`-?\d+(?:\.\d+)?`;
107
+ const SVG_NS_PATTERN = String.raw`http://www\.w3\.org/2000/svg`;
108
+ // Exactly the markup drawingToSvg and typedToSvg write. Path data may only hold the commands and numbers strokeOutline
109
+ // emits, and no attribute or text may hold markup, so a match cannot carry scripts, links, or other elements.
110
+ const DRAWN_SVG = new RegExp(`^<svg xmlns="${SVG_NS_PATTERN}" viewBox="(${NUMBER}) (${NUMBER}) (${NUMBER}) (${NUMBER})" width="${NUMBER}" height="${NUMBER}">` + `<g fill="currentColor">((?:<path d="[MLQAZ\\d .-]+"/>)+)</g></svg>$`);
111
+ const TYPED_SVG = new RegExp(`^<svg xmlns="${SVG_NS_PATTERN}" viewBox="0 0 (\\d+) \\d+" width="\\d+" height="\\d+">` + `<text x="\\d+" y="\\d+" font-family="([^"<>]*)" font-size="\\d+" fill="currentColor">[^<>]*</text></svg>$`);
112
+
113
+ /** Reads the drawing space and strokes back from an SVG drawingToSvg wrote, so a stored signature can be extended or undone. */
114
+ export const svgToDrawing = svg => {
115
+ const match = DRAWN_SVG.exec(svg);
116
+ if (!match) return null;
117
+ const [x = 0, y = 0, width = 0, height = 0] = match.slice(1, 5).map(Number);
118
+ if (width <= 0 || height <= 0) return null;
119
+ const strokes = [...(match[5] ?? "").matchAll(/<path d="([^"]+)"\/>/g)].flatMap(path => path[1] ? [path[1]] : []);
120
+ return {
121
+ x,
122
+ y,
123
+ width,
124
+ height,
125
+ strokes
126
+ };
127
+ };
128
+
129
+ /**
130
+ * Lays out a typed name as an SVG document in the given font family. `measure`
131
+ * returns the text width at `TYPED_FONT_SIZE`; without one, the width is estimated.
132
+ */
133
+ export const typedToSvg = (name, fontFamily, measure) => {
134
+ const font = `${TYPED_FONT_SIZE}px ${fontFamily}`;
135
+ const textWidth = measure?.(name, font) ?? name.length * TYPED_FONT_SIZE * 0.5;
136
+ const width = Math.ceil(textWidth + TYPED_PADDING * 2);
137
+ const height = Math.ceil(TYPED_FONT_SIZE * 1.6);
138
+ const baseline = Math.round(TYPED_FONT_SIZE * 1.15);
139
+ return `<svg xmlns="${SVG_NS}" viewBox="0 0 ${width} ${height}" width="${width}" height="${height}">` + `<text x="${TYPED_PADDING}" y="${baseline}" font-family="${escapeXml(fontFamily)}" font-size="${TYPED_FONT_SIZE}" fill="currentColor">${escapeXml(name)}</text></svg>`;
140
+ };
141
+
142
+ /**
143
+ * Reads a submitted signature on the server: the JSON string of the field's
144
+ * hidden input, or a value the browser sent as JSON. It accepts only markup
145
+ * the field writes and returns it rebuilt, so the result is safe to store and
146
+ * to embed. An empty or invalid value returns `null`.
147
+ */
148
+ export const parseSignature = input => {
149
+ let value = input;
150
+ if (typeof input === "string") {
151
+ if (!input) return null;
152
+ try {
153
+ value = JSON.parse(input);
154
+ } catch {
155
+ return null;
156
+ }
157
+ }
158
+ if (typeof value !== "object" || value === null || !("kind" in value) || !("svg" in value) || typeof value.svg !== "string") return null;
159
+ if (value.kind === "drawn") {
160
+ const drawing = svgToDrawing(value.svg);
161
+ const svg = drawing && drawingToSvg(drawing);
162
+ return svg ? {
163
+ kind: "drawn",
164
+ svg
165
+ } : null;
166
+ }
167
+ if (value.kind !== "typed" || !("name" in value) || typeof value.name !== "string") return null;
168
+ const name = signatureName(value.name);
169
+ const match = TYPED_SVG.exec(value.svg);
170
+ if (!name || !match) return null;
171
+ const textWidth = Number(match[1]) - TYPED_PADDING * 2;
172
+ return {
173
+ kind: "typed",
174
+ name,
175
+ svg: typedToSvg(name, unescapeXml(match[2] ?? ""), () => Math.max(0, textWidth))
176
+ };
177
+ };
178
+ /**
179
+ * Rasterizes a signature into a PNG data URL in the browser, for a document or
180
+ * service that needs a bitmap. A typed signature renders with the fonts
181
+ * installed on this device: an SVG image cannot use the page's web fonts.
182
+ */
183
+ export const signatureToPng = async (value, options = {}) => {
184
+ const viewBox = /viewBox="-?[\d.]+ -?[\d.]+ ([\d.]+) ([\d.]+)"/.exec(value.svg);
185
+ if (!viewBox) throw new Error("The signature SVG has no drawing size.");
186
+ const scale = options.scale ?? 2;
187
+ const width = Math.max(1, Math.round(Number(viewBox[1]) * scale));
188
+ const height = Math.max(1, Math.round(Number(viewBox[2]) * scale));
189
+ const svg = value.svg.replace("<svg ", `<svg color="${escapeXml(options.color ?? "#000000")}" `);
190
+ const image = new Image();
191
+ image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
192
+ await image.decode();
193
+ const canvas = document.createElement("canvas");
194
+ canvas.width = width;
195
+ canvas.height = height;
196
+ const context = canvas.getContext("2d");
197
+ if (!context) throw new Error("This browser cannot draw a signature image.");
198
+ if (options.background) {
199
+ context.fillStyle = options.background;
200
+ context.fillRect(0, 0, width, height);
201
+ }
202
+ context.drawImage(image, 0, 0, width, height);
203
+ const url = canvas.toDataURL("image/png");
204
+ // A canvas beyond the browser's size limit returns "data:," instead of failing.
205
+ if (!url.startsWith("data:image/png")) throw new Error("The signature is too large for a PNG at this scale.");
206
+ return url;
207
+ };
208
+ //# sourceMappingURL=signature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["inputs/signature.ts"],"sourcesContent":["/**\n * A signature as the field reports it. `kind` records how it was given, so the\n * application can keep that fact next to the image. `svg` is a standalone SVG\n * document whose ink uses `currentColor`, so it renders black as an image. A\n * value that arrives from a browser is untrusted input: read it with\n * `parseSignature` before storing or embedding it.\n */\nexport type SignatureValue = { kind: \"drawn\"; svg: string } | { kind: \"typed\"; name: string; svg: string };\n\n/** One sampled pointer position in drawing units, with a pressure from 0 to 1. */\nexport type SignaturePoint = { x: number; y: number; pressure: number };\n\n/** The drawing space as an SVG viewBox and one closed outline path per stroke. */\nexport type SignatureDrawing = { x: number; y: number; width: number; height: number; strokes: readonly string[] };\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\n/** Typed names are laid out at this size; the SVG scales as a whole. */\nconst TYPED_FONT_SIZE = 48;\nconst TYPED_PADDING = 12;\n\nconst round = (value: number) => Math.round(value * 10) / 10;\nconst point = (x: number, y: number) => `${round(x)} ${round(y)}`;\n\n/** Control characters, lone surrogates, and the two noncharacters XML forbids, which a strict XML parser rejects. */\nconst NOT_XML_TEXT = /[\\p{Cc}\\p{Cs}\\uFFFE\\uFFFF]/gu;\n\n/** Escapes text for XML content or attributes and drops characters XML cannot hold, so the document stays well-formed. */\nexport const escapeXml = (text: string): string =>\n text\n .replace(NOT_XML_TEXT, \"\")\n .replace(/[&<>\"']/g, (char) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\", \"'\": \"&apos;\" })[char] ?? char);\n\nconst unescapeXml = (text: string): string =>\n text.replace(/&(amp|lt|gt|quot|apos);/g, (_, name: string) => ({ amp: \"&\", lt: \"<\", gt: \">\", quot: '\"', apos: \"'\" })[name] ?? \"\");\n\n/** The name as a typed signature stores it: without characters an SVG cannot hold and without surrounding space. */\nexport const signatureName = (name: string): string => name.replace(NOT_XML_TEXT, \"\").trim();\n\n/** Stroke width in drawing units at a pressure: light pressure still leaves a readable line. */\nexport const strokeWidth = (size: number, pressure: number): number => size * (0.35 + 0.65 * Math.min(1, Math.max(0, pressure)));\n\n/**\n * Turns sampled points into one filled outline: both edges follow the points at\n * half the local stroke width and are smoothed through their midpoints, and\n * round caps close the ends. A filled outline, unlike a stroked path, can change\n * width along the line, so pen pressure and drawing speed show in the ink.\n */\nexport const strokeOutline = (points: readonly SignaturePoint[], size: number): string => {\n const first = points[0];\n if (!first) return \"\";\n if (points.length === 1) {\n const radius = round(strokeWidth(size, first.pressure) / 2);\n const arc = (x: number) => `A${radius} ${radius} 0 1 0 ${point(x, first.y)}`;\n return `M${point(first.x - radius, first.y)}${arc(first.x + radius)}${arc(first.x - radius)}Z`;\n }\n type Edge = { x: number; y: number; radius: number };\n const left: Edge[] = [];\n const right: Edge[] = [];\n points.forEach((current, index) => {\n const before = points[index - 1] ?? current;\n const after = points[index + 1] ?? current;\n const dx = after.x - before.x;\n const dy = after.y - before.y;\n const length = Math.hypot(dx, dy) || 1;\n const radius = strokeWidth(size, current.pressure) / 2;\n const nx = (-dy / length) * radius;\n const ny = (dx / length) * radius;\n left.push({ x: current.x + nx, y: current.y + ny, radius });\n right.push({ x: current.x - nx, y: current.y - ny, radius });\n });\n right.reverse();\n // Quadratic segments through the midpoints of neighbouring edge points keep the outline smooth without overshoot.\n const edge = (side: readonly Edge[]) =>\n side\n .map((current, index) => {\n if (index === 0) return \"\";\n const next = side[index + 1];\n if (!next) return `L${point(current.x, current.y)}`;\n return `Q${point(current.x, current.y)} ${point((current.x + next.x) / 2, (current.y + next.y) / 2)}`;\n })\n .join(\"\");\n const cap = (from: Edge, to: Edge) => `A${round(from.radius)} ${round(from.radius)} 0 0 0 ${point(to.x, to.y)}`;\n const start = left[0] ?? right[0];\n const end = left[left.length - 1];\n const back = right[0];\n const backEnd = right[right.length - 1];\n if (!start || !end || !back || !backEnd) return \"\";\n return `M${point(start.x, start.y)}${edge(left)}${cap(end, back)}${edge(right)}${cap(backEnd, start)}Z`;\n};\n\n/** Serializes a drawing as a standalone SVG document; an empty drawing has no value. */\nexport const drawingToSvg = (drawing: SignatureDrawing): string | null => {\n if (drawing.strokes.length === 0) return null;\n const width = round(drawing.width);\n const height = round(drawing.height);\n const paths = drawing.strokes.map((stroke) => `<path d=\"${escapeXml(stroke)}\"/>`).join(\"\");\n return `<svg xmlns=\"${SVG_NS}\" viewBox=\"${round(drawing.x)} ${round(drawing.y)} ${width} ${height}\" width=\"${width}\" height=\"${height}\"><g fill=\"currentColor\">${paths}</g></svg>`;\n};\n\nconst NUMBER = String.raw`-?\\d+(?:\\.\\d+)?`;\nconst SVG_NS_PATTERN = String.raw`http://www\\.w3\\.org/2000/svg`;\n// Exactly the markup drawingToSvg and typedToSvg write. Path data may only hold the commands and numbers strokeOutline\n// emits, and no attribute or text may hold markup, so a match cannot carry scripts, links, or other elements.\nconst DRAWN_SVG = new RegExp(\n `^<svg xmlns=\"${SVG_NS_PATTERN}\" viewBox=\"(${NUMBER}) (${NUMBER}) (${NUMBER}) (${NUMBER})\" width=\"${NUMBER}\" height=\"${NUMBER}\">` +\n `<g fill=\"currentColor\">((?:<path d=\"[MLQAZ\\\\d .-]+\"/>)+)</g></svg>$`,\n);\nconst TYPED_SVG = new RegExp(\n `^<svg xmlns=\"${SVG_NS_PATTERN}\" viewBox=\"0 0 (\\\\d+) \\\\d+\" width=\"\\\\d+\" height=\"\\\\d+\">` +\n `<text x=\"\\\\d+\" y=\"\\\\d+\" font-family=\"([^\"<>]*)\" font-size=\"\\\\d+\" fill=\"currentColor\">[^<>]*</text></svg>$`,\n);\n\n/** Reads the drawing space and strokes back from an SVG drawingToSvg wrote, so a stored signature can be extended or undone. */\nexport const svgToDrawing = (svg: string): SignatureDrawing | null => {\n const match = DRAWN_SVG.exec(svg);\n if (!match) return null;\n const [x = 0, y = 0, width = 0, height = 0] = match.slice(1, 5).map(Number);\n if (width <= 0 || height <= 0) return null;\n const strokes = [...(match[5] ?? \"\").matchAll(/<path d=\"([^\"]+)\"\\/>/g)].flatMap((path) => (path[1] ? [path[1]] : []));\n return { x, y, width, height, strokes };\n};\n\n/**\n * Lays out a typed name as an SVG document in the given font family. `measure`\n * returns the text width at `TYPED_FONT_SIZE`; without one, the width is estimated.\n */\nexport const typedToSvg = (name: string, fontFamily: string, measure?: (text: string, font: string) => number | undefined): string => {\n const font = `${TYPED_FONT_SIZE}px ${fontFamily}`;\n const textWidth = measure?.(name, font) ?? name.length * TYPED_FONT_SIZE * 0.5;\n const width = Math.ceil(textWidth + TYPED_PADDING * 2);\n const height = Math.ceil(TYPED_FONT_SIZE * 1.6);\n const baseline = Math.round(TYPED_FONT_SIZE * 1.15);\n return (\n `<svg xmlns=\"${SVG_NS}\" viewBox=\"0 0 ${width} ${height}\" width=\"${width}\" height=\"${height}\">` +\n `<text x=\"${TYPED_PADDING}\" y=\"${baseline}\" font-family=\"${escapeXml(fontFamily)}\" font-size=\"${TYPED_FONT_SIZE}\" fill=\"currentColor\">${escapeXml(name)}</text></svg>`\n );\n};\n\n/**\n * Reads a submitted signature on the server: the JSON string of the field's\n * hidden input, or a value the browser sent as JSON. It accepts only markup\n * the field writes and returns it rebuilt, so the result is safe to store and\n * to embed. An empty or invalid value returns `null`.\n */\nexport const parseSignature = (input: unknown): SignatureValue | null => {\n let value: unknown = input;\n if (typeof input === \"string\") {\n if (!input) return null;\n try {\n value = JSON.parse(input);\n } catch {\n return null;\n }\n }\n if (typeof value !== \"object\" || value === null || !(\"kind\" in value) || !(\"svg\" in value) || typeof value.svg !== \"string\") return null;\n if (value.kind === \"drawn\") {\n const drawing = svgToDrawing(value.svg);\n const svg = drawing && drawingToSvg(drawing);\n return svg ? { kind: \"drawn\", svg } : null;\n }\n if (value.kind !== \"typed\" || !(\"name\" in value) || typeof value.name !== \"string\") return null;\n const name = signatureName(value.name);\n const match = TYPED_SVG.exec(value.svg);\n if (!name || !match) return null;\n const textWidth = Number(match[1]) - TYPED_PADDING * 2;\n return { kind: \"typed\", name, svg: typedToSvg(name, unescapeXml(match[2] ?? \"\"), () => Math.max(0, textWidth)) };\n};\n\nexport type SignaturePngOptions = {\n /** Device pixels per SVG unit. Defaults to `2`. */\n scale?: number;\n /** Ink colour. Defaults to `\"#000000\"`. */\n color?: string;\n /** Background fill. Defaults to transparent. */\n background?: string;\n};\n\n/**\n * Rasterizes a signature into a PNG data URL in the browser, for a document or\n * service that needs a bitmap. A typed signature renders with the fonts\n * installed on this device: an SVG image cannot use the page's web fonts.\n */\nexport const signatureToPng = async (value: SignatureValue, options: SignaturePngOptions = {}): Promise<string> => {\n const viewBox = /viewBox=\"-?[\\d.]+ -?[\\d.]+ ([\\d.]+) ([\\d.]+)\"/.exec(value.svg);\n if (!viewBox) throw new Error(\"The signature SVG has no drawing size.\");\n const scale = options.scale ?? 2;\n const width = Math.max(1, Math.round(Number(viewBox[1]) * scale));\n const height = Math.max(1, Math.round(Number(viewBox[2]) * scale));\n const svg = value.svg.replace(\"<svg \", `<svg color=\"${escapeXml(options.color ?? \"#000000\")}\" `);\n const image = new Image();\n image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;\n await image.decode();\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const context = canvas.getContext(\"2d\");\n if (!context) throw new Error(\"This browser cannot draw a signature image.\");\n if (options.background) {\n context.fillStyle = options.background;\n context.fillRect(0, 0, width, height);\n }\n context.drawImage(image, 0, 0, width, height);\n const url = canvas.toDataURL(\"image/png\");\n // A canvas beyond the browser's size limit returns \"data:,\" instead of failing.\n if (!url.startsWith(\"data:image/png\")) throw new Error(\"The signature is too large for a PNG at this scale.\");\n return url;\n};\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;;AAGA;;AAGA,MAAM,MAAM,GAAG,4BAA4B;AAC3C;AACA,MAAM,eAAe,GAAG,EAAE;AAC1B,MAAM,aAAa,GAAG,EAAE;AAExB,MAAM,KAAK,GAAI,KAAa,IAAK,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,GAAG,EAAE;AAC5D,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,CAAS,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE;;AAEjE;AACA,MAAM,YAAY,GAAG,8BAA8B;;AAEnD;AACA,OAAO,MAAM,SAAS,GAAI,IAAY,IACpC,IAAI,CACD,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CACzB,OAAO,CAAC,UAAU,EAAG,IAAI,IAAK,CAAC;EAAE,GAAG,EAAE,OAAO;EAAE,GAAG,EAAE,MAAM;EAAE,GAAG,EAAE,MAAM;EAAE,GAAG,EAAE,QAAQ;EAAE,GAAG,EAAE;AAAS,CAAC,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC;AAE5H,MAAM,WAAW,GAAI,IAAY,IAC/B,IAAI,CAAC,OAAO,CAAC,0BAA0B,EAAE,CAAC,CAAC,EAAE,IAAY,KAAK,CAAC;EAAE,GAAG,EAAE,GAAG;EAAE,EAAE,EAAE,GAAG;EAAE,EAAE,EAAE,GAAG;EAAE,IAAI,EAAE,GAAG;EAAE,IAAI,EAAE;AAAI,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;;AAEnI;AACA,OAAO,MAAM,aAAa,GAAI,IAAY,IAAa,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;;AAE5F;AACA,OAAO,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,QAAgB,KAAa,IAAI,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;;AAEhI;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAM,aAAa,GAAG,CAAC,MAAiC,EAAE,IAAY,KAAa;EACxF,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC;EACvB,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE;EACrB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;IACvB,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC3D,MAAM,GAAG,GAAI,CAAS,IAAK,IAAI,MAAM,IAAI,MAAM,UAAU,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE;IAC5E,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG;EAChG;EAEA,MAAM,IAAY,GAAG,EAAE;EACvB,MAAM,KAAa,GAAG,EAAE;EACxB,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,KAAK;IACjC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,OAAO;IAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,OAAO;IAC1C,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;IAC7B,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;IAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC;IACtC,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;IACtD,MAAM,EAAE,GAAI,CAAC,EAAE,GAAG,MAAM,GAAI,MAAM;IAClC,MAAM,EAAE,GAAI,EAAE,GAAG,MAAM,GAAI,MAAM;IACjC,IAAI,CAAC,IAAI,CAAC;MAAE,CAAC,EAAE,OAAO,CAAC,CAAC,GAAG,EAAE;MAAE,CAAC,EAAE,OAAO,CAAC,CAAC,GAAG,EAAE;MAAE;IAAO,CAAC,CAAC;IAC3D,KAAK,CAAC,IAAI,CAAC;MAAE,CAAC,EAAE,OAAO,CAAC,CAAC,GAAG,EAAE;MAAE,CAAC,EAAE,OAAO,CAAC,CAAC,GAAG,EAAE;MAAE;IAAO,CAAC,CAAC;EAC9D,CAAC,CAAC;EACF,KAAK,CAAC,OAAO,CAAC,CAAC;EACf;EACA,MAAM,IAAI,GAAI,IAAqB,IACjC,IAAI,CACD,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,KAAK;IACvB,IAAI,KAAK,KAAK,CAAC,EAAE,OAAO,EAAE;IAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;IAC5B,IAAI,CAAC,IAAI,EAAE,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE;IACnD,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE;EACvG,CAAC,CAAC,CACD,IAAI,CAAC,EAAE,CAAC;EACb,MAAM,GAAG,GAAG,CAAC,IAAU,EAAE,EAAQ,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE;EAC/G,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;EACjC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;EACjC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC;EACrB,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;EACvC,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE;EAClD,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG;AACzG,CAAC;;AAED;AACA,OAAO,MAAM,YAAY,GAAI,OAAyB,IAAoB;EACxE,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,OAAO,IAAI;EAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;EAClC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;EACpC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAE,MAAM,IAAK,YAAY,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;EAC1F,OAAO,eAAe,MAAM,cAAc,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,MAAM,YAAY,KAAK,aAAa,MAAM,4BAA4B,KAAK,YAAY;AACpL,CAAC;AAED,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,iBAAiB;AAC1C,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,8BAA8B;AAC/D;AACA;AACA,MAAM,SAAS,GAAG,IAAI,MAAM,CAC1B,gBAAgB,cAAc,eAAe,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,aAAa,MAAM,aAAa,MAAM,IAAI,GAC/H,qEACJ,CAAC;AACD,MAAM,SAAS,GAAG,IAAI,MAAM,CAC1B,gBAAgB,cAAc,yDAAyD,GACrF,2GACJ,CAAC;;AAED;AACA,OAAO,MAAM,YAAY,GAAI,GAAW,IAA8B;EACpE,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;EACjC,IAAI,CAAC,KAAK,EAAE,OAAO,IAAI;EACvB,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;EAC3E,IAAI,KAAK,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,OAAO,IAAI;EAC1C,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC,CAAC,OAAO,CAAE,IAAI,IAAM,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,EAAG,CAAC;EACrH,OAAO;IAAE,CAAC;IAAE,CAAC;IAAE,KAAK;IAAE,MAAM;IAAE;EAAQ,CAAC;AACzC,CAAC;;AAED;AACA;AACA;AACA;AACA,OAAO,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,UAAkB,EAAE,OAA4D,KAAa;EACpI,MAAM,IAAI,GAAG,GAAG,eAAe,MAAM,UAAU,EAAE;EACjD,MAAM,SAAS,GAAG,OAAO,GAAG,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,eAAe,GAAG,GAAG;EAC9E,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,aAAa,GAAG,CAAC,CAAC;EACtD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,GAAG,GAAG,CAAC;EAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC;EACnD,OACE,eAAe,MAAM,kBAAkB,KAAK,IAAI,MAAM,YAAY,KAAK,aAAa,MAAM,IAAI,GAC9F,YAAY,aAAa,QAAQ,QAAQ,kBAAkB,SAAS,CAAC,UAAU,CAAC,gBAAgB,eAAe,yBAAyB,SAAS,CAAC,IAAI,CAAC,eAAe;AAE1K,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAM,cAAc,GAAI,KAAc,IAA4B;EACvE,IAAI,KAAc,GAAG,KAAK;EAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;IAC7B,IAAI,CAAC,KAAK,EAAE,OAAO,IAAI;IACvB,IAAI;MACF,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;IAC3B,CAAC,CAAC,MAAM;MACN,OAAO,IAAI;IACb;EACF;EACA,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,EAAE,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,IAAI,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ,EAAE,OAAO,IAAI;EACxI,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE;IAC1B,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC;IACvC,MAAM,GAAG,GAAG,OAAO,IAAI,YAAY,CAAC,OAAO,CAAC;IAC5C,OAAO,GAAG,GAAG;MAAE,IAAI,EAAE,OAAO;MAAE;IAAI,CAAC,GAAG,IAAI;EAC5C;EACA,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,EAAE,MAAM,IAAI,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,OAAO,IAAI;EAC/F,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC;EACtC,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;EACvC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,IAAI;EAChC,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,aAAa,GAAG,CAAC;EACtD,OAAO;IAAE,IAAI,EAAE,OAAO;IAAE,IAAI;IAAE,GAAG,EAAE,UAAU,CAAC,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC;EAAE,CAAC;AAClH,CAAC;AAWD;AACA;AACA;AACA;AACA;AACA,OAAO,MAAM,cAAc,GAAG,OAAO,KAAqB,EAAE,OAA4B,GAAG,CAAC,CAAC,KAAsB;EACjH,MAAM,OAAO,GAAG,+CAA+C,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;EAC/E,IAAI,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC;EACvE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC;EAChC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;EACjE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;EAClE,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,eAAe,SAAS,CAAC,OAAO,CAAC,KAAK,IAAI,SAAS,CAAC,IAAI,CAAC;EAChG,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,CAAC;EACzB,KAAK,CAAC,GAAG,GAAG,oCAAoC,kBAAkB,CAAC,GAAG,CAAC,EAAE;EACzE,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;EACpB,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC;EAC/C,MAAM,CAAC,KAAK,GAAG,KAAK;EACpB,MAAM,CAAC,MAAM,GAAG,MAAM;EACtB,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;EACvC,IAAI,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC;EAC5E,IAAI,OAAO,CAAC,UAAU,EAAE;IACtB,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,UAAU;IACtC,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC;EACvC;EACA,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC;EAC7C,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC;EACzC;EACA,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC;EAC7G,OAAO,GAAG;AACZ,CAAC","ignoreList":[]}
@@ -0,0 +1,49 @@
1
+ import { memo as _$memo } from "solid-js/web";
2
+ import { createComponent as _$createComponent } from "solid-js/web";
3
+ import { ErrorBoundary, untrack } from "solid-js";
4
+ import { Button } from "../actions/Button";
5
+ import { useUiMessages } from "../intl/messages";
6
+ import Placeholder from "../surfaces/Placeholder";
7
+
8
+ /**
9
+ * Guards a Solid root that @k2b/ui creates itself, such as a dialog or a floating window. These roots are mostly
10
+ * opened from a click handler, outside any owner and any island boundary. Without a boundary of their own, content
11
+ * that throws on an update throws into the code that wrote the signal and leaves other parts of the page stale.
12
+ * The content is created untracked, as `render()` and components create it, so a signal it reads directly does not
13
+ * rebuild it. `onClose` adds a Close button for a root whose content draws its own close control.
14
+ */
15
+ export function RenderErrorBoundary(props) {
16
+ return _$createComponent(ErrorBoundary, {
17
+ fallback: error => {
18
+ globalThis.reportError(error);
19
+ props.onError?.();
20
+ return _$createComponent(RenderErrorNotice, {
21
+ get onClose() {
22
+ return props.onClose;
23
+ }
24
+ });
25
+ },
26
+ get children() {
27
+ return untrack(props.content);
28
+ }
29
+ });
30
+ }
31
+ function RenderErrorNotice(props) {
32
+ const messages = useUiMessages();
33
+ return _$createComponent(Placeholder, {
34
+ state: "error",
35
+ get title() {
36
+ return messages().couldNotDisplayContent;
37
+ },
38
+ get action() {
39
+ return _$memo(() => !!props.onClose)() ? _$createComponent(Button, {
40
+ variant: "secondary",
41
+ onClick: () => props.onClose?.(),
42
+ get children() {
43
+ return messages().close;
44
+ }
45
+ }) : undefined;
46
+ }
47
+ });
48
+ }
49
+ //# sourceMappingURL=render-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["internal/render-error.tsx"],"sourcesContent":["import { ErrorBoundary, type JSX, untrack } from \"solid-js\";\nimport { Button } from \"../actions/Button\";\nimport { useUiMessages } from \"../intl/messages\";\nimport Placeholder from \"../surfaces/Placeholder\";\n\n/**\n * Guards a Solid root that @k2b/ui creates itself, such as a dialog or a floating window. These roots are mostly\n * opened from a click handler, outside any owner and any island boundary. Without a boundary of their own, content\n * that throws on an update throws into the code that wrote the signal and leaves other parts of the page stale.\n * The content is created untracked, as `render()` and components create it, so a signal it reads directly does not\n * rebuild it. `onClose` adds a Close button for a root whose content draws its own close control.\n */\nexport function RenderErrorBoundary(props: { content: () => JSX.Element; onClose?: () => void; onError?: () => void }): JSX.Element {\n return (\n <ErrorBoundary\n fallback={(error: unknown) => {\n globalThis.reportError(error);\n props.onError?.();\n return <RenderErrorNotice onClose={props.onClose} />;\n }}\n >\n {untrack(props.content)}\n </ErrorBoundary>\n );\n}\n\nfunction RenderErrorNotice(props: { onClose?: () => void }): JSX.Element {\n const messages = useUiMessages();\n return (\n <Placeholder\n state=\"error\"\n title={messages().couldNotDisplayContent}\n action={\n props.onClose ? (\n <Button variant=\"secondary\" onClick={() => props.onClose?.()}>\n {messages().close}\n </Button>\n ) : undefined\n }\n />\n );\n}\n"],"mappings":";;AAAA,SAAS,aAAa,EAAY,OAAO,QAAQ,UAAU;AAC3D,SAAS,MAAM,QAAQ,mBAAmB;AAC1C,SAAS,aAAa,QAAQ,kBAAkB;AAChD,OAAO,WAAW,MAAM,yBAAyB;;AAEjD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAAS,mBAAmB,CAAC,KAAiF,EAAe;EAClI,yBACG,aAAa;IACZ,QAAQ,EAAG,KAAc,IAAK;MAC5B,UAAU,CAAC,WAAW,CAAC,KAAK,CAAC;MAC7B,KAAK,CAAC,OAAO,GAAG,CAAC;MACjB,yBAAQ,iBAAiB;QAAA,IAAC,OAAO;UAAA,OAAE,KAAK,CAAC,OAAO;QAAA;MAAA;IAClD,CAAC;IAAA;MAAA,OAEA,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;IAAA;EAAA;AAG7B;AAEA,SAAS,iBAAiB,CAAC,KAA+B,EAAe;EACvE,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC;EAChC,yBACG,WAAW;IACV,KAAK;IAAA,IACL,KAAK;MAAA,OAAE,QAAQ,CAAC,CAAC,CAAC,sBAAsB;IAAA;IAAA,IACxC,MAAM;MAAA,OACJ,oBAAK,CAAC,OAAO,wBACV,MAAM;QAAC,OAAO;QAAa,OAAO,EAAE,MAAM,KAAK,CAAC,OAAO,GAAG,CAAC;QAAA;UAAA,OACzD,QAAQ,CAAC,CAAC,CAAC,KAAK;QAAA;MAAA,KAEjB,SAAS;IAAA;EAAA;AAIrB","ignoreList":[]}