@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,700 @@
1
+ /**
2
+ * Dates, times, numbers, money and lists written the way the reader's
3
+ * language writes them, through the platform's `Intl` formatters.
4
+ *
5
+ * Every function formats in the current language (`currentLanguage.value`
6
+ * from `./i18n`), read again on every call, so the text drawn after a
7
+ * language change is in the new language. Pass `language` to format in
8
+ * another one: a server answering a request names the request's language,
9
+ * since its current language is nobody's in particular.
10
+ *
11
+ * Each formatter is made once per locale and options, then kept. No browser
12
+ * code: it runs in a Worker, Node or a test as it does in a page.
13
+ */
14
+ import { currentLanguage } from "./i18n";
15
+ import { DEFAULT_LANGUAGE, LANGUAGES, normalizeLanguage } from "./languages";
16
+ import type { Language } from "./languages";
17
+
18
+ // ============================================================================
19
+ // Locales
20
+ // ============================================================================
21
+
22
+ /**
23
+ * The locale each language of the list formats in: the language with the
24
+ * region it is most read in. `pt` is Portugal's Portuguese and `pt-BR`
25
+ * Brazil's; `Intl` alone reads a bare `pt` as Brazil's.
26
+ */
27
+ export const LOCALE_TAGS: Readonly<Record<Language, string>> = {
28
+ da: "da-DK",
29
+ de: "de-DE",
30
+ en: "en-US",
31
+ es: "es-ES",
32
+ fi: "fi-FI",
33
+ fr: "fr-FR",
34
+ is: "is-IS",
35
+ it: "it-IT",
36
+ ja: "ja-JP",
37
+ ko: "ko-KR",
38
+ nb: "nb-NO",
39
+ nn: "nn-NO",
40
+ no: "no-NO",
41
+ pt: "pt-PT",
42
+ "pt-BR": "pt-BR",
43
+ sv: "sv-SE",
44
+ zh: "zh-CN",
45
+ };
46
+
47
+ export type FormatSettings = {
48
+ /**
49
+ * The locale a language formats in, where a product's readers expect
50
+ * another region than `LOCALE_TAGS` gives: `{ en: "en-GB" }` writes
51
+ * English dates day first.
52
+ */
53
+ locales?: Partial<Record<string, string>>;
54
+ /**
55
+ * The time zone dates and times are shown in, unless a call names one.
56
+ * Without it, the runtime's own zone (a Worker's is UTC).
57
+ */
58
+ timeZone?: string;
59
+ };
60
+
61
+ let settings: FormatSettings = {};
62
+
63
+ /** Each language or tag asked for, to the locale it formats in. */
64
+ const resolvedTags = new Map<string, string>();
65
+
66
+ /** Set the locales and time zone every call starts from (see `FormatSettings`). */
67
+ export function configureFormat(next: FormatSettings): void {
68
+ settings = {
69
+ ...settings,
70
+ ...next,
71
+ locales: { ...settings.locales, ...next.locales },
72
+ };
73
+ resolvedTags.clear();
74
+ }
75
+
76
+ function canonical(tag: string): string | null {
77
+ try {
78
+ return Intl.getCanonicalLocales(tag)[0] ?? null;
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+
84
+ function regional(language: Language): string {
85
+ const own = settings.locales?.[language];
86
+ return (own && canonical(own)) || LOCALE_TAGS[language];
87
+ }
88
+
89
+ /**
90
+ * The locale tag a language formats in: `da` gives `da-DK`, `pt` gives
91
+ * `pt-PT`, `pt-br` gives `pt-BR`. A tag with its own region or script keeps
92
+ * it (`en-GB`, `zh-Hant`), and a language outside the list passes through
93
+ * (`nl`). Without an argument, the current language's.
94
+ */
95
+ export function localeTag(language?: string | null): string {
96
+ const tag = String(language || currentLanguage.value || "")
97
+ .trim()
98
+ .replace(/_/g, "-");
99
+ let resolved = resolvedTags.get(tag);
100
+ if (!resolved) {
101
+ resolved = resolveTag(tag);
102
+ // Tags come from a short list; a server reading arbitrary ones starts over
103
+ if (resolvedTags.size >= MAX_FORMATTERS) {
104
+ resolvedTags.clear();
105
+ }
106
+ resolvedTags.set(tag, resolved);
107
+ }
108
+ return resolved;
109
+ }
110
+
111
+ function resolveTag(tag: string): string {
112
+ if (!tag) {
113
+ return regional(DEFAULT_LANGUAGE);
114
+ }
115
+
116
+ const lower = tag.toLowerCase();
117
+ const member = LANGUAGES.find((code) => code.toLowerCase() === lower);
118
+ if (member) {
119
+ return regional(member);
120
+ }
121
+
122
+ // A tag of its own, when the runtime has its conventions (not the
123
+ // runtime's default standing in for one it lacks)
124
+ const written = canonical(tag);
125
+ if (written && supported(written)) {
126
+ return written;
127
+ }
128
+
129
+ // Otherwise the list's language it names, else the default
130
+ const known = normalizeLanguage(tag, null);
131
+ return regional(known ?? DEFAULT_LANGUAGE);
132
+ }
133
+
134
+ function supported(tag: string): boolean {
135
+ try {
136
+ return Intl.NumberFormat.supportedLocalesOf([tag]).length > 0;
137
+ } catch {
138
+ return false;
139
+ }
140
+ }
141
+
142
+ // ============================================================================
143
+ // Kept formatters
144
+ // ============================================================================
145
+
146
+ type WithLanguage = {
147
+ /** The language to format in, instead of the current one: a code of the list or any locale tag. */
148
+ language?: string;
149
+ };
150
+
151
+ const MAX_FORMATTERS = 256;
152
+ const formatters = new Map<string, unknown>();
153
+
154
+ function optionsKey(options: object): string {
155
+ const entries = Object.entries(options).filter(
156
+ ([, value]) => value !== undefined
157
+ );
158
+ entries.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
159
+ return JSON.stringify(entries);
160
+ }
161
+
162
+ function kept<T>(
163
+ kind: string,
164
+ locale: string,
165
+ options: object,
166
+ make: () => T
167
+ ): T {
168
+ const key = `${kind}|${locale}|${optionsKey(options)}`;
169
+ if (formatters.has(key)) {
170
+ return formatters.get(key) as T;
171
+ }
172
+ if (formatters.size >= MAX_FORMATTERS) {
173
+ // The oldest goes first
174
+ const oldest = formatters.keys().next();
175
+ if (!oldest.done) {
176
+ formatters.delete(oldest.value);
177
+ }
178
+ }
179
+ const formatter = make();
180
+ formatters.set(key, formatter);
181
+ return formatter;
182
+ }
183
+
184
+ function split<T extends WithLanguage>(
185
+ options: T
186
+ ): [string, Omit<T, "language">] {
187
+ const { language, ...rest } = options;
188
+ return [localeTag(language), rest];
189
+ }
190
+
191
+ /**
192
+ * The kept `Intl.DateTimeFormat` for any options the named functions do not
193
+ * cover (`{ weekday: "short" }`), in the current language or `language`. The
194
+ * time zone set with `configureFormat` applies unless the options name one.
195
+ */
196
+ export function dateFormatter(
197
+ options: Intl.DateTimeFormatOptions & WithLanguage = {}
198
+ ): Intl.DateTimeFormat {
199
+ const [locale, rest] = split(options);
200
+ const resolved: Intl.DateTimeFormatOptions = {
201
+ ...rest,
202
+ timeZone: rest.timeZone ?? settings.timeZone,
203
+ };
204
+ return kept("date", locale, resolved, () => new Intl.DateTimeFormat(locale, resolved));
205
+ }
206
+
207
+ /** The kept `Intl.NumberFormat` for any options, in the current language or `language`. */
208
+ export function numberFormatter(
209
+ options: Intl.NumberFormatOptions & WithLanguage = {}
210
+ ): Intl.NumberFormat {
211
+ const [locale, rest] = split(options);
212
+ return kept("number", locale, rest, () => new Intl.NumberFormat(locale, rest));
213
+ }
214
+
215
+ /** The kept `Intl.RelativeTimeFormat`, in the current language or `language`. */
216
+ export function relativeTimeFormatter(
217
+ options: Intl.RelativeTimeFormatOptions & WithLanguage = {}
218
+ ): Intl.RelativeTimeFormat {
219
+ const [locale, rest] = split(options);
220
+ return kept(
221
+ "relative",
222
+ locale,
223
+ rest,
224
+ () => new Intl.RelativeTimeFormat(locale, rest)
225
+ );
226
+ }
227
+
228
+ export type ListType = "conjunction" | "disjunction" | "unit";
229
+ export type ListStyle = "long" | "short" | "narrow";
230
+
231
+ /** What `Intl.ListFormat` offers, for runtimes whose types predate it. */
232
+ export type ListFormatter = {
233
+ format(items: Iterable<string>): string;
234
+ };
235
+
236
+ type ListFormatConstructor = new (
237
+ locale: string,
238
+ options: { type?: ListType; style?: ListStyle }
239
+ ) => ListFormatter;
240
+
241
+ /** The kept `Intl.ListFormat`, in the current language or `language`. */
242
+ export function listFormatter(
243
+ options: { type?: ListType; style?: ListStyle } & WithLanguage = {}
244
+ ): ListFormatter {
245
+ const [locale, rest] = split(options);
246
+ return kept("list", locale, rest, () => {
247
+ const ListFormat = (Intl as unknown as { ListFormat?: ListFormatConstructor })
248
+ .ListFormat;
249
+ // A runtime without it joins with commas, as English would
250
+ return ListFormat
251
+ ? new ListFormat(locale, rest)
252
+ : { format: (items: Iterable<string>) => [...items].join(", ") };
253
+ });
254
+ }
255
+
256
+ // ============================================================================
257
+ // Dates and times
258
+ // ============================================================================
259
+
260
+ /**
261
+ * A moment: a `Date`, milliseconds since 1970 or a date string. A date
262
+ * written without a time (`2026-10-17`) is that day wherever it is read.
263
+ */
264
+ export type DateInput = Date | number | string;
265
+
266
+ export type DateLength = "short" | "medium" | "long";
267
+
268
+ export type DateOptions = WithLanguage & {
269
+ /** How much is written: `short` (10/17/26), `medium` (Oct 17, 2026) or `long` (October 17, 2026). */
270
+ length?: DateLength;
271
+ /** The time zone to show it in (`Asia/Tokyo`, `UTC`). */
272
+ timeZone?: string;
273
+ };
274
+
275
+ const DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
276
+
277
+ /** The moment, and whether it is a day without a time. */
278
+ function readDate(value: DateInput): { time: number; day: boolean } {
279
+ if (typeof value === "string") {
280
+ const day = DATE_ONLY.exec(value.trim());
281
+ if (day) {
282
+ return {
283
+ time: Date.UTC(Number(day[1]), Number(day[2]) - 1, Number(day[3])),
284
+ day: true,
285
+ };
286
+ }
287
+ return { time: Date.parse(value), day: false };
288
+ }
289
+ return {
290
+ time: value instanceof Date ? value.getTime() : Number(value),
291
+ day: false,
292
+ };
293
+ }
294
+
295
+ function dateArguments(
296
+ options: DateOptions | DateLength | undefined,
297
+ fallback: DateLength
298
+ ): DateOptions & { length: DateLength } {
299
+ if (typeof options === "string") {
300
+ return { length: options };
301
+ }
302
+ return { ...options, length: options?.length ?? fallback };
303
+ }
304
+
305
+ function formatMoment(
306
+ value: DateInput,
307
+ parts: Intl.DateTimeFormatOptions,
308
+ { language, timeZone }: DateOptions
309
+ ): string {
310
+ const { time, day } = readDate(value);
311
+ if (!Number.isFinite(time)) {
312
+ return "";
313
+ }
314
+ // A day without a time is kept on its own date in every zone
315
+ const zone = day ? "UTC" : timeZone;
316
+ return dateFormatter({ ...parts, timeZone: zone, language }).format(time);
317
+ }
318
+
319
+ /** A date: `Oct 17, 2026` (medium, the default), `10/17/26` (short), `October 17, 2026` (long). */
320
+ export function formatDate(
321
+ value: DateInput,
322
+ options?: DateOptions | DateLength
323
+ ): string {
324
+ const { length, ...rest } = dateArguments(options, "medium");
325
+ return formatMoment(value, { dateStyle: length }, rest);
326
+ }
327
+
328
+ /** A time of day: `2:30 PM` (short, the default), `2:30:05 PM` (medium), `2:30:05 PM UTC` (long). */
329
+ export function formatTime(
330
+ value: DateInput,
331
+ options?: DateOptions | DateLength
332
+ ): string {
333
+ const { length, ...rest } = dateArguments(options, "short");
334
+ return formatMoment(value, { timeStyle: length }, rest);
335
+ }
336
+
337
+ /** A date at a time: the date at `length` (medium by default), the time to the minute. */
338
+ export function formatDateTime(
339
+ value: DateInput,
340
+ options?: DateOptions | DateLength
341
+ ): string {
342
+ const { length, ...rest } = dateArguments(options, "medium");
343
+ return formatMoment(value, { dateStyle: length, timeStyle: "short" }, rest);
344
+ }
345
+
346
+ const MONTH: Record<DateLength, "numeric" | "short" | "long"> = {
347
+ short: "numeric",
348
+ medium: "short",
349
+ long: "long",
350
+ };
351
+
352
+ /** A month of a year: `October 2026` (long, the default), `Oct 2026` (medium), `10/2026` (short). */
353
+ export function formatMonthYear(
354
+ value: DateInput,
355
+ options?: DateOptions | DateLength
356
+ ): string {
357
+ const { length, ...rest } = dateArguments(options, "long");
358
+ return formatMoment(value, { year: "numeric", month: MONTH[length] }, rest);
359
+ }
360
+
361
+ /** A day of a month, without the year: `Oct 17` (medium, the default), `10/17` (short), `October 17` (long). */
362
+ export function formatMonthDay(
363
+ value: DateInput,
364
+ options?: DateOptions | DateLength
365
+ ): string {
366
+ const { length, ...rest } = dateArguments(options, "medium");
367
+ return formatMoment(value, { month: MONTH[length], day: "numeric" }, rest);
368
+ }
369
+
370
+ /**
371
+ * A day of a month at a time, without the year: `Oct 17, 2:30 PM` (medium,
372
+ * the default), `10/17, 2:30 PM` (short), `October 17 at 2:30 PM` (long).
373
+ */
374
+ export function formatMonthDayTime(
375
+ value: DateInput,
376
+ options?: DateOptions | DateLength
377
+ ): string {
378
+ const { length, ...rest } = dateArguments(options, "medium");
379
+ return formatMoment(
380
+ value,
381
+ { month: MONTH[length], day: "numeric", hour: "numeric", minute: "2-digit" },
382
+ rest
383
+ );
384
+ }
385
+
386
+ // ============================================================================
387
+ // Relative time
388
+ // ============================================================================
389
+
390
+ export type RelativeUnit =
391
+ | "second"
392
+ | "minute"
393
+ | "hour"
394
+ | "day"
395
+ | "week"
396
+ | "month"
397
+ | "quarter"
398
+ | "year";
399
+
400
+ export type RelativeTimeOptions = WithLanguage & {
401
+ /** The moment it is measured from. Defaults to now. */
402
+ now?: DateInput;
403
+ /** Always count in this unit, rather than the one that reads best. */
404
+ unit?: RelativeUnit;
405
+ /** `auto` (the default) writes "yesterday" and "now"; `always` writes "1 day ago". */
406
+ numeric?: "auto" | "always";
407
+ /** `long` (the default), `short` ("3 min. ago") or `narrow`. */
408
+ style?: "long" | "short" | "narrow";
409
+ /** The zone whose calendar decides what "yesterday" is. Defaults as dates do. */
410
+ timeZone?: string;
411
+ };
412
+
413
+ const SECOND = 1000;
414
+ const MINUTE = 60 * SECOND;
415
+ const HOUR = 60 * MINUTE;
416
+ const DAY = 24 * HOUR;
417
+ const MONTH_DAYS = 30.436875;
418
+ const YEAR_DAYS = 365.2425;
419
+
420
+ const UNIT_LENGTH: Record<RelativeUnit, number> = {
421
+ second: SECOND,
422
+ minute: MINUTE,
423
+ hour: HOUR,
424
+ day: DAY,
425
+ week: 7 * DAY,
426
+ month: MONTH_DAYS * DAY,
427
+ quarter: 3 * MONTH_DAYS * DAY,
428
+ year: YEAR_DAYS * DAY,
429
+ };
430
+
431
+ /** The calendar day a moment falls on in a zone, counted from 1970. */
432
+ function dayNumber(time: number, timeZone: string | undefined): number {
433
+ if (!timeZone) {
434
+ const date = new Date(time);
435
+ return Math.round(
436
+ Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) / DAY
437
+ );
438
+ }
439
+ const parts = kept(
440
+ "calendar",
441
+ "en-US",
442
+ { timeZone },
443
+ () =>
444
+ new Intl.DateTimeFormat("en-US-u-ca-gregory-nu-latn", {
445
+ timeZone,
446
+ year: "numeric",
447
+ month: "numeric",
448
+ day: "numeric",
449
+ })
450
+ ).formatToParts(time);
451
+ const part = (type: string) =>
452
+ Number(parts.find((entry) => entry.type === type)?.value);
453
+ return Math.round(Date.UTC(part("year"), part("month") - 1, part("day")) / DAY);
454
+ }
455
+
456
+ /**
457
+ * The count and unit a moment reads best in, from `now`: under 45 seconds is
458
+ * now, under 45 minutes counts minutes, under 22 hours (or the same day)
459
+ * hours, under a week calendar days (so "yesterday" is the day before
460
+ * today), under 26 days weeks, under 335 days months, then years. A day
461
+ * without a time (`2026-10-17`) counts days from today. Negative counts are
462
+ * in the past.
463
+ */
464
+ export function relativeTimeUnit(
465
+ value: DateInput,
466
+ now: DateInput = Date.now(),
467
+ timeZone: string | undefined = settings.timeZone
468
+ ): [number, RelativeUnit] {
469
+ const { time: at, day } = readDate(value);
470
+ const from = readDate(now).time;
471
+ const elapsed = at - from;
472
+ const size = Math.abs(elapsed);
473
+ const sign = elapsed < 0 ? -1 : 1;
474
+
475
+ if (day) {
476
+ // A day without a time is counted in days from today: "today", "yesterday"
477
+ const days = Math.round(at / DAY) - dayNumber(from, timeZone);
478
+ return days === 0 ? [0, "day"] : inDays(days);
479
+ }
480
+ if (size < 45 * SECOND) {
481
+ // -0 keeps the past's wording where a count is written ("0 seconds ago")
482
+ return [elapsed < 0 ? -0 : 0, "second"];
483
+ }
484
+ if (size < 45 * MINUTE) {
485
+ return [sign * Math.max(1, Math.round(size / MINUTE)), "minute"];
486
+ }
487
+ const days = dayNumber(at, timeZone) - dayNumber(from, timeZone);
488
+ if (size < 22 * HOUR || days === 0) {
489
+ return [sign * Math.max(1, Math.round(size / HOUR)), "hour"];
490
+ }
491
+ return inDays(days);
492
+ }
493
+
494
+ /** A count of calendar days (never 0) in the unit it reads best in. */
495
+ function inDays(days: number): [number, RelativeUnit] {
496
+ const whole = Math.abs(days);
497
+ const sign = days < 0 ? -1 : 1;
498
+ if (whole < 7) {
499
+ return [days, "day"];
500
+ }
501
+ if (whole < 26) {
502
+ return [sign * Math.round(whole / 7), "week"];
503
+ }
504
+ if (whole < 335) {
505
+ return [sign * Math.max(1, Math.round(whole / MONTH_DAYS)), "month"];
506
+ }
507
+ return [sign * Math.max(1, Math.round(whole / YEAR_DAYS)), "year"];
508
+ }
509
+
510
+ /**
511
+ * How long ago or how far ahead a moment is: "3 minutes ago", "yesterday",
512
+ * "in 2 days", "now", in the unit that reads best (see `relativeTimeUnit`)
513
+ * or the one given.
514
+ */
515
+ export function formatRelativeTime(
516
+ value: DateInput,
517
+ options: RelativeTimeOptions = {}
518
+ ): string {
519
+ const { now = Date.now(), unit, numeric = "auto", style = "long", language } =
520
+ options;
521
+ const timeZone = options.timeZone ?? settings.timeZone;
522
+ const at = readDate(value).time;
523
+ const from = readDate(now).time;
524
+ if (!Number.isFinite(at) || !Number.isFinite(from)) {
525
+ return "";
526
+ }
527
+ const [count, chosen] = unit
528
+ ? [Math.round((at - from) / UNIT_LENGTH[unit]), unit]
529
+ : relativeTimeUnit(value, from, timeZone);
530
+ return relativeTimeFormatter({ numeric, style, language }).format(count, chosen);
531
+ }
532
+
533
+ // ============================================================================
534
+ // Numbers
535
+ // ============================================================================
536
+
537
+ export type NumberOptions = WithLanguage & Intl.NumberFormatOptions;
538
+
539
+ /** A number with the language's separators: `1,234.5`, `1.234,5`. Any `Intl.NumberFormat` option applies. */
540
+ export function formatNumber(value: number | bigint, options: NumberOptions = {}): string {
541
+ return numberFormatter(options).format(value);
542
+ }
543
+
544
+ /** A number in short: `12K`, `1.2M`, `1,2 mio.`, `1.2万`, to one decimal unless the options say otherwise. */
545
+ export function formatCompact(value: number | bigint, options: NumberOptions = {}): string {
546
+ return numberFormatter({
547
+ notation: "compact",
548
+ maximumFractionDigits: 1,
549
+ ...options,
550
+ }).format(value);
551
+ }
552
+
553
+ /** A fraction as a percentage: `0.25` gives `25%` (`25 %` in Danish). */
554
+ export function formatPercent(value: number, options: NumberOptions = {}): string {
555
+ return numberFormatter({ style: "percent", ...options }).format(value);
556
+ }
557
+
558
+ // ============================================================================
559
+ // Money
560
+ // ============================================================================
561
+
562
+ export type CurrencyDisplay = "symbol" | "narrowSymbol" | "code" | "name";
563
+
564
+ export type MoneyOptions = WithLanguage &
565
+ Omit<Intl.NumberFormatOptions, "style" | "currency" | "currencyDisplay"> & {
566
+ /**
567
+ * The amount is counted in the currency's minor unit, as payment
568
+ * services send it: 1999 is $19.99, and 1999 yen are 1999 yen.
569
+ */
570
+ minor?: boolean;
571
+ /** The currency as its symbol (`$`, the default), its narrow symbol, its code (`USD`) or its name. */
572
+ display?: CurrencyDisplay;
573
+ /** Whole units only, rounded: `$20`. */
574
+ whole?: boolean;
575
+ };
576
+
577
+ function currencyCode(currency: string): string {
578
+ return String(currency ?? "").trim().toUpperCase();
579
+ }
580
+
581
+ /**
582
+ * How many decimals a currency's minor unit has, as ISO 4217 counts them:
583
+ * 2 for USD, EUR and DKK, 0 for JPY and KRW, 3 for BHD. 2 for a code the
584
+ * runtime does not know.
585
+ */
586
+ export function currencyDigits(currency: string): number {
587
+ const code = currencyCode(currency);
588
+ try {
589
+ return kept(
590
+ "digits",
591
+ "en",
592
+ { currency: code },
593
+ () => new Intl.NumberFormat("en", { style: "currency", currency: code })
594
+ ).resolvedOptions().maximumFractionDigits ?? 2;
595
+ } catch {
596
+ return 2;
597
+ }
598
+ }
599
+
600
+ /** An amount in minor units as whole units: 1999 cents are 19.99 dollars; yen stay as they are. */
601
+ export function fromMinorUnits(amount: number, currency: string): number {
602
+ return amount / 10 ** currencyDigits(currency);
603
+ }
604
+
605
+ /** An amount in whole units as minor units, rounded: 19.99 dollars are 1999 cents. */
606
+ export function toMinorUnits(amount: number, currency: string): number {
607
+ return Math.round(amount * 10 ** currencyDigits(currency));
608
+ }
609
+
610
+ function moneyFormatter(
611
+ currency: string,
612
+ options: MoneyOptions
613
+ ): { format: Intl.NumberFormat | null; value: (amount: number) => number; code: string; digits: number } {
614
+ const { minor, display = "symbol", whole, language, ...rest } = options;
615
+ const code = currencyCode(currency);
616
+ const digits = currencyDigits(code);
617
+ const value = (amount: number) => (minor ? amount / 10 ** digits : amount);
618
+ try {
619
+ const format = numberFormatter({
620
+ ...rest,
621
+ ...(whole ? { minimumFractionDigits: 0, maximumFractionDigits: 0 } : {}),
622
+ style: "currency",
623
+ currency: code,
624
+ currencyDisplay: display,
625
+ language,
626
+ });
627
+ return { format, value, code, digits };
628
+ } catch {
629
+ return { format: null, value, code, digits };
630
+ }
631
+ }
632
+
633
+ /**
634
+ * An amount of money in the language's way: `$1,234.50`, `1.234,50 kr.`,
635
+ * `¥1,235`. The currency decides the decimals (none for yen). With `minor`,
636
+ * the amount is in cents, øre or the like. A currency code that is not one
637
+ * (not three letters) is written after the number (`12.00 US`) rather than
638
+ * throwing.
639
+ */
640
+ export function formatMoney(
641
+ amount: number,
642
+ currency: string,
643
+ options: MoneyOptions = {}
644
+ ): string {
645
+ const { format, value, code, digits } = moneyFormatter(currency, options);
646
+ if (format) {
647
+ return format.format(value(amount));
648
+ }
649
+ const places = options.whole ? 0 : digits;
650
+ return `${formatNumber(value(amount), {
651
+ minimumFractionDigits: places,
652
+ maximumFractionDigits: places,
653
+ language: options.language,
654
+ })} ${code}`;
655
+ }
656
+
657
+ /**
658
+ * The parts of `formatMoney`'s text, as `Intl.NumberFormat.formatToParts`
659
+ * gives them (`currency`, `integer`, `group`, `decimal`, `fraction`, …), for
660
+ * drawing digits apart from the rest, each in a cell of its own width.
661
+ */
662
+ export function formatMoneyToParts(
663
+ amount: number,
664
+ currency: string,
665
+ options: MoneyOptions = {}
666
+ ): Intl.NumberFormatPart[] {
667
+ const { format, value } = moneyFormatter(currency, options);
668
+ if (format) {
669
+ return format.formatToParts(value(amount));
670
+ }
671
+ return [{ type: "literal", value: formatMoney(amount, currency, options) }];
672
+ }
673
+
674
+ /** The symbol the language writes a currency with: `$`, `kr.`, `€`, `US$` in British English. */
675
+ export function currencySymbol(
676
+ currency: string,
677
+ options: WithLanguage & { display?: "symbol" | "narrowSymbol" } = {}
678
+ ): string {
679
+ const parts = formatMoneyToParts(0, currency, options);
680
+ return parts.find((part) => part.type === "currency")?.value ?? currencyCode(currency);
681
+ }
682
+
683
+ // ============================================================================
684
+ // Lists
685
+ // ============================================================================
686
+
687
+ export type ListOptions = WithLanguage & {
688
+ /** `conjunction` (the default) joins with "and", `disjunction` with "or", `unit` with neither. */
689
+ type?: ListType;
690
+ /** `long` (the default), `short` or `narrow`. */
691
+ style?: ListStyle;
692
+ };
693
+
694
+ /** Items joined the language's way: `A, B, and C`, `A, B og C`, `A、B、C`. */
695
+ export function formatList(items: Iterable<string>, options: ListOptions = {}): string {
696
+ const { type = "conjunction", style = "long", language } = options;
697
+ return listFormatter({ type, style, language }).format(
698
+ Array.from(items, (item) => String(item))
699
+ );
700
+ }