@0001-labs/design-system-1 0.3.0-alpha.13 → 0.3.0-alpha.15

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 (722) hide show
  1. package/DS1/0-face/calendar.ts +194 -0
  2. package/DS1/0-face/carousel.ts +623 -0
  3. package/DS1/0-face/component-visibility.ts +14 -0
  4. package/DS1/0-face/copy-text.ts +96 -0
  5. package/DS1/0-face/device.ts +8 -58
  6. package/DS1/0-face/dialog-swipe.ts +249 -0
  7. package/DS1/0-face/dialogs.ts +208 -0
  8. package/DS1/0-face/drawer.ts +258 -0
  9. package/DS1/0-face/feedback-words.ts +36 -0
  10. package/DS1/0-face/feedback.ts +40 -0
  11. package/DS1/0-face/format.ts +700 -0
  12. package/DS1/0-face/generated/toast-assets.ts +18 -0
  13. package/DS1/0-face/grid-fit.ts +1 -1
  14. package/DS1/0-face/grid.ts +3 -2
  15. package/DS1/0-face/header.ts +78 -0
  16. package/DS1/0-face/i18n.ts +93 -18
  17. package/DS1/0-face/keys.ts +276 -0
  18. package/DS1/0-face/languages.ts +4 -4
  19. package/DS1/0-face/layout-inspector.ts +6 -6
  20. package/DS1/0-face/library-styles.ts +33 -0
  21. package/DS1/0-face/load-toaster.ts +7 -0
  22. package/DS1/0-face/preference-options.ts +29 -0
  23. package/DS1/0-face/preferences.ts +3 -11
  24. package/DS1/0-face/pricing.ts +98 -43
  25. package/DS1/0-face/scaling.ts +160 -4
  26. package/DS1/0-face/shared-words.ts +9 -4
  27. package/DS1/0-face/sidebar.ts +201 -0
  28. package/DS1/0-face/theme-bootstrap.js +184 -0
  29. package/DS1/0-face/theme.ts +89 -47
  30. package/DS1/0-face/toast-queue.ts +157 -0
  31. package/DS1/0-face/toaster.ts +384 -0
  32. package/DS1/0-face/tooltips.ts +157 -15
  33. package/DS1/0-face/translations/da.json +60 -1
  34. package/DS1/0-face/translations/de.json +60 -1
  35. package/DS1/0-face/translations/en.json +60 -1
  36. package/DS1/0-face/translations/es.json +60 -1
  37. package/DS1/0-face/translations/fi.json +60 -1
  38. package/DS1/0-face/translations/fr.json +60 -1
  39. package/DS1/0-face/translations/is.json +60 -1
  40. package/DS1/0-face/translations/it.json +60 -1
  41. package/DS1/0-face/translations/ja.json +60 -1
  42. package/DS1/0-face/translations/ko.json +60 -1
  43. package/DS1/0-face/translations/nb.json +60 -1
  44. package/DS1/0-face/translations/pt-BR.json +60 -1
  45. package/DS1/0-face/translations/pt.json +60 -1
  46. package/DS1/0-face/translations/sv.json +60 -1
  47. package/DS1/0-face/translations/zh.json +60 -1
  48. package/DS1/0-face/translations-format.ts +3 -4
  49. package/DS1/1-root/card.css +192 -0
  50. package/DS1/1-root/carousel.css +136 -0
  51. package/DS1/1-root/cjk.css +23 -0
  52. package/DS1/1-root/component-metrics.css +1 -0
  53. package/DS1/1-root/component-tokens.css +51 -8
  54. package/DS1/1-root/controls.css +891 -14
  55. package/DS1/1-root/dialog-placement.css +191 -0
  56. package/DS1/1-root/document.css +183 -0
  57. package/DS1/1-root/drawer.css +83 -0
  58. package/DS1/1-root/empty.css +83 -0
  59. package/DS1/1-root/figures.css +141 -0
  60. package/DS1/1-root/fonts/Iosevka-LICENSE.md +3 -1
  61. package/DS1/1-root/fonts/Iosevka-Regular.woff2 +0 -0
  62. package/DS1/1-root/fonts.css +69 -0
  63. package/DS1/1-root/footer.css +186 -0
  64. package/DS1/1-root/header.css +165 -0
  65. package/DS1/1-root/library-tokens.ts +91 -0
  66. package/DS1/1-root/measures.ts +53 -0
  67. package/DS1/1-root/notice.css +165 -0
  68. package/DS1/1-root/one.css +14 -84
  69. package/DS1/1-root/one.semantic.css +200 -148
  70. package/DS1/1-root/palette.css +58 -0
  71. package/DS1/1-root/palette.ts +19 -0
  72. package/DS1/1-root/progress.css +356 -0
  73. package/DS1/1-root/prose.css +202 -0
  74. package/DS1/1-root/row.css +300 -0
  75. package/DS1/1-root/table.css +111 -0
  76. package/DS1/1-root/text.css +70 -0
  77. package/DS1/2-core/button-split.ts +55 -0
  78. package/DS1/2-core/chart/chart.css +169 -0
  79. package/DS1/2-core/chart/grid-chart.ts +554 -0
  80. package/DS1/2-core/ds-avatar.ts +25 -6
  81. package/DS1/2-core/ds-badge.ts +10 -10
  82. package/DS1/2-core/ds-button.ts +111 -26
  83. package/DS1/2-core/ds-card.ts +135 -105
  84. package/DS1/2-core/ds-chart.ts +327 -0
  85. package/DS1/2-core/ds-composer.ts +8 -2
  86. package/DS1/2-core/ds-cycle.ts +97 -21
  87. package/DS1/2-core/ds-empty.ts +120 -0
  88. package/DS1/2-core/ds-icon.ts +11 -2
  89. package/DS1/2-core/ds-input.ts +165 -6
  90. package/DS1/2-core/ds-message.ts +218 -0
  91. package/DS1/2-core/ds-notice.ts +10 -0
  92. package/DS1/2-core/ds-progress.ts +380 -0
  93. package/DS1/2-core/ds-stat.ts +174 -0
  94. package/DS1/2-core/ds-switch.ts +1 -0
  95. package/DS1/2-core/ds-text.ts +15 -2
  96. package/DS1/2-core/ds-toast.ts +354 -0
  97. package/DS1/2-core/figures.ts +105 -0
  98. package/DS1/2-core/generated/ds-icon-map.ts +238 -128
  99. package/DS1/2-core/generated/icon-avatar.ts +2 -0
  100. package/DS1/2-core/generated/icons-extra.ts +120 -65
  101. package/DS1/2-core/generated/icons-immediate.ts +1 -1
  102. package/DS1/2-core/styles/ds-avatar.css +9 -8
  103. package/DS1/2-core/styles/ds-button-host.css +26 -3
  104. package/DS1/2-core/styles/ds-button-split.css +67 -0
  105. package/DS1/2-core/styles/ds-button.css +49 -15
  106. package/DS1/2-core/styles/ds-card.css +34 -0
  107. package/DS1/2-core/styles/ds-composer.css +13 -0
  108. package/DS1/2-core/styles/ds-cycle.css +10 -0
  109. package/DS1/2-core/styles/ds-empty.css +18 -0
  110. package/DS1/2-core/styles/ds-gap.css +1 -0
  111. package/DS1/2-core/styles/ds-icon.css +1 -0
  112. package/DS1/2-core/styles/ds-input.css +59 -6
  113. package/DS1/2-core/styles/ds-notice.css +32 -0
  114. package/DS1/2-core/styles/ds-progress.css +25 -0
  115. package/DS1/2-core/styles/ds-select.css +2 -2
  116. package/DS1/2-core/styles/ds-split.css +2 -62
  117. package/DS1/2-core/styles/ds-stat.css +34 -0
  118. package/DS1/2-core/styles/ds-switch.css +16 -1
  119. package/DS1/2-core/styles/ds-text.css +43 -12
  120. package/DS1/2-core/styles/ds-toast.css +182 -0
  121. package/DS1/2-core/styles/ds-tooltip.css +1 -0
  122. package/DS1/3-unit/calendar-base.ts +263 -0
  123. package/DS1/3-unit/ds-accordion.ts +9 -1
  124. package/DS1/3-unit/ds-avatar-group.ts +1 -0
  125. package/DS1/3-unit/ds-calendar-large.ts +349 -0
  126. package/DS1/3-unit/ds-calendar.ts +344 -0
  127. package/DS1/3-unit/ds-carousel.ts +368 -0
  128. package/DS1/3-unit/ds-date-picker.ts +540 -0
  129. package/DS1/3-unit/ds-form.ts +45 -37
  130. package/DS1/3-unit/ds-list.ts +48 -1
  131. package/DS1/3-unit/ds-matrix.ts +318 -0
  132. package/DS1/3-unit/ds-menu.ts +8 -4
  133. package/DS1/3-unit/ds-row.ts +84 -1
  134. package/DS1/3-unit/ds-sidebar.ts +366 -0
  135. package/DS1/3-unit/ds-table.ts +75 -34
  136. package/DS1/3-unit/styles/ds-accordion.css +1 -0
  137. package/DS1/3-unit/styles/ds-avatar-group.css +2 -4
  138. package/DS1/3-unit/styles/ds-calendar-large.css +165 -0
  139. package/DS1/3-unit/styles/ds-calendar.css +238 -0
  140. package/DS1/3-unit/styles/ds-carousel.css +30 -0
  141. package/DS1/3-unit/styles/ds-date-picker.css +124 -0
  142. package/DS1/3-unit/styles/ds-list.css +1 -0
  143. package/DS1/3-unit/styles/ds-matrix.css +203 -0
  144. package/DS1/3-unit/styles/ds-menu.css +31 -6
  145. package/DS1/3-unit/styles/ds-row.css +84 -2
  146. package/DS1/3-unit/styles/ds-sidebar.css +134 -0
  147. package/DS1/3-unit/styles/ds-table.css +3 -74
  148. package/DS1/4-page/ds-dialog.ts +40 -4
  149. package/DS1/4-page/ds-document.ts +240 -0
  150. package/DS1/4-page/ds-footer.ts +165 -0
  151. package/DS1/4-page/ds-grid.ts +39 -59
  152. package/DS1/4-page/ds-header.ts +171 -0
  153. package/DS1/4-page/ds-page.ts +184 -0
  154. package/DS1/4-page/email.ts +2 -3
  155. package/DS1/4-page/styles/ds-container.css +1 -0
  156. package/DS1/4-page/styles/ds-document.css +18 -0
  157. package/DS1/4-page/styles/ds-footer.css +29 -0
  158. package/DS1/4-page/styles/ds-grid.css +31 -85
  159. package/DS1/4-page/styles/ds-header.css +57 -0
  160. package/DS1/4-page/styles/ds-page.css +214 -0
  161. package/DS1/components.ts +7 -1
  162. package/DS1/index.ts +24 -6
  163. package/DS1/react/avatar.ts +4 -5
  164. package/DS1/react/button-split.ts +29 -0
  165. package/DS1/react/button.ts +32 -13
  166. package/DS1/react/carousel.ts +76 -0
  167. package/DS1/react/dialog.ts +36 -5
  168. package/DS1/react/document.ts +14 -0
  169. package/DS1/react/empty.ts +32 -0
  170. package/DS1/react/footer.ts +17 -0
  171. package/DS1/react/header.ts +15 -0
  172. package/DS1/react/input.ts +28 -17
  173. package/DS1/react/message.ts +73 -0
  174. package/DS1/react/notice.ts +59 -0
  175. package/DS1/react/page.ts +13 -0
  176. package/DS1/react/progress.ts +99 -0
  177. package/DS1/react/sidebar.ts +71 -0
  178. package/DS1/react/stat.ts +73 -0
  179. package/DS1/react/toast.ts +82 -0
  180. package/DS1/x-icon/Tabler-LICENSE.md +27 -0
  181. package/DS1/x-icon/airtable.svg +5 -0
  182. package/DS1/x-icon/attach.svg +3 -0
  183. package/DS1/x-icon/audio.svg +3 -0
  184. package/DS1/x-icon/bag-alt.svg +3 -0
  185. package/DS1/x-icon/bank.svg +3 -0
  186. package/DS1/x-icon/banknote.svg +3 -0
  187. package/DS1/x-icon/bolt.svg +3 -0
  188. package/DS1/x-icon/book-alt.svg +3 -0
  189. package/DS1/x-icon/book.svg +4 -0
  190. package/DS1/x-icon/box.svg +3 -0
  191. package/DS1/x-icon/briefcase.svg +3 -0
  192. package/DS1/x-icon/building.svg +4 -0
  193. package/DS1/x-icon/car.svg +3 -0
  194. package/DS1/x-icon/card.svg +3 -0
  195. package/DS1/x-icon/cart.svg +3 -0
  196. package/DS1/x-icon/{ezo-android-screen-stats.svg → chevron-down.svg} +1 -1
  197. package/DS1/x-icon/{ezo-item-button-start.svg → chevron-left.svg} +1 -1
  198. package/DS1/x-icon/{ezo-android-screen-lists.svg → chevron-right.svg} +1 -1
  199. package/DS1/x-icon/{ezo-item-button-reopen.svg → chevron-up.svg} +1 -1
  200. package/DS1/x-icon/cloud.svg +3 -0
  201. package/DS1/x-icon/coffee.svg +3 -0
  202. package/DS1/x-icon/coins.svg +3 -0
  203. package/DS1/x-icon/cursor.svg +3 -0
  204. package/DS1/x-icon/curve.svg +7 -0
  205. package/DS1/x-icon/dislike.svg +4 -0
  206. package/DS1/x-icon/dot-bank.svg +3 -0
  207. package/DS1/x-icon/dot-banknote.svg +3 -0
  208. package/DS1/x-icon/dot-banknotes.svg +3 -0
  209. package/DS1/x-icon/dot-card.svg +3 -0
  210. package/DS1/x-icon/dot-coin.svg +3 -0
  211. package/DS1/x-icon/dot-coins.svg +3 -0
  212. package/DS1/x-icon/dot-receipt.svg +3 -0
  213. package/DS1/x-icon/dot-safe.svg +3 -0
  214. package/DS1/x-icon/dot-transfer.svg +3 -0
  215. package/DS1/x-icon/dot-wallet.svg +3 -0
  216. package/DS1/x-icon/download.svg +4 -0
  217. package/DS1/x-icon/drive.svg +5 -0
  218. package/DS1/x-icon/drop.svg +3 -0
  219. package/DS1/x-icon/dropbox.svg +3 -0
  220. package/DS1/x-icon/dumbbell.svg +3 -0
  221. package/DS1/x-icon/eye-off.svg +3 -0
  222. package/DS1/x-icon/eye.svg +3 -0
  223. package/DS1/x-icon/facebook.svg +3 -0
  224. package/DS1/x-icon/figma.svg +5 -0
  225. package/DS1/x-icon/flame.svg +3 -0
  226. package/DS1/x-icon/flip.svg +5 -0
  227. package/DS1/x-icon/food.svg +3 -0
  228. package/DS1/x-icon/fuel.svg +3 -0
  229. package/DS1/x-icon/game.svg +3 -0
  230. package/DS1/x-icon/gift.svg +3 -0
  231. package/DS1/x-icon/github.svg +3 -0
  232. package/DS1/x-icon/heart.svg +3 -0
  233. package/DS1/x-icon/history.svg +3 -0
  234. package/DS1/x-icon/home-alt.svg +3 -0
  235. package/DS1/x-icon/income.svg +3 -0
  236. package/DS1/x-icon/info.svg +5 -0
  237. package/DS1/x-icon/instagram.svg +5 -0
  238. package/DS1/x-icon/jpbank.svg +3 -0
  239. package/DS1/x-icon/key.svg +4 -0
  240. package/DS1/x-icon/keyboard.svg +3 -0
  241. package/DS1/x-icon/like.svg +4 -0
  242. package/DS1/x-icon/linkedin.svg +7 -0
  243. package/DS1/x-icon/loom.svg +6 -0
  244. package/DS1/x-icon/map.svg +4 -0
  245. package/DS1/x-icon/medical.svg +3 -0
  246. package/DS1/x-icon/minus.svg +3 -0
  247. package/DS1/x-icon/moon.svg +3 -0
  248. package/DS1/x-icon/move.svg +3 -0
  249. package/DS1/x-icon/music-alt.svg +3 -0
  250. package/DS1/x-icon/music.svg +5 -0
  251. package/DS1/x-icon/notion.svg +6 -0
  252. package/DS1/x-icon/offline.svg +3 -0
  253. package/DS1/x-icon/pause.svg +4 -0
  254. package/DS1/x-icon/paw.svg +3 -0
  255. package/DS1/x-icon/pdf.svg +3 -0
  256. package/DS1/x-icon/people.svg +5 -0
  257. package/DS1/x-icon/percent.svg +3 -0
  258. package/DS1/x-icon/phone.svg +3 -0
  259. package/DS1/x-icon/pill.svg +3 -0
  260. package/DS1/x-icon/plane.svg +3 -0
  261. package/DS1/x-icon/postal.svg +3 -0
  262. package/DS1/x-icon/present.svg +4 -0
  263. package/DS1/x-icon/reaction.svg +5 -0
  264. package/DS1/x-icon/receipt.svg +3 -0
  265. package/DS1/x-icon/refresh.svg +3 -0
  266. package/DS1/x-icon/refund.svg +3 -0
  267. package/DS1/x-icon/repeat.svg +3 -0
  268. package/DS1/x-icon/rotate.svg +4 -0
  269. package/DS1/x-icon/safe.svg +3 -0
  270. package/DS1/x-icon/school-alt.svg +3 -0
  271. package/DS1/x-icon/shield-alt.svg +3 -0
  272. package/DS1/x-icon/shield.svg +4 -0
  273. package/DS1/x-icon/shirt.svg +3 -0
  274. package/DS1/x-icon/shop.svg +3 -0
  275. package/DS1/x-icon/sign-in.svg +3 -0
  276. package/DS1/x-icon/sign-out.svg +3 -0
  277. package/DS1/x-icon/slack.svg +6 -0
  278. package/DS1/x-icon/sparkle.svg +3 -0
  279. package/DS1/x-icon/start.svg +4 -0
  280. package/DS1/x-icon/stop.svg +3 -0
  281. package/DS1/x-icon/sun.svg +3 -0
  282. package/DS1/x-icon/system.svg +4 -0
  283. package/DS1/x-icon/tag.svg +3 -0
  284. package/DS1/x-icon/ticket.svg +3 -0
  285. package/DS1/x-icon/title-struck.svg +4 -0
  286. package/DS1/x-icon/train.svg +3 -0
  287. package/DS1/x-icon/transfer.svg +3 -0
  288. package/DS1/x-icon/tv.svg +3 -0
  289. package/DS1/x-icon/upload.svg +4 -0
  290. package/DS1/x-icon/video.svg +4 -0
  291. package/DS1/x-icon/vimeo.svg +3 -0
  292. package/DS1/x-icon/wallet.svg +3 -0
  293. package/DS1/x-icon/wifi.svg +3 -0
  294. package/DS1/x-icon/window.svg +4 -0
  295. package/DS1/x-icon/wrench.svg +3 -0
  296. package/DS1/x-icon/x-brand.svg +4 -0
  297. package/DS1/x-icon/youtube.svg +4 -0
  298. package/README.md +3 -3
  299. package/dist/0-face/calendar.d.ts +75 -0
  300. package/dist/0-face/calendar.d.ts.map +1 -0
  301. package/dist/0-face/calendar.js +149 -0
  302. package/dist/0-face/carousel.d.ts +174 -0
  303. package/dist/0-face/carousel.d.ts.map +1 -0
  304. package/dist/0-face/carousel.js +583 -0
  305. package/dist/0-face/component-visibility.d.ts +7 -0
  306. package/dist/0-face/component-visibility.d.ts.map +1 -0
  307. package/dist/0-face/component-visibility.js +11 -0
  308. package/dist/0-face/copy-text.d.ts +11 -0
  309. package/dist/0-face/copy-text.d.ts.map +1 -0
  310. package/dist/0-face/copy-text.js +97 -0
  311. package/dist/0-face/device.d.ts +3 -7
  312. package/dist/0-face/device.d.ts.map +1 -1
  313. package/dist/0-face/device.js +7 -41
  314. package/dist/0-face/dialog-swipe.d.ts +49 -0
  315. package/dist/0-face/dialog-swipe.d.ts.map +1 -0
  316. package/dist/0-face/dialog-swipe.js +239 -0
  317. package/dist/0-face/dialogs.d.ts +33 -0
  318. package/dist/0-face/dialogs.d.ts.map +1 -0
  319. package/dist/0-face/dialogs.js +173 -0
  320. package/dist/0-face/drawer.d.ts +64 -0
  321. package/dist/0-face/drawer.d.ts.map +1 -0
  322. package/dist/0-face/drawer.js +256 -0
  323. package/dist/0-face/feedback-words.d.ts +21 -0
  324. package/dist/0-face/feedback-words.d.ts.map +1 -0
  325. package/dist/0-face/feedback-words.js +30 -0
  326. package/dist/0-face/feedback.d.ts +41 -0
  327. package/dist/0-face/feedback.d.ts.map +1 -0
  328. package/dist/0-face/feedback.js +35 -0
  329. package/dist/0-face/format.d.ts +166 -0
  330. package/dist/0-face/format.d.ts.map +1 -0
  331. package/dist/0-face/format.js +445 -0
  332. package/dist/0-face/generated/toast-assets.d.ts +11 -0
  333. package/dist/0-face/generated/toast-assets.d.ts.map +1 -0
  334. package/dist/0-face/generated/toast-assets.js +14 -0
  335. package/dist/0-face/grid-fit.js +1 -1
  336. package/dist/0-face/grid.d.ts.map +1 -1
  337. package/dist/0-face/grid.js +2 -1
  338. package/dist/0-face/header.d.ts +38 -0
  339. package/dist/0-face/header.d.ts.map +1 -0
  340. package/dist/0-face/header.js +80 -0
  341. package/dist/0-face/i18n.d.ts +32 -2
  342. package/dist/0-face/i18n.d.ts.map +1 -1
  343. package/dist/0-face/i18n.js +65 -19
  344. package/dist/0-face/keys.d.ts +104 -0
  345. package/dist/0-face/keys.d.ts.map +1 -0
  346. package/dist/0-face/keys.js +253 -0
  347. package/dist/0-face/languages.d.ts +2 -2
  348. package/dist/0-face/languages.js +3 -3
  349. package/dist/0-face/layout-inspector.js +6 -6
  350. package/dist/0-face/library-styles.d.ts +7 -0
  351. package/dist/0-face/library-styles.d.ts.map +1 -0
  352. package/dist/0-face/library-styles.js +29 -0
  353. package/dist/0-face/load-toaster.d.ts +8 -0
  354. package/dist/0-face/load-toaster.d.ts.map +1 -0
  355. package/dist/0-face/load-toaster.js +7 -0
  356. package/dist/0-face/preference-options.d.ts +34 -0
  357. package/dist/0-face/preference-options.d.ts.map +1 -0
  358. package/dist/0-face/preference-options.js +1 -0
  359. package/dist/0-face/preferences.d.ts +3 -12
  360. package/dist/0-face/preferences.d.ts.map +1 -1
  361. package/dist/0-face/pricing.d.ts +19 -9
  362. package/dist/0-face/pricing.d.ts.map +1 -1
  363. package/dist/0-face/pricing.js +81 -40
  364. package/dist/0-face/scaling.d.ts +71 -1
  365. package/dist/0-face/scaling.d.ts.map +1 -1
  366. package/dist/0-face/scaling.js +108 -3
  367. package/dist/0-face/shared-words.d.ts +9 -4
  368. package/dist/0-face/shared-words.d.ts.map +1 -1
  369. package/dist/0-face/sidebar.d.ts +69 -0
  370. package/dist/0-face/sidebar.d.ts.map +1 -0
  371. package/dist/0-face/sidebar.js +191 -0
  372. package/dist/0-face/theme-bootstrap.d.ts +1 -0
  373. package/dist/0-face/theme-bootstrap.d.ts.map +1 -0
  374. package/dist/0-face/theme-bootstrap.js +214 -0
  375. package/dist/0-face/theme.d.ts +32 -0
  376. package/dist/0-face/theme.d.ts.map +1 -1
  377. package/dist/0-face/theme.js +69 -43
  378. package/dist/0-face/toast-queue.d.ts +110 -0
  379. package/dist/0-face/toast-queue.d.ts.map +1 -0
  380. package/dist/0-face/toast-queue.js +98 -0
  381. package/dist/0-face/toaster.d.ts +27 -0
  382. package/dist/0-face/toaster.d.ts.map +1 -0
  383. package/dist/0-face/toaster.js +373 -0
  384. package/dist/0-face/tooltips.d.ts +24 -1
  385. package/dist/0-face/tooltips.d.ts.map +1 -1
  386. package/dist/0-face/tooltips.js +155 -16
  387. package/dist/0-face/translations/da.json +60 -1
  388. package/dist/0-face/translations/de.json +60 -1
  389. package/dist/0-face/translations/en.json +60 -1
  390. package/dist/0-face/translations/es.json +60 -1
  391. package/dist/0-face/translations/fi.json +60 -1
  392. package/dist/0-face/translations/fr.json +60 -1
  393. package/dist/0-face/translations/is.json +60 -1
  394. package/dist/0-face/translations/it.json +60 -1
  395. package/dist/0-face/translations/ja.json +60 -1
  396. package/dist/0-face/translations/ko.json +60 -1
  397. package/dist/0-face/translations/nb.json +60 -1
  398. package/dist/0-face/translations/pt-BR.json +60 -1
  399. package/dist/0-face/translations/pt.json +60 -1
  400. package/dist/0-face/translations/sv.json +60 -1
  401. package/dist/0-face/translations/zh.json +60 -1
  402. package/dist/0-face/translations-format.d.ts.map +1 -1
  403. package/dist/0-face/translations-format.js +3 -4
  404. package/dist/1-root/library-tokens.d.ts +31 -0
  405. package/dist/1-root/library-tokens.d.ts.map +1 -0
  406. package/dist/1-root/library-tokens.js +84 -0
  407. package/dist/1-root/measures.d.ts +52 -0
  408. package/dist/1-root/measures.d.ts.map +1 -0
  409. package/dist/1-root/measures.js +49 -0
  410. package/dist/1-root/palette.d.ts +5 -0
  411. package/dist/1-root/palette.d.ts.map +1 -1
  412. package/dist/1-root/palette.js +18 -0
  413. package/dist/2-core/button-split.d.ts +21 -0
  414. package/dist/2-core/button-split.d.ts.map +1 -0
  415. package/dist/2-core/button-split.js +48 -0
  416. package/dist/2-core/chart/grid-chart.d.ts +113 -0
  417. package/dist/2-core/chart/grid-chart.d.ts.map +1 -0
  418. package/dist/2-core/chart/grid-chart.js +466 -0
  419. package/dist/2-core/ds-avatar.d.ts +20 -3
  420. package/dist/2-core/ds-avatar.d.ts.map +1 -1
  421. package/dist/2-core/ds-avatar.js +19 -5
  422. package/dist/2-core/ds-badge.js +10 -13
  423. package/dist/2-core/ds-button.d.ts +76 -0
  424. package/dist/2-core/ds-button.d.ts.map +1 -1
  425. package/dist/2-core/ds-button.js +101 -26
  426. package/dist/2-core/ds-card.d.ts +76 -11
  427. package/dist/2-core/ds-card.d.ts.map +1 -1
  428. package/dist/2-core/ds-card.js +129 -105
  429. package/dist/2-core/ds-chart.d.ts +105 -0
  430. package/dist/2-core/ds-chart.d.ts.map +1 -0
  431. package/dist/2-core/ds-chart.js +390 -0
  432. package/dist/2-core/ds-composer.d.ts +7 -0
  433. package/dist/2-core/ds-composer.d.ts.map +1 -1
  434. package/dist/2-core/ds-composer.js +1 -1
  435. package/dist/2-core/ds-cycle.d.ts +36 -2
  436. package/dist/2-core/ds-cycle.d.ts.map +1 -1
  437. package/dist/2-core/ds-cycle.js +95 -18
  438. package/dist/2-core/ds-empty.d.ts +74 -0
  439. package/dist/2-core/ds-empty.d.ts.map +1 -0
  440. package/dist/2-core/ds-empty.js +96 -0
  441. package/dist/2-core/ds-icon.d.ts +6 -0
  442. package/dist/2-core/ds-icon.d.ts.map +1 -1
  443. package/dist/2-core/ds-icon.js +12 -2
  444. package/dist/2-core/ds-input.d.ts +39 -1
  445. package/dist/2-core/ds-input.d.ts.map +1 -1
  446. package/dist/2-core/ds-input.js +150 -6
  447. package/dist/2-core/ds-message.d.ts +154 -0
  448. package/dist/2-core/ds-message.d.ts.map +1 -0
  449. package/dist/2-core/ds-message.js +177 -0
  450. package/dist/2-core/ds-notice.d.ts +13 -0
  451. package/dist/2-core/ds-notice.d.ts.map +1 -0
  452. package/dist/2-core/ds-notice.js +11 -0
  453. package/dist/2-core/ds-progress.d.ts +200 -0
  454. package/dist/2-core/ds-progress.d.ts.map +1 -0
  455. package/dist/2-core/ds-progress.js +319 -0
  456. package/dist/2-core/ds-stat.d.ts +126 -0
  457. package/dist/2-core/ds-stat.d.ts.map +1 -0
  458. package/dist/2-core/ds-stat.js +137 -0
  459. package/dist/2-core/ds-switch.d.ts +1 -0
  460. package/dist/2-core/ds-switch.d.ts.map +1 -1
  461. package/dist/2-core/ds-switch.js +1 -0
  462. package/dist/2-core/ds-text.d.ts +15 -2
  463. package/dist/2-core/ds-text.d.ts.map +1 -1
  464. package/dist/2-core/ds-text.js +13 -0
  465. package/dist/2-core/ds-toast.d.ts +139 -0
  466. package/dist/2-core/ds-toast.d.ts.map +1 -0
  467. package/dist/2-core/ds-toast.js +329 -0
  468. package/dist/2-core/figures.d.ts +56 -0
  469. package/dist/2-core/figures.d.ts.map +1 -0
  470. package/dist/2-core/figures.js +70 -0
  471. package/dist/2-core/generated/ds-icon-map.d.ts +120 -65
  472. package/dist/2-core/generated/ds-icon-map.d.ts.map +1 -1
  473. package/dist/2-core/generated/ds-icon-map.js +238 -128
  474. package/dist/2-core/generated/icon-avatar.d.ts +2 -0
  475. package/dist/2-core/generated/icon-avatar.d.ts.map +1 -0
  476. package/dist/2-core/generated/icon-avatar.js +2 -0
  477. package/dist/2-core/generated/icons-extra.d.ts.map +1 -1
  478. package/dist/2-core/generated/icons-extra.js +120 -65
  479. package/dist/2-core/generated/icons-immediate.d.ts +1 -1
  480. package/dist/2-core/generated/icons-immediate.d.ts.map +1 -1
  481. package/dist/2-core/generated/icons-immediate.js +1 -1
  482. package/dist/3-unit/calendar-base.d.ts +149 -0
  483. package/dist/3-unit/calendar-base.d.ts.map +1 -0
  484. package/dist/3-unit/calendar-base.js +172 -0
  485. package/dist/3-unit/ds-accordion.d.ts +9 -0
  486. package/dist/3-unit/ds-accordion.d.ts.map +1 -1
  487. package/dist/3-unit/ds-accordion.js +7 -1
  488. package/dist/3-unit/ds-avatar-group.d.ts +1 -0
  489. package/dist/3-unit/ds-avatar-group.d.ts.map +1 -1
  490. package/dist/3-unit/ds-calendar-large.d.ts +138 -0
  491. package/dist/3-unit/ds-calendar-large.d.ts.map +1 -0
  492. package/dist/3-unit/ds-calendar-large.js +305 -0
  493. package/dist/3-unit/ds-calendar.d.ts +175 -0
  494. package/dist/3-unit/ds-calendar.d.ts.map +1 -0
  495. package/dist/3-unit/ds-calendar.js +303 -0
  496. package/dist/3-unit/ds-carousel.d.ts +182 -0
  497. package/dist/3-unit/ds-carousel.d.ts.map +1 -0
  498. package/dist/3-unit/ds-carousel.js +327 -0
  499. package/dist/3-unit/ds-date-picker.d.ts +203 -0
  500. package/dist/3-unit/ds-date-picker.d.ts.map +1 -0
  501. package/dist/3-unit/ds-date-picker.js +499 -0
  502. package/dist/3-unit/ds-form.d.ts.map +1 -1
  503. package/dist/3-unit/ds-form.js +38 -28
  504. package/dist/3-unit/ds-list.d.ts +12 -0
  505. package/dist/3-unit/ds-list.d.ts.map +1 -1
  506. package/dist/3-unit/ds-list.js +52 -1
  507. package/dist/3-unit/ds-matrix.d.ts +146 -0
  508. package/dist/3-unit/ds-matrix.d.ts.map +1 -0
  509. package/dist/3-unit/ds-matrix.js +248 -0
  510. package/dist/3-unit/ds-menu.d.ts +1 -1
  511. package/dist/3-unit/ds-menu.d.ts.map +1 -1
  512. package/dist/3-unit/ds-menu.js +7 -4
  513. package/dist/3-unit/ds-row.d.ts +66 -0
  514. package/dist/3-unit/ds-row.d.ts.map +1 -1
  515. package/dist/3-unit/ds-row.js +80 -1
  516. package/dist/3-unit/ds-sidebar.d.ts +149 -0
  517. package/dist/3-unit/ds-sidebar.d.ts.map +1 -0
  518. package/dist/3-unit/ds-sidebar.js +342 -0
  519. package/dist/3-unit/ds-table.d.ts +28 -10
  520. package/dist/3-unit/ds-table.d.ts.map +1 -1
  521. package/dist/3-unit/ds-table.js +53 -25
  522. package/dist/4-page/ds-dialog.d.ts +16 -1
  523. package/dist/4-page/ds-dialog.d.ts.map +1 -1
  524. package/dist/4-page/ds-dialog.js +39 -3
  525. package/dist/4-page/ds-document.d.ts +98 -0
  526. package/dist/4-page/ds-document.d.ts.map +1 -0
  527. package/dist/4-page/ds-document.js +213 -0
  528. package/dist/4-page/ds-footer.d.ts +100 -0
  529. package/dist/4-page/ds-footer.d.ts.map +1 -0
  530. package/dist/4-page/ds-footer.js +119 -0
  531. package/dist/4-page/ds-grid.d.ts +32 -12
  532. package/dist/4-page/ds-grid.d.ts.map +1 -1
  533. package/dist/4-page/ds-grid.js +30 -39
  534. package/dist/4-page/ds-header.d.ts +84 -0
  535. package/dist/4-page/ds-header.d.ts.map +1 -0
  536. package/dist/4-page/ds-header.js +143 -0
  537. package/dist/4-page/ds-page.d.ts +110 -0
  538. package/dist/4-page/ds-page.d.ts.map +1 -0
  539. package/dist/4-page/ds-page.js +155 -0
  540. package/dist/4-page/email.d.ts +2 -3
  541. package/dist/4-page/email.d.ts.map +1 -1
  542. package/dist/4-page/email.js +2 -3
  543. package/dist/components.d.ts +4 -1
  544. package/dist/components.d.ts.map +1 -1
  545. package/dist/components.js +4 -1
  546. package/dist/design-system-1.bundle.js +12322 -5523
  547. package/dist/design-system-1.bundle.js.map +1 -1
  548. package/dist/design-system-1.bundle.min.js +8771 -4115
  549. package/dist/design-system-1.bundle.min.js.map +1 -1
  550. package/dist/design-system-1.css +1 -1
  551. package/dist/feedback.js +541 -0
  552. package/dist/feedback.js.map +1 -0
  553. package/dist/{icons-extra-BoAACA4X.js → icons-extra-CiDDs7_-.js} +404 -312
  554. package/dist/icons-extra-CiDDs7_-.js.map +1 -0
  555. package/dist/{icons-extra-DlxTo_Y4.js → icons-extra-hauwCx07.js} +121 -66
  556. package/dist/icons-extra-hauwCx07.js.map +1 -0
  557. package/dist/index.d.ts +21 -6
  558. package/dist/index.d.ts.map +1 -1
  559. package/dist/index.js +21 -6
  560. package/dist/react/avatar.d.ts +5 -9
  561. package/dist/react/avatar.d.ts.map +1 -1
  562. package/dist/react/avatar.js +2 -2
  563. package/dist/react/button-split.d.ts +19 -0
  564. package/dist/react/button-split.d.ts.map +1 -0
  565. package/dist/react/{split.js → button-split.js} +12 -9
  566. package/dist/react/button.d.ts +18 -7
  567. package/dist/react/button.d.ts.map +1 -1
  568. package/dist/react/button.js +13 -4
  569. package/dist/react/carousel.d.ts +63 -0
  570. package/dist/react/carousel.d.ts.map +1 -0
  571. package/dist/react/carousel.js +47 -0
  572. package/dist/react/choice.d.ts +1 -1
  573. package/dist/react/context-menu.d.ts +337 -337
  574. package/dist/react/dialog.d.ts +684 -678
  575. package/dist/react/dialog.d.ts.map +1 -1
  576. package/dist/react/dialog.js +29 -3
  577. package/dist/react/document.d.ts +11 -0
  578. package/dist/react/document.d.ts.map +1 -0
  579. package/dist/react/document.js +10 -0
  580. package/dist/react/empty.d.ts +34 -0
  581. package/dist/react/empty.d.ts.map +1 -0
  582. package/dist/react/empty.js +8 -0
  583. package/dist/react/file-picker.d.ts +1 -1
  584. package/dist/react/footer.d.ts +13 -0
  585. package/dist/react/footer.d.ts.map +1 -0
  586. package/dist/react/footer.js +12 -0
  587. package/dist/react/header.d.ts +12 -0
  588. package/dist/react/header.d.ts.map +1 -0
  589. package/dist/react/header.js +11 -0
  590. package/dist/react/input.d.ts +31 -9
  591. package/dist/react/input.d.ts.map +1 -1
  592. package/dist/react/input.js +94 -94
  593. package/dist/react/message.d.ts +66 -0
  594. package/dist/react/message.d.ts.map +1 -0
  595. package/dist/react/message.js +37 -0
  596. package/dist/react/notice.d.ts +45 -0
  597. package/dist/react/notice.d.ts.map +1 -0
  598. package/dist/react/notice.js +34 -0
  599. package/dist/react/page.d.ts +10 -0
  600. package/dist/react/page.d.ts.map +1 -0
  601. package/dist/react/page.js +9 -0
  602. package/dist/react/progress.d.ts +107 -0
  603. package/dist/react/progress.d.ts.map +1 -0
  604. package/dist/react/progress.js +47 -0
  605. package/dist/react/sidebar.d.ts +54 -0
  606. package/dist/react/sidebar.d.ts.map +1 -0
  607. package/dist/react/sidebar.js +48 -0
  608. package/dist/react/stat.d.ts +78 -0
  609. package/dist/react/stat.d.ts.map +1 -0
  610. package/dist/react/stat.js +25 -0
  611. package/dist/react/toast.d.ts +37 -0
  612. package/dist/react/toast.d.ts.map +1 -0
  613. package/dist/react/toast.js +55 -0
  614. package/dist/shared-words-CyZYc_De.js +1818 -0
  615. package/dist/shared-words-CyZYc_De.js.map +1 -0
  616. package/dist/shared-words-DAVSET4Z.js +1800 -0
  617. package/dist/shared-words-DAVSET4Z.js.map +1 -0
  618. package/package.json +19 -6
  619. package/DS1/2-core/ds-banner.ts +0 -122
  620. package/DS1/2-core/ds-date.ts +0 -24
  621. package/DS1/2-core/ds-language.ts +0 -94
  622. package/DS1/2-core/ds-pagedots.ts +0 -52
  623. package/DS1/2-core/ds-split.ts +0 -122
  624. package/DS1/2-core/ds-textarea.ts +0 -136
  625. package/DS1/2-core/styles/ds-banner.css +0 -77
  626. package/DS1/2-core/styles/ds-date.css +0 -9
  627. package/DS1/2-core/styles/ds-language.css +0 -78
  628. package/DS1/2-core/styles/ds-pagedots.css +0 -31
  629. package/DS1/2-core/styles/ds-textarea.css +0 -27
  630. package/DS1/4-page/ds-layout.ts +0 -949
  631. package/DS1/4-page/styles/ds-layout.css +0 -246
  632. package/DS1/react/split.ts +0 -42
  633. package/DS1/x-icon/ezo-accordion-chevron.svg +0 -11
  634. package/DS1/x-icon/ezo-account-close.svg +0 -1
  635. package/DS1/x-icon/ezo-android-screen-archive.svg +0 -4
  636. package/DS1/x-icon/ezo-android-screen-calendar.svg +0 -6
  637. package/DS1/x-icon/ezo-android-screen-camera.svg +0 -5
  638. package/DS1/x-icon/ezo-android-screen-check.svg +0 -3
  639. package/DS1/x-icon/ezo-android-screen-compose.svg +0 -4
  640. package/DS1/x-icon/ezo-android-screen-globe.svg +0 -1
  641. package/DS1/x-icon/ezo-android-screen-shared.svg +0 -5
  642. package/DS1/x-icon/ezo-android-screen-store.svg +0 -4
  643. package/DS1/x-icon/ezo-android-screen-views.svg +0 -4
  644. package/DS1/x-icon/ezo-android-screen-work.svg +0 -3
  645. package/DS1/x-icon/ezo-arrow-cursor.svg +0 -8
  646. package/DS1/x-icon/ezo-block-accordion.svg +0 -1
  647. package/DS1/x-icon/ezo-block-divider.svg +0 -1
  648. package/DS1/x-icon/ezo-block-group.svg +0 -1
  649. package/DS1/x-icon/ezo-block-image.svg +0 -1
  650. package/DS1/x-icon/ezo-block-section.svg +0 -1
  651. package/DS1/x-icon/ezo-block-table.svg +0 -1
  652. package/DS1/x-icon/ezo-code.svg +0 -14
  653. package/DS1/x-icon/ezo-down.svg +0 -3
  654. package/DS1/x-icon/ezo-figma-premium-star.svg +0 -5
  655. package/DS1/x-icon/ezo-item-button-complete.svg +0 -4
  656. package/DS1/x-icon/ezo-item-button-header.svg +0 -3
  657. package/DS1/x-icon/ezo-item-button-open.svg +0 -3
  658. package/DS1/x-icon/ezo-item-button-star.svg +0 -3
  659. package/DS1/x-icon/ezo-item-button-unheader.svg +0 -7
  660. package/DS1/x-icon/ezo-item-button-unstar.svg +0 -5
  661. package/DS1/x-icon/ezo-item-star.svg +0 -5
  662. package/DS1/x-icon/ezo-left.svg +0 -3
  663. package/DS1/x-icon/ezo-logo.svg +0 -1
  664. package/DS1/x-icon/ezo-mic.svg +0 -8
  665. package/DS1/x-icon/ezo-move-column.svg +0 -14
  666. package/DS1/x-icon/ezo-move-row.svg +0 -18
  667. package/DS1/x-icon/ezo-navigation-check.svg +0 -1
  668. package/DS1/x-icon/ezo-note-back.svg +0 -5
  669. package/DS1/x-icon/ezo-note-menu-complete.svg +0 -11
  670. package/DS1/x-icon/ezo-note-menu-delete.svg +0 -4
  671. package/DS1/x-icon/ezo-note-menu-duplicate.svg +0 -8
  672. package/DS1/x-icon/ezo-note-menu-note.svg +0 -5
  673. package/DS1/x-icon/ezo-note-menu-page.svg +0 -3
  674. package/DS1/x-icon/ezo-note-menu-star.svg +0 -5
  675. package/DS1/x-icon/ezo-note-menu-start.svg +0 -6
  676. package/DS1/x-icon/ezo-note-menu-stop.svg +0 -6
  677. package/DS1/x-icon/ezo-note-more.svg +0 -7
  678. package/DS1/x-icon/ezo-note-star.svg +0 -5
  679. package/DS1/x-icon/ezo-pattern-call-to-action.svg +0 -1
  680. package/DS1/x-icon/ezo-play.svg +0 -5
  681. package/DS1/x-icon/ezo-premium-star.svg +0 -5
  682. package/DS1/x-icon/ezo-setting-1xdots.svg +0 -13
  683. package/DS1/x-icon/ezo-setting-1xgrid.svg +0 -10
  684. package/DS1/x-icon/ezo-setting-1xlines.svg +0 -7
  685. package/DS1/x-icon/ezo-setting-2xdots.svg +0 -8
  686. package/DS1/x-icon/ezo-setting-2xgrid.svg +0 -8
  687. package/DS1/x-icon/ezo-setting-2xlines.svg +0 -6
  688. package/DS1/x-icon/ezo-setting-blank.svg +0 -5
  689. package/DS1/x-icon/ezo-store-chevron.svg +0 -1
  690. package/DS1/x-icon/ezo-upgrade-close.svg +0 -5
  691. package/DS1/x-icon/product-sources.json +0 -2557
  692. package/dist/2-core/ds-banner.d.ts +0 -68
  693. package/dist/2-core/ds-banner.d.ts.map +0 -1
  694. package/dist/2-core/ds-banner.js +0 -98
  695. package/dist/2-core/ds-date.d.ts +0 -16
  696. package/dist/2-core/ds-date.d.ts.map +0 -1
  697. package/dist/2-core/ds-date.js +0 -15
  698. package/dist/2-core/ds-language.d.ts +0 -75
  699. package/dist/2-core/ds-language.d.ts.map +0 -1
  700. package/dist/2-core/ds-language.js +0 -74
  701. package/dist/2-core/ds-pagedots.d.ts +0 -32
  702. package/dist/2-core/ds-pagedots.d.ts.map +0 -1
  703. package/dist/2-core/ds-pagedots.js +0 -36
  704. package/dist/2-core/ds-split.d.ts +0 -75
  705. package/dist/2-core/ds-split.d.ts.map +0 -1
  706. package/dist/2-core/ds-split.js +0 -101
  707. package/dist/2-core/ds-textarea.d.ts +0 -46
  708. package/dist/2-core/ds-textarea.d.ts.map +0 -1
  709. package/dist/2-core/ds-textarea.js +0 -121
  710. package/dist/4-page/ds-layout.d.ts +0 -31
  711. package/dist/4-page/ds-layout.d.ts.map +0 -1
  712. package/dist/4-page/ds-layout.js +0 -930
  713. package/dist/icons-extra-BoAACA4X.js.map +0 -1
  714. package/dist/icons-extra-DlxTo_Y4.js.map +0 -1
  715. package/dist/react/split.d.ts +0 -15
  716. package/dist/react/split.d.ts.map +0 -1
  717. package/dist/shared-words-Cg6Phtx0.js +0 -915
  718. package/dist/shared-words-Cg6Phtx0.js.map +0 -1
  719. package/dist/shared-words-Ddz0yUEF.js +0 -933
  720. package/dist/shared-words-Ddz0yUEF.js.map +0 -1
  721. /package/DS1/x-icon/{ezo-password-visibility-see.svg → password-see.svg} +0 -0
  722. /package/DS1/x-icon/{ezo-password-visibility-unsee.svg → password-unsee.svg} +0 -0
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Copy text to the clipboard and say so in a toast, for pages with or
3
+ * without a framework.
4
+ *
5
+ * It writes with the Clipboard API, and where that is missing or refused (a
6
+ * page served over plain HTTP, an older browser) it copies from a hidden field
7
+ * instead. The toast is the shared word "Copied" in the page's language,
8
+ * shown by `<ds-toast>` where the page has one and by the page toaster
9
+ * otherwise; a failure says "Could not copy".
10
+ *
11
+ * It is small on its own: the page toaster (`./toaster.ts`) loads only when a
12
+ * toast is shown and nothing in the page draws toasts yet, so a page that
13
+ * copies with `toast: false`, or has `<ds-toast>`, never loads it. Import it
14
+ * from here, not from `./feedback.ts`, where the page needs nothing else.
15
+ */
16
+ import { feedbackWord } from "./feedback-words";
17
+ import { toast, toasterListening } from "./toast-queue";
18
+
19
+ export interface CopyTextOptions {
20
+ /** The toast's words when it is copied; `false` shows no toast at all. "Copied" by default. */
21
+ toast?: string | false;
22
+ }
23
+
24
+ /** The open modal dialog, if any: the rest of the page is inert, so the hidden field goes in it. */
25
+ function modalDialog(): HTMLDialogElement | null {
26
+ try {
27
+ return document.querySelector<HTMLDialogElement>("dialog:modal");
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+
33
+ /** The fallback: select the text in a hidden field and copy the selection. */
34
+ function copyFromField(text: string): boolean {
35
+ if (typeof document === "undefined" || !document.body) return false;
36
+ const active = document.activeElement instanceof HTMLElement ? document.activeElement : null;
37
+ const selection = document.getSelection();
38
+ const ranges = selection ? Array.from({ length: selection.rangeCount }, (_, index) => selection.getRangeAt(index)) : [];
39
+ const field = document.createElement("textarea");
40
+ field.value = text;
41
+ field.setAttribute("readonly", "");
42
+ field.setAttribute("aria-hidden", "true");
43
+ field.tabIndex = -1;
44
+ field.style.cssText = "position:fixed;top:0;left:0;width:1px;height:1px;margin:0;padding:0;border:0;opacity:0;pointer-events:none";
45
+ (modalDialog() ?? document.body).appendChild(field);
46
+ field.focus({ preventScroll: true });
47
+ field.select();
48
+ field.setSelectionRange(0, text.length);
49
+ let copied = false;
50
+ try {
51
+ copied = document.execCommand("copy");
52
+ } catch {
53
+ copied = false;
54
+ }
55
+ field.remove();
56
+ active?.focus({ preventScroll: true });
57
+ if (selection) {
58
+ selection.removeAllRanges();
59
+ for (const range of ranges) selection.addRange(range);
60
+ }
61
+ return copied;
62
+ }
63
+
64
+ /**
65
+ * Copy `text` and confirm it with a toast. Resolves to whether it was copied.
66
+ * Call it from the click or key press that asks for it: browsers allow writing
67
+ * to the clipboard only then.
68
+ */
69
+ export async function copyText(text: string, options: CopyTextOptions = {}): Promise<boolean> {
70
+ let copied = false;
71
+ const clipboard = typeof navigator === "undefined" ? undefined : navigator.clipboard;
72
+ if (clipboard?.writeText) {
73
+ try {
74
+ await clipboard.writeText(text);
75
+ copied = true;
76
+ } catch {
77
+ copied = false;
78
+ }
79
+ }
80
+ if (!copied) copied = copyFromField(text);
81
+ if (options.toast !== false) await say(copied, copied ? options.toast ?? feedbackWord("Copied") : feedbackWord("Could not copy"));
82
+ return copied;
83
+ }
84
+
85
+ /** The toast, with the page toaster loaded first where nothing in the page draws toasts yet. */
86
+ async function say(copied: boolean, words: string): Promise<void> {
87
+ if (!toasterListening()) {
88
+ try {
89
+ (await import("./load-toaster")).pageToaster();
90
+ } catch {
91
+ // No toaster could load: the copy itself still answers.
92
+ }
93
+ }
94
+ if (copied) toast.success(words);
95
+ else toast.error(words);
96
+ }
@@ -2,8 +2,10 @@
2
2
  // Device detection, context utilities, and responsive scaling
3
3
 
4
4
  import { signal } from "@lit-labs/signals";
5
- import { calculateScalingFactor as calculateScale, defaultScalingConfig, type ScalingConfig } from "./scaling";
6
- export type { ScalingConfig, ScalingMode } from "./scaling";
5
+ import { applyScaling, calculateScalingFactor as calculateScale, defaultScalingConfig, detectMobileDevice, type ScalingConfig } from "./scaling";
6
+ export type { ScalingConfig, ScalingMode, ScalingOptions, ScalingSnap } from "./scaling";
7
+ // One rule for which screens scale, shared with pages that copy scaling.ts alone.
8
+ export { applyScaling, detectMobileDevice, startScaling, toDevicePixel, toLayoutStep } from "./scaling";
7
9
 
8
10
  // ============================================================================
9
11
  // Types
@@ -32,38 +34,6 @@ export const scalingConfig = signal<ScalingConfig>(defaultScalingConfig);
32
34
  // Device Detection
33
35
  // ============================================================================
34
36
 
35
- /**
36
- * Comprehensive mobile device detection
37
- * Combines user agent detection, touch capability, and viewport size
38
- */
39
- export function detectMobileDevice(): boolean {
40
- if (typeof navigator === "undefined" || typeof window === "undefined") {
41
- return false;
42
- }
43
-
44
- const nav = navigator as any;
45
- const win = window as any;
46
- const ua: string =
47
- (nav && (nav.userAgent || nav.vendor)) || (win && win.opera) || "";
48
-
49
- // User agent based detection
50
- const uaMatchesMobile =
51
- /Mobile|Android|iP(ad|hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)|Windows Phone|Phone|Tablet/i.test(
52
- ua
53
- );
54
-
55
- // Touch capability detection
56
- const touchPoints = (nav && nav.maxTouchPoints) || 0;
57
- const isTouchCapable = touchPoints > 1;
58
-
59
- // Viewport detection
60
- const narrowViewport = win
61
- ? Math.min(win.innerWidth || 0, win.innerHeight || 0) <= 820
62
- : false;
63
-
64
- return uaMatchesMobile || (isTouchCapable && narrowViewport);
65
- }
66
-
67
37
  /**
68
38
  * Get detailed device information
69
39
  */
@@ -132,39 +102,19 @@ export function setScalingConfig(config: Partial<ScalingConfig>): void {
132
102
 
133
103
  /**
134
104
  * Update the scaling factor based on current viewport
135
- * Desktop always uses factor 1; mobile uses computed factor
105
+ * Desktop always uses factor 1; mobile uses computed factor (see scaling.ts)
136
106
  */
137
107
  export function updateScalingFactor(): void {
138
108
  if (typeof window === "undefined" || typeof document === "undefined") {
139
109
  return;
140
110
  }
141
111
 
142
- const isMobile = detectMobileDevice();
143
-
144
- // Desktop: always use factor 1
145
- if (!isMobile) {
146
- const factor = 1;
147
- scalingFactor.set(factor);
148
- document.documentElement.style.setProperty("--sf", factor.toString());
149
- window.dispatchEvent(
150
- new CustomEvent("scaling-changed", {
151
- detail: { scalingFactor: factor, config: scalingConfig.get() },
152
- })
153
- );
154
- return;
155
- }
156
-
157
- // Mobile: compute scaling based on viewport
158
- const viewportWidth = document.documentElement.clientWidth;
159
112
  const config = scalingConfig.get();
160
- const newFactor = calculateScalingFactor(viewportWidth, config);
161
-
162
- scalingFactor.set(newFactor);
163
- document.documentElement.style.setProperty("--sf", newFactor.toString());
164
-
113
+ const factor = applyScaling(config);
114
+ scalingFactor.set(factor);
165
115
  window.dispatchEvent(
166
116
  new CustomEvent("scaling-changed", {
167
- detail: { scalingFactor: newFactor, config },
117
+ detail: { scalingFactor: factor, config },
168
118
  })
169
119
  );
170
120
  }
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Swipe a sheet or a drawer away: a finger that drags a dialog towards the
3
+ * edge it came from moves it, and letting go far or fast enough dismisses it.
4
+ * `ds-dialog` binds it for `placement="sheet"`, `"end"` and `"start"`; a page
5
+ * without Lit binds its own `dialog.ds-dialog--sheet` (or `--end`, `--start`)
6
+ * with `bindDialogSwipe(dialog)`. Plain TypeScript with no framework.
7
+ *
8
+ * It follows the finger as touch does on Apple's platforms:
9
+ *
10
+ * - Touch alone; a mouse or a trackpad closes with Escape or the close button.
11
+ * - Ten pixels of movement before it decides, then it tracks the finger one
12
+ * to one from where it was grabbed, a moving dialog included (it starts
13
+ * from the dialog's place on screen, not from where it was going).
14
+ * - A drag along the content's own scroll is the content's: a sheet scrolled
15
+ * down scrolls back up first, a horizontal strip in a drawer scrolls, and a
16
+ * slider, a range input or anything marked `data-ds-dialog-no-swipe` keeps
17
+ * its touches.
18
+ * - Dragged the other way, it gives a little and pulls back (rubber-banding).
19
+ * - On release it projects where the throw would come to rest (the
20
+ * deceleration of a scroll, rate 0.998) and dismisses when that is past half
21
+ * its size and the finger is not moving back; otherwise it settles home.
22
+ * - The backdrop fades as the dialog goes (`--ds-dialog-progress`).
23
+ *
24
+ * Dismissing asks first, as a close button does: the element sends
25
+ * `ds-close-request` with the reason `"swipe"`, the native frame a cancelable
26
+ * `ds-close-request` on the dialog, and closes it when no listener prevents
27
+ * that. Declined (a form with unsaved changes asking first), it settles home.
28
+ * The motion itself is dialog-placement.css's, in transform alone.
29
+ */
30
+
31
+ export type DialogPlacement = "center" | "sheet" | "end" | "start";
32
+
33
+ export interface DialogSwipeOptions {
34
+ /** Where the dialog stands, read at each touch; from its class or `data-ds-placement` by default. */
35
+ placement?: DialogPlacement | (() => DialogPlacement);
36
+ /**
37
+ * Called when a swipe would dismiss it. By default a cancelable
38
+ * `ds-close-request` (`detail.reason` `"swipe"`) on the dialog, then
39
+ * `dialog.close()` unless a listener prevented it. Still open two frames
40
+ * later, the dialog settles home.
41
+ */
42
+ onDismiss?: (dialog: HTMLDialogElement) => void;
43
+ }
44
+
45
+ /** Ten pixels before a touch is a drag, as the platforms wait. */
46
+ const SLOP = 10;
47
+ /** A scroll's deceleration: where a throw at this speed comes to rest. */
48
+ const DECELERATION = 0.998;
49
+ const project = (velocity: number) => ((velocity / 1000) * DECELERATION) / (1 - DECELERATION);
50
+ /** Past its edge the dialog follows less the further it goes. */
51
+ const rubberband = (overshoot: number, dimension: number, constant = 0.55) =>
52
+ (overshoot * dimension * constant) / (dimension + constant * Math.abs(overshoot));
53
+
54
+ const PLACEMENTS = new Set<DialogPlacement>(["center", "sheet", "end", "start"]);
55
+
56
+ /** A dialog's placement, from `data-ds-placement` or its `ds-dialog--` class. */
57
+ export function dialogPlacement(dialog: HTMLDialogElement): DialogPlacement {
58
+ const marked = dialog.getAttribute("data-ds-placement") as DialogPlacement | null;
59
+ if (marked && PLACEMENTS.has(marked)) return marked;
60
+ for (const placement of ["sheet", "end", "start"] as const) if (dialog.classList.contains(`ds-dialog--${placement}`)) return placement;
61
+ return "center";
62
+ }
63
+
64
+ type Gesture = {
65
+ id: number;
66
+ x: number;
67
+ y: number;
68
+ /** Where the dialog stood when the finger came down, towards its edge. */
69
+ base: number;
70
+ /** How far the finger had moved when the drag began. */
71
+ start: number;
72
+ mode: "pending" | "drag" | "ignore";
73
+ offset: number;
74
+ samples: { time: number; offset: number }[];
75
+ target: EventTarget | null;
76
+ path: EventTarget[];
77
+ };
78
+
79
+ /** The axis the dialog leaves along, and the sign of the way out on it. */
80
+ function direction(dialog: HTMLDialogElement, placement: DialogPlacement): { axis: "x" | "y"; sign: 1 | -1 } {
81
+ if (placement === "sheet") return { axis: "y", sign: 1 };
82
+ const rtl = dialog.ownerDocument.defaultView?.getComputedStyle(dialog).direction === "rtl";
83
+ const right = (placement === "end") !== rtl;
84
+ return { axis: "x", sign: right ? 1 : -1 };
85
+ }
86
+
87
+ /**
88
+ * True when the content under the finger would scroll for this drag, so it
89
+ * keeps the touch: a finger moving down on content scrolled down scrolls it
90
+ * back up first, and one moving up scrolls it on while there is more.
91
+ */
92
+ function contentScrolls(path: EventTarget[], dialog: HTMLDialogElement, axis: "x" | "y", along: number): boolean {
93
+ const view = dialog.ownerDocument.defaultView;
94
+ for (const node of path) {
95
+ if (!(node instanceof Element)) continue;
96
+ const style = view?.getComputedStyle(node);
97
+ if (axis === "y") {
98
+ const scrolls = /auto|scroll/.test(style?.overflowY ?? "") && node.scrollHeight > node.clientHeight;
99
+ if (scrolls && (along > 0 ? node.scrollTop > 0 : node.scrollTop + node.clientHeight < node.scrollHeight - 1)) return true;
100
+ } else {
101
+ const scrolls = /auto|scroll/.test(style?.overflowX ?? "") && node.scrollWidth > node.clientWidth;
102
+ if (scrolls) return true;
103
+ }
104
+ if (node === dialog) break;
105
+ }
106
+ return false;
107
+ }
108
+
109
+ /** The dialog's place on screen along its axis, towards its edge, from its live transform. */
110
+ function presented(dialog: HTMLDialogElement, axis: "x" | "y", sign: 1 | -1): number {
111
+ const view = dialog.ownerDocument.defaultView;
112
+ const transform = view?.getComputedStyle(dialog).transform;
113
+ if (!transform || transform === "none" || typeof DOMMatrixReadOnly === "undefined") return 0;
114
+ try {
115
+ const matrix = new DOMMatrixReadOnly(transform);
116
+ return sign * (axis === "y" ? matrix.m42 : matrix.m41);
117
+ } catch {
118
+ return 0;
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Let a finger swipe a sheet or drawer away. Returns the function that
124
+ * unbinds it and clears what it set on the dialog.
125
+ */
126
+ export function bindDialogSwipe(dialog: HTMLDialogElement, options: DialogSwipeOptions = {}): () => void {
127
+ const placementNow = (): DialogPlacement => {
128
+ const given = typeof options.placement === "function" ? options.placement() : options.placement;
129
+ return given ?? dialogPlacement(dialog);
130
+ };
131
+ let gesture: Gesture | null = null;
132
+ let settleTimer = 0;
133
+ const view = dialog.ownerDocument.defaultView;
134
+ const frame = (callback: () => void) => (view?.requestAnimationFrame ? view.requestAnimationFrame(callback) : view?.setTimeout(callback, 16) ?? 0);
135
+
136
+ const write = (offset: number, size: number, sign: 1 | -1) => {
137
+ dialog.style.setProperty("--ds-dialog-drag", `${sign * offset}px`);
138
+ dialog.style.setProperty("--ds-dialog-progress", String(size > 0 ? Math.min(1, Math.max(0, offset / size)) : 0));
139
+ };
140
+ const clear = () => {
141
+ dialog.style.removeProperty("--ds-dialog-drag");
142
+ dialog.style.removeProperty("--ds-dialog-progress");
143
+ dialog.removeAttribute("data-ds-dialog-dragging");
144
+ };
145
+ /** Home: the stylesheet's transition takes it back from where it is. */
146
+ const settle = () => {
147
+ dialog.removeAttribute("data-ds-dialog-dragging");
148
+ dialog.style.setProperty("--ds-dialog-drag", "0px");
149
+ dialog.style.setProperty("--ds-dialog-progress", "0");
150
+ };
151
+
152
+ const dismiss = () => {
153
+ dialog.removeAttribute("data-ds-dialog-dragging");
154
+ if (options.onDismiss) options.onDismiss(dialog);
155
+ else {
156
+ const request = new CustomEvent("ds-close-request", { detail: { reason: "swipe" }, bubbles: true, composed: true, cancelable: true });
157
+ if (dialog.dispatchEvent(request)) dialog.close();
158
+ }
159
+ // A product may close it a moment later (a framework's state); declined, it goes home.
160
+ const token = ++settleTimer;
161
+ frame(() => frame(() => { if (token === settleTimer && dialog.open) settle(); }));
162
+ };
163
+
164
+ const onStart = (event: TouchEvent) => {
165
+ const placement = placementNow();
166
+ if (!dialog.open || placement === "center") return;
167
+ if (event.touches.length !== 1) {
168
+ if (gesture?.mode === "drag") settle();
169
+ gesture = null;
170
+ return;
171
+ }
172
+ const path = event.composedPath();
173
+ if (path.some(node => node instanceof Element && node.matches('[data-ds-dialog-no-swipe], input[type="range"], [role="slider"]'))) return;
174
+ const touch = event.touches[0]!;
175
+ const { axis, sign } = direction(dialog, placement);
176
+ gesture = { id: touch.identifier, x: touch.clientX, y: touch.clientY, base: Math.max(0, presented(dialog, axis, sign)), start: 0, mode: "pending", offset: 0, samples: [], target: event.target, path };
177
+ };
178
+
179
+ const onMove = (event: TouchEvent) => {
180
+ if (!gesture || gesture.mode === "ignore") return;
181
+ const touch = [...event.touches].find(each => each.identifier === gesture!.id);
182
+ if (!touch) return;
183
+ const placement = placementNow();
184
+ if (placement === "center") { gesture = null; return; }
185
+ const { axis, sign } = direction(dialog, placement);
186
+ const dx = touch.clientX - gesture.x, dy = touch.clientY - gesture.y;
187
+ const along = axis === "y" ? dy * sign : dx * sign;
188
+ const across = axis === "y" ? dx : dy;
189
+ if (gesture.mode === "pending") {
190
+ if (Math.hypot(dx, dy) < SLOP) return;
191
+ if (Math.abs(along) <= Math.abs(across) || contentScrolls(gesture.path, dialog, axis, along)) {
192
+ gesture.mode = "ignore";
193
+ return;
194
+ }
195
+ gesture.mode = "drag";
196
+ gesture.start = along;
197
+ settleTimer++;
198
+ dialog.setAttribute("data-ds-dialog-dragging", "");
199
+ }
200
+ event.preventDefault();
201
+ const rect = dialog.getBoundingClientRect();
202
+ const size = axis === "y" ? rect.height : rect.width;
203
+ const raw = gesture.base + along - gesture.start;
204
+ gesture.offset = raw < 0 ? rubberband(raw, size || 1) : raw;
205
+ write(gesture.offset, size, sign);
206
+ const time = event.timeStamp || (view?.performance?.now() ?? Date.now());
207
+ gesture.samples.push({ time, offset: gesture.offset });
208
+ while (gesture.samples.length > 2 && time - gesture.samples[0]!.time > 150) gesture.samples.shift();
209
+ };
210
+
211
+ const onEnd = (event: TouchEvent) => {
212
+ if (!gesture) return;
213
+ if ([...event.touches].some(each => each.identifier === gesture!.id)) return;
214
+ const ended = gesture;
215
+ gesture = null;
216
+ if (ended.mode !== "drag") return;
217
+ const placement = placementNow();
218
+ const { axis } = direction(dialog, placement === "center" ? "sheet" : placement);
219
+ const rect = dialog.getBoundingClientRect();
220
+ const size = axis === "y" ? rect.height : rect.width;
221
+ // The speed of the last tenth of a second: a finger held still before letting go throws nothing.
222
+ const now = event.timeStamp || (view?.performance?.now() ?? Date.now());
223
+ const recent = ended.samples.filter(sample => now - sample.time <= 100);
224
+ const first = recent[0], last = recent[recent.length - 1];
225
+ const elapsed = first && last ? last.time - first.time : 0;
226
+ const velocity = first && last && elapsed > 0 ? ((last.offset - first.offset) / elapsed) * 1000 : 0;
227
+ const resting = ended.offset + project(velocity);
228
+ if (event.type !== "touchcancel" && placement !== "center" && ended.offset > 0 && resting > size / 2 && velocity > -100) dismiss();
229
+ else settle();
230
+ };
231
+
232
+ // Open again: it starts from its edge, with nothing left of the last drag.
233
+ const observer = new MutationObserver(() => { if (dialog.open) { settleTimer++; gesture = null; clear(); } });
234
+ observer.observe(dialog, { attributes: true, attributeFilter: ["open"] });
235
+ dialog.addEventListener("touchstart", onStart, { passive: true });
236
+ dialog.addEventListener("touchmove", onMove, { passive: false });
237
+ dialog.addEventListener("touchend", onEnd, { passive: true });
238
+ dialog.addEventListener("touchcancel", onEnd, { passive: true });
239
+ return () => {
240
+ observer.disconnect();
241
+ dialog.removeEventListener("touchstart", onStart);
242
+ dialog.removeEventListener("touchmove", onMove);
243
+ dialog.removeEventListener("touchend", onEnd);
244
+ dialog.removeEventListener("touchcancel", onEnd);
245
+ settleTimer++;
246
+ gesture = null;
247
+ clear();
248
+ };
249
+ }
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Dialogs that answer with a promise, in place of the browser's `alert()`,
3
+ * `confirm()` and `prompt()`, for pages without Lit or any other framework.
4
+ *
5
+ * Each is drawn on the native `dialog.ds-dialog` frame from `1-root/controls.css`
6
+ * (which the page loads) with its native buttons, `.ds-button--primary`,
7
+ * `--secondary` and `--danger`, and opened with `showModal()`, so the browser
8
+ * keeps the rest of the page inert behind it. Their words default to the
9
+ * shared words (OK, Cancel) in the page's language where the page loads the
10
+ * language module, and English otherwise (`./feedback-words.ts`).
11
+ *
12
+ * - Focus moves into the dialog when it opens, and back to what had it when it
13
+ * closes.
14
+ * - Escape cancels; Enter in a prompt's field confirms it.
15
+ * - A click on the backdrop cancels, except on a destructive confirmation
16
+ * (`danger`), which takes an answer.
17
+ * - Two never open at once: a dialog asked for while another is open waits
18
+ * for it to close.
19
+ * - A dialog asked for in a click opens within that click, so a phone shows
20
+ * its keyboard for a prompt's field.
21
+ */
22
+ import { feedbackWord } from "./feedback-words";
23
+
24
+ export interface AlertOptions {
25
+ title: string;
26
+ text?: string;
27
+ /** The button's words; the shared word "OK" by default. */
28
+ confirm?: string;
29
+ }
30
+
31
+ export interface ConfirmOptions {
32
+ title: string;
33
+ text?: string;
34
+ /** The confirming button's words; the shared word "OK" by default. Name the action when it has one ("Delete"). */
35
+ confirm?: string;
36
+ /** The cancelling button's words; the shared word "Cancel" by default. */
37
+ cancel?: string;
38
+ /** A destructive action: the confirming button in the danger colour, Cancel focused, and no backdrop dismissal. */
39
+ danger?: boolean;
40
+ }
41
+
42
+ export interface PromptOptions {
43
+ title: string;
44
+ text?: string;
45
+ /** The field's label, shown above it; without one, the title names the field. */
46
+ label?: string;
47
+ value?: string;
48
+ placeholder?: string;
49
+ confirm?: string;
50
+ cancel?: string;
51
+ }
52
+
53
+ type Kind = "alert" | "confirm" | "prompt";
54
+ interface Spec {
55
+ kind: Kind;
56
+ title: string;
57
+ text?: string;
58
+ confirm: string;
59
+ cancel?: string;
60
+ danger?: boolean;
61
+ field?: { label?: string; value: string; placeholder?: string };
62
+ }
63
+
64
+ /* One dialog at a time; the next waits in line. */
65
+ const waiting: Array<() => void> = [];
66
+ let open = false;
67
+ let count = 0;
68
+
69
+ function inLine<T>(show: () => Promise<T>): Promise<T> {
70
+ return new Promise<T>((resolve, reject) => {
71
+ const start = () => {
72
+ open = true;
73
+ show().then(resolve, reject).finally(() => {
74
+ open = false;
75
+ waiting.shift()?.();
76
+ });
77
+ };
78
+ if (open) waiting.push(start);
79
+ else start();
80
+ });
81
+ }
82
+
83
+ /** The element with focus, inside open shadow roots too. */
84
+ function focused(): HTMLElement | null {
85
+ let active = document.activeElement;
86
+ while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;
87
+ return active instanceof HTMLElement ? active : null;
88
+ }
89
+
90
+ function element<K extends keyof HTMLElementTagNameMap>(name: K, attributes: Record<string, string> = {}, text?: string): HTMLElementTagNameMap[K] {
91
+ const node = document.createElement(name);
92
+ for (const [key, value] of Object.entries(attributes)) node.setAttribute(key, value);
93
+ if (text !== undefined) node.textContent = text;
94
+ return node;
95
+ }
96
+
97
+ /** Answers `true` for the confirming button, `false` for Cancel, Escape and the backdrop; a prompt answers its text. */
98
+ function show(spec: Spec): Promise<boolean | string | null> {
99
+ return new Promise(resolve => {
100
+ const id = `ds-feedback-${++count}`;
101
+ const opener = focused();
102
+ const dialog = element("dialog", {
103
+ class: "ds-dialog",
104
+ role: spec.kind === "prompt" ? "dialog" : "alertdialog",
105
+ "aria-labelledby": `${id}-title`,
106
+ "data-ds-feedback": spec.kind,
107
+ });
108
+ const header = element("div", { "data-ds-dialog": "header" });
109
+ header.appendChild(element("h2", { id: `${id}-title`, "data-ds-dialog": "title" }, spec.title));
110
+ if (spec.text) {
111
+ header.appendChild(element("p", { id: `${id}-text`, "data-ds-dialog": "description" }, spec.text));
112
+ dialog.setAttribute("aria-describedby", `${id}-text`);
113
+ }
114
+ dialog.appendChild(header);
115
+
116
+ let input: HTMLInputElement | undefined;
117
+ if (spec.field) {
118
+ const field = element("div", { "data-ds-dialog": "field" });
119
+ input = element("input", { id: `${id}-field`, class: "ds-input", type: "text", autocomplete: "off" });
120
+ input.value = spec.field.value;
121
+ if (spec.field.placeholder) input.placeholder = spec.field.placeholder;
122
+ if (spec.field.label) field.appendChild(element("label", { for: input.id, "data-ds-dialog": "label" }, spec.field.label));
123
+ else input.setAttribute("aria-labelledby", `${id}-title`);
124
+ field.appendChild(input);
125
+ dialog.appendChild(field);
126
+ }
127
+
128
+ const footer = element("div", { "data-ds-dialog": "footer" });
129
+ const cancel = spec.cancel === undefined ? undefined : element("button", { type: "button", class: "ds-button ds-button--secondary", value: "cancel" }, spec.cancel);
130
+ const confirm = element("button", { type: "button", class: spec.danger ? "ds-button ds-button--secondary ds-button--danger" : "ds-button ds-button--primary", value: "confirm" }, spec.confirm);
131
+ if (cancel) footer.appendChild(cancel);
132
+ footer.appendChild(confirm);
133
+ dialog.appendChild(footer);
134
+
135
+ const declined = spec.kind === "prompt" ? null : false;
136
+ let settled = false;
137
+ const finish = (confirmed: boolean) => {
138
+ if (settled) return;
139
+ settled = true;
140
+ if (dialog.open) dialog.close();
141
+ dialog.remove();
142
+ if (opener?.isConnected) opener.focus({ preventScroll: true });
143
+ resolve(spec.kind === "alert" ? true : confirmed ? (input ? input.value : true) : declined);
144
+ };
145
+
146
+ confirm.addEventListener("click", () => finish(true));
147
+ cancel?.addEventListener("click", () => finish(false));
148
+ input?.addEventListener("keydown", event => {
149
+ if (event.key !== "Enter" || event.isComposing || event.shiftKey || event.altKey || event.metaKey || event.ctrlKey) return;
150
+ event.preventDefault();
151
+ finish(true);
152
+ });
153
+ dialog.addEventListener("cancel", event => { event.preventDefault(); finish(false); });
154
+ // The browser can still close it (a second Escape): that is a cancel too.
155
+ dialog.addEventListener("close", () => finish(false));
156
+
157
+ // The backdrop is the dialog itself, outside its box; a press must start and end there.
158
+ const outside = (event: MouseEvent) => {
159
+ if (event.target !== dialog) return false;
160
+ const box = dialog.getBoundingClientRect();
161
+ return event.clientX < box.left || event.clientX > box.right || event.clientY < box.top || event.clientY > box.bottom;
162
+ };
163
+ let pressedOutside = false;
164
+ if (!spec.danger) {
165
+ dialog.addEventListener("pointerdown", event => { pressedOutside = event.button === 0 && outside(event); });
166
+ dialog.addEventListener("click", event => {
167
+ if (pressedOutside && outside(event)) finish(false);
168
+ pressedOutside = false;
169
+ });
170
+ }
171
+
172
+ const first = input ?? (spec.danger ? cancel : undefined) ?? confirm;
173
+ first.setAttribute("autofocus", "");
174
+ document.body.appendChild(dialog);
175
+ try {
176
+ dialog.showModal();
177
+ } catch {
178
+ dialog.setAttribute("open", "");
179
+ }
180
+ first.focus();
181
+ input?.select();
182
+ });
183
+ }
184
+
185
+ /** In place of `alert()`: a message with one button. Resolves when it is dismissed. */
186
+ export function alert(options: AlertOptions | string): Promise<void> {
187
+ const { title, text, confirm } = typeof options === "string" ? { title: options } as AlertOptions : options;
188
+ if (typeof document === "undefined") return Promise.resolve();
189
+ return inLine(() => show({ kind: "alert", title, text, confirm: confirm ?? feedbackWord("OK") })).then(() => undefined);
190
+ }
191
+
192
+ /** In place of `confirm()`: resolves to `true` when confirmed, `false` otherwise. */
193
+ export function confirm(options: ConfirmOptions | string): Promise<boolean> {
194
+ const { title, text, confirm: yes, cancel, danger } = typeof options === "string" ? { title: options } as ConfirmOptions : options;
195
+ if (typeof document === "undefined") return Promise.resolve(false);
196
+ return inLine(() => show({ kind: "confirm", title, text, confirm: yes ?? feedbackWord("OK"), cancel: cancel ?? feedbackWord("Cancel"), danger })).then(answer => answer === true);
197
+ }
198
+
199
+ /** In place of `prompt()`: resolves to the text written, or `null` when cancelled. */
200
+ export function prompt(options: PromptOptions | string): Promise<string | null> {
201
+ const { title, text, label, value = "", placeholder, confirm: yes, cancel } = typeof options === "string" ? { title: options } as PromptOptions : options;
202
+ if (typeof document === "undefined") return Promise.resolve(null);
203
+ return inLine(() => show({
204
+ kind: "prompt", title, text,
205
+ confirm: yes ?? feedbackWord("OK"), cancel: cancel ?? feedbackWord("Cancel"),
206
+ field: { label, value, placeholder },
207
+ })).then(answer => typeof answer === "string" ? answer : null);
208
+ }