@medalsocial/booking 0.0.0-placeholder.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +6 -0
  4. package/README.md +220 -1
  5. package/dist/core/age.d.mts +34 -0
  6. package/dist/core/age.mjs +95 -0
  7. package/dist/core/age.mjs.map +1 -0
  8. package/dist/core/attempt-store.d.mts +111 -0
  9. package/dist/core/attempt-store.mjs +159 -0
  10. package/dist/core/attempt-store.mjs.map +1 -0
  11. package/dist/core/categories.d.mts +27 -0
  12. package/dist/core/categories.mjs +42 -0
  13. package/dist/core/categories.mjs.map +1 -0
  14. package/dist/core/clock.d.mts +33 -0
  15. package/dist/core/clock.mjs +251 -0
  16. package/dist/core/clock.mjs.map +1 -0
  17. package/dist/core/config.d.mts +178 -0
  18. package/dist/core/config.mjs +394 -0
  19. package/dist/core/config.mjs.map +1 -0
  20. package/dist/core/consent.d.mts +14 -0
  21. package/dist/core/consent.mjs +17 -0
  22. package/dist/core/consent.mjs.map +1 -0
  23. package/dist/core/deep-link.d.mts +40 -0
  24. package/dist/core/deep-link.mjs +160 -0
  25. package/dist/core/deep-link.mjs.map +1 -0
  26. package/dist/core/display-name.d.mts +18 -0
  27. package/dist/core/display-name.mjs +30 -0
  28. package/dist/core/display-name.mjs.map +1 -0
  29. package/dist/core/draft-store.d.mts +89 -0
  30. package/dist/core/draft-store.mjs +131 -0
  31. package/dist/core/draft-store.mjs.map +1 -0
  32. package/dist/core/dto.d.mts +46 -0
  33. package/dist/core/dto.mjs +157 -0
  34. package/dist/core/dto.mjs.map +1 -0
  35. package/dist/core/ics.d.mts +63 -0
  36. package/dist/core/ics.mjs +130 -0
  37. package/dist/core/ics.mjs.map +1 -0
  38. package/dist/core/index.d.mts +35 -0
  39. package/dist/core/index.mjs +32 -0
  40. package/dist/core/labels.d.mts +88 -0
  41. package/dist/core/labels.mjs +93 -0
  42. package/dist/core/labels.mjs.map +1 -0
  43. package/dist/core/machine.d.mts +645 -0
  44. package/dist/core/machine.mjs +988 -0
  45. package/dist/core/machine.mjs.map +1 -0
  46. package/dist/core/money.d.mts +12 -0
  47. package/dist/core/money.mjs +51 -0
  48. package/dist/core/money.mjs.map +1 -0
  49. package/dist/core/next-available.d.mts +20 -0
  50. package/dist/core/next-available.mjs +36 -0
  51. package/dist/core/next-available.mjs.map +1 -0
  52. package/dist/core/next-free.d.mts +25 -0
  53. package/dist/core/next-free.mjs +44 -0
  54. package/dist/core/next-free.mjs.map +1 -0
  55. package/dist/core/party-slots.d.mts +79 -0
  56. package/dist/core/party-slots.mjs +229 -0
  57. package/dist/core/party-slots.mjs.map +1 -0
  58. package/dist/core/paths.d.mts +11 -0
  59. package/dist/core/paths.mjs +56 -0
  60. package/dist/core/paths.mjs.map +1 -0
  61. package/dist/core/phone.d.mts +17 -0
  62. package/dist/core/phone.mjs +58 -0
  63. package/dist/core/phone.mjs.map +1 -0
  64. package/dist/core/portal/cookies.d.mts +29 -0
  65. package/dist/core/portal/dto.d.mts +140 -0
  66. package/dist/core/portal/dto.mjs +142 -0
  67. package/dist/core/portal/dto.mjs.map +1 -0
  68. package/dist/core/portal/family.d.mts +39 -0
  69. package/dist/core/portal/family.mjs +39 -0
  70. package/dist/core/portal/family.mjs.map +1 -0
  71. package/dist/core/portal/login-input.d.mts +29 -0
  72. package/dist/core/portal/login-input.mjs +29 -0
  73. package/dist/core/portal/login-input.mjs.map +1 -0
  74. package/dist/core/portal/next-path.d.mts +12 -0
  75. package/dist/core/portal/next-path.mjs +98 -0
  76. package/dist/core/portal/next-path.mjs.map +1 -0
  77. package/dist/core/portal/return-path.d.mts +7 -0
  78. package/dist/core/portal/return-path.mjs +49 -0
  79. package/dist/core/portal/return-path.mjs.map +1 -0
  80. package/dist/core/portal/session-cookie.d.mts +27 -0
  81. package/dist/core/portal/session-cookie.mjs +30 -0
  82. package/dist/core/portal/session-cookie.mjs.map +1 -0
  83. package/dist/core/portal/vipps-link.d.mts +56 -0
  84. package/dist/core/portal/vipps-link.mjs +136 -0
  85. package/dist/core/portal/vipps-link.mjs.map +1 -0
  86. package/dist/core/portal/vipps-return.d.mts +39 -0
  87. package/dist/core/portal/vipps-return.mjs +69 -0
  88. package/dist/core/portal/vipps-return.mjs.map +1 -0
  89. package/dist/core/rebook-store.d.mts +28 -0
  90. package/dist/core/rebook-store.mjs +60 -0
  91. package/dist/core/rebook-store.mjs.map +1 -0
  92. package/dist/core/restore-gate.d.mts +40 -0
  93. package/dist/core/restore-gate.mjs +96 -0
  94. package/dist/core/restore-gate.mjs.map +1 -0
  95. package/dist/core/stores.d.mts +8 -0
  96. package/dist/core/stores.mjs +23 -0
  97. package/dist/core/stores.mjs.map +1 -0
  98. package/dist/core/types.d.mts +245 -0
  99. package/dist/core/visit.d.mts +26 -0
  100. package/dist/core/visit.mjs +40 -0
  101. package/dist/core/visit.mjs.map +1 -0
  102. package/dist/core/wire.d.mts +37 -0
  103. package/dist/next/cache/memory.d.mts +14 -0
  104. package/dist/next/cache/memory.mjs +51 -0
  105. package/dist/next/cache/memory.mjs.map +1 -0
  106. package/dist/next/cache/next-data.d.mts +6 -0
  107. package/dist/next/cache/next-data.mjs +35 -0
  108. package/dist/next/cache/next-data.mjs.map +1 -0
  109. package/dist/next/cache/noop.d.mts +6 -0
  110. package/dist/next/cache/noop.mjs +15 -0
  111. package/dist/next/cache/noop.mjs.map +1 -0
  112. package/dist/next/cache/workers.d.mts +29 -0
  113. package/dist/next/cache/workers.mjs +74 -0
  114. package/dist/next/cache/workers.mjs.map +1 -0
  115. package/dist/next/catalogue.d.mts +38 -0
  116. package/dist/next/catalogue.mjs +215 -0
  117. package/dist/next/catalogue.mjs.map +1 -0
  118. package/dist/next/handler.d.mts +11 -0
  119. package/dist/next/handler.mjs +150 -0
  120. package/dist/next/handler.mjs.map +1 -0
  121. package/dist/next/index.d.mts +17 -0
  122. package/dist/next/index.mjs +14 -0
  123. package/dist/next/loaders.d.mts +79 -0
  124. package/dist/next/loaders.mjs +195 -0
  125. package/dist/next/loaders.mjs.map +1 -0
  126. package/dist/next/manage-dto.d.mts +6 -0
  127. package/dist/next/manage-dto.mjs +28 -0
  128. package/dist/next/manage-dto.mjs.map +1 -0
  129. package/dist/next/medal.d.mts +105 -0
  130. package/dist/next/medal.mjs +155 -0
  131. package/dist/next/medal.mjs.map +1 -0
  132. package/dist/next/options.d.mts +158 -0
  133. package/dist/next/options.mjs +65 -0
  134. package/dist/next/options.mjs.map +1 -0
  135. package/dist/next/portal/guardian.d.mts +42 -0
  136. package/dist/next/portal/guardian.mjs +76 -0
  137. package/dist/next/portal/guardian.mjs.map +1 -0
  138. package/dist/next/portal/medal-portal.d.mts +161 -0
  139. package/dist/next/portal/medal-portal.mjs +825 -0
  140. package/dist/next/portal/medal-portal.mjs.map +1 -0
  141. package/dist/next/portal/session.d.mts +38 -0
  142. package/dist/next/portal/session.mjs +40 -0
  143. package/dist/next/portal/session.mjs.map +1 -0
  144. package/dist/next/portal/vipps-flash.d.mts +14 -0
  145. package/dist/next/portal/vipps-flash.mjs +41 -0
  146. package/dist/next/portal/vipps-flash.mjs.map +1 -0
  147. package/dist/next/portal.d.mts +205 -0
  148. package/dist/next/portal.mjs +469 -0
  149. package/dist/next/portal.mjs.map +1 -0
  150. package/dist/next/request.d.mts +51 -0
  151. package/dist/next/request.mjs +123 -0
  152. package/dist/next/request.mjs.map +1 -0
  153. package/dist/next/routes/catalogue-routes.mjs +344 -0
  154. package/dist/next/routes/catalogue-routes.mjs.map +1 -0
  155. package/dist/next/routes/create.mjs +460 -0
  156. package/dist/next/routes/create.mjs.map +1 -0
  157. package/dist/next/routes/manage.mjs +191 -0
  158. package/dist/next/routes/manage.mjs.map +1 -0
  159. package/dist/next/routes/portal-routes.mjs +312 -0
  160. package/dist/next/routes/portal-routes.mjs.map +1 -0
  161. package/dist/next/routes/shared.mjs +162 -0
  162. package/dist/next/routes/shared.mjs.map +1 -0
  163. package/dist/next/routes/vipps-return.mjs +166 -0
  164. package/dist/next/routes/vipps-return.mjs.map +1 -0
  165. package/dist/next/runtime.d.mts +49 -0
  166. package/dist/next/runtime.mjs +94 -0
  167. package/dist/next/runtime.mjs.map +1 -0
  168. package/dist/next/seed.d.mts +71 -0
  169. package/dist/next/seed.mjs +495 -0
  170. package/dist/next/seed.mjs.map +1 -0
  171. package/dist/next/server.d.mts +26 -0
  172. package/dist/next/server.mjs +63 -0
  173. package/dist/next/server.mjs.map +1 -0
  174. package/dist/react/BookingLink.d.mts +61 -0
  175. package/dist/react/BookingLink.mjs +102 -0
  176. package/dist/react/BookingLink.mjs.map +1 -0
  177. package/dist/react/BookingWizard.d.mts +49 -0
  178. package/dist/react/BookingWizard.mjs +598 -0
  179. package/dist/react/BookingWizard.mjs.map +1 -0
  180. package/dist/react/LoginPageQuery.d.mts +37 -0
  181. package/dist/react/LoginPageQuery.mjs +106 -0
  182. package/dist/react/LoginPageQuery.mjs.map +1 -0
  183. package/dist/react/LoginSheet.d.mts +49 -0
  184. package/dist/react/LoginSheet.mjs +98 -0
  185. package/dist/react/LoginSheet.mjs.map +1 -0
  186. package/dist/react/ManageBooking.d.mts +50 -0
  187. package/dist/react/ManageBooking.mjs +197 -0
  188. package/dist/react/ManageBooking.mjs.map +1 -0
  189. package/dist/react/PortalDashboard.d.mts +10 -0
  190. package/dist/react/Provider.d.mts +82 -0
  191. package/dist/react/Provider.mjs +132 -0
  192. package/dist/react/Provider.mjs.map +1 -0
  193. package/dist/react/actions.d.mts +133 -0
  194. package/dist/react/actions.mjs +90 -0
  195. package/dist/react/actions.mjs.map +1 -0
  196. package/dist/react/index.d.mts +28 -0
  197. package/dist/react/index.mjs +20 -0
  198. package/dist/react/kit.d.mts +54 -0
  199. package/dist/react/kit.mjs +127 -0
  200. package/dist/react/kit.mjs.map +1 -0
  201. package/dist/react/labels/index.d.mts +23 -0
  202. package/dist/react/labels/index.mjs +44 -0
  203. package/dist/react/labels/index.mjs.map +1 -0
  204. package/dist/react/labels/login.d.mts +10 -0
  205. package/dist/react/labels/login.mjs +13 -0
  206. package/dist/react/labels/login.mjs.map +1 -0
  207. package/dist/react/labels/manage.d.mts +15 -0
  208. package/dist/react/labels/manage.mjs +19 -0
  209. package/dist/react/labels/manage.mjs.map +1 -0
  210. package/dist/react/labels/portal.d.mts +25 -0
  211. package/dist/react/labels/portal.mjs +41 -0
  212. package/dist/react/labels/portal.mjs.map +1 -0
  213. package/dist/react/labels/screens.d.mts +11 -0
  214. package/dist/react/labels/screens.mjs +721 -0
  215. package/dist/react/labels/screens.mjs.map +1 -0
  216. package/dist/react/labels/wizard.d.mts +76 -0
  217. package/dist/react/labels/wizard.mjs +97 -0
  218. package/dist/react/labels/wizard.mjs.map +1 -0
  219. package/dist/react/link/index.d.mts +5 -0
  220. package/dist/react/link/index.mjs +5 -0
  221. package/dist/react/portal/SessionTouch.d.mts +19 -0
  222. package/dist/react/portal/SessionTouch.mjs +40 -0
  223. package/dist/react/portal/SessionTouch.mjs.map +1 -0
  224. package/dist/react/portal/age-prompt.d.mts +10 -0
  225. package/dist/react/portal/age-prompt.mjs +21 -0
  226. package/dist/react/portal/age-prompt.mjs.map +1 -0
  227. package/dist/react/portal/book-again.d.mts +18 -0
  228. package/dist/react/portal/book-again.mjs +21 -0
  229. package/dist/react/portal/book-again.mjs.map +1 -0
  230. package/dist/react/portal/child-summary.d.mts +17 -0
  231. package/dist/react/portal/child-summary.mjs +45 -0
  232. package/dist/react/portal/child-summary.mjs.map +1 -0
  233. package/dist/react/portal/dashboard.d.mts +74 -0
  234. package/dist/react/portal/dashboard.mjs +204 -0
  235. package/dist/react/portal/dashboard.mjs.map +1 -0
  236. package/dist/react/portal/leave.d.mts +17 -0
  237. package/dist/react/portal/leave.mjs +21 -0
  238. package/dist/react/portal/leave.mjs.map +1 -0
  239. package/dist/react/portal/sections.d.mts +12 -0
  240. package/dist/react/portal/sections.mjs +48 -0
  241. package/dist/react/portal/sections.mjs.map +1 -0
  242. package/dist/react/portal/tabs.d.mts +23 -0
  243. package/dist/react/portal/tabs.mjs +36 -0
  244. package/dist/react/portal/tabs.mjs.map +1 -0
  245. package/dist/react/portal/vipps-flash.d.mts +11 -0
  246. package/dist/react/portal/vipps-flash.mjs +18 -0
  247. package/dist/react/portal/vipps-flash.mjs.map +1 -0
  248. package/dist/react/portal/wired.mjs +202 -0
  249. package/dist/react/portal/wired.mjs.map +1 -0
  250. package/dist/react/screen-labels.mjs +14 -0
  251. package/dist/react/screen-labels.mjs.map +1 -0
  252. package/dist/react/shared.d.mts +13 -0
  253. package/dist/react/shared.mjs +7 -0
  254. package/dist/react/use-next-free.d.mts +19 -0
  255. package/dist/react/use-next-free.mjs +56 -0
  256. package/dist/react/use-next-free.mjs.map +1 -0
  257. package/dist/react/useBooking.d.mts +179 -0
  258. package/dist/react/useBooking.mjs +1331 -0
  259. package/dist/react/useBooking.mjs.map +1 -0
  260. package/dist/react/wizard/adapters.mjs +189 -0
  261. package/dist/react/wizard/adapters.mjs.map +1 -0
  262. package/dist/react/wizard/index.d.mts +11 -0
  263. package/dist/react/wizard/index.mjs +6 -0
  264. package/dist/react/wizard-error.mjs +29 -0
  265. package/dist/react/wizard-error.mjs.map +1 -0
  266. package/package.json +126 -5
@@ -0,0 +1,93 @@
1
+ //#region src/core/labels.ts
2
+ /** Whether `value` is a `BookingLabel`: a string, or an array of strings. */
3
+ function isBookingLabel(value) {
4
+ return typeof value === "string" || Array.isArray(value) && value.every((piece) => typeof piece === "string");
5
+ }
6
+ /** The built-in packs, by language subtag. */
7
+ const LABEL_PACKS = {
8
+ nb: {
9
+ "clock.today": "i dag",
10
+ "clock.tomorrow": "i morgen",
11
+ "clock.todayChip": "I dag",
12
+ "clock.tomorrowChip": "I morgen",
13
+ "clock.when": "{day} {time}",
14
+ "clock.dateTime": "{date} kl. {time}",
15
+ "age.exact": "{min} år",
16
+ "age.range": "{min}–{max} år",
17
+ "summary.firstAvailable": "Første ledige",
18
+ "summary.pickTime": "Velg tid",
19
+ "summary.services": "{count} tjenester",
20
+ "summary.totalMinutes": "{minutes} min totalt",
21
+ "people.children": "{count} barn",
22
+ "people.self": "Meg selv",
23
+ "people.adult": "Voksen",
24
+ "daypart.formiddag": "Formiddag",
25
+ "daypart.ettermiddag": "Ettermiddag",
26
+ "daypart.kveld": "Kveld"
27
+ },
28
+ en: {
29
+ "clock.today": "today",
30
+ "clock.tomorrow": "tomorrow",
31
+ "clock.todayChip": "Today",
32
+ "clock.tomorrowChip": "Tomorrow",
33
+ "clock.when": "{day} {time}",
34
+ "clock.dateTime": "{date} at {time}",
35
+ "age.exact": "{min} yrs",
36
+ "age.range": "{min}–{max} yrs",
37
+ "summary.firstAvailable": "First available",
38
+ "summary.pickTime": "Pick a time",
39
+ "summary.services": "{count} services",
40
+ "summary.totalMinutes": "{minutes} min in total",
41
+ "people.children": "{count} children",
42
+ "people.self": "Me",
43
+ "people.adult": "Adult",
44
+ "daypart.morning": "Morning",
45
+ "daypart.afternoon": "Afternoon",
46
+ "daypart.evening": "Evening",
47
+ "daypart.formiddag": "Morning",
48
+ "daypart.ettermiddag": "Afternoon",
49
+ "daypart.kveld": "Evening"
50
+ }
51
+ };
52
+ /** Norwegian in any of its three spellings reads the `nb` pack; anything else `en`. */
53
+ function labelPackFor(locale) {
54
+ const language = locale.toLowerCase().split("-")[0];
55
+ return language === "nb" || language === "no" || language === "nn" ? "nb" : "en";
56
+ }
57
+ /** The built-in pack for `locale`, with the site's own words over it. */
58
+ function resolveLabels(locale, overrides) {
59
+ const merged = { ...LABEL_PACKS[labelPackFor(locale)] };
60
+ for (const [key, value] of Object.entries(overrides ?? {})) if (isBookingLabel(value)) merged[key] = value;
61
+ return merged;
62
+ }
63
+ function fillOne(template, values) {
64
+ return template.replace(/\{(\w+)\}/g, (hole, name) => Object.hasOwn(values, name) ? String(values[name]) : hole);
65
+ }
66
+ /** A label as plain text: an array's elements joined, a string as it is. */
67
+ function labelText(label) {
68
+ return typeof label === "string" ? label : label.join("");
69
+ }
70
+ /**
71
+ * `fill('{count} barn', { count: 2 })` → «2 barn», as plain text: an array is
72
+ * filled element by element and joined. An unknown hole stays as written.
73
+ */
74
+ function fill(template, values) {
75
+ return typeof template === "string" ? fillOne(template, values) : template.map((piece) => fillOne(piece, values)).join("");
76
+ }
77
+ /**
78
+ * A filled label as the text nodes it renders as: one for a string (like a
79
+ * template literal), one per element for an array, each element filled with
80
+ * the same values — `fillParts(['Steg ', '{step}', ' av {total}'], { step: 3,
81
+ * total: 4 })` → `['Steg ', '3', ' av 4']`. Rendered as element children,
82
+ * each entry becomes its own text node, so a pack decides where a sentence
83
+ * breaks (a browser lays text out per text node, and the same sentence split
84
+ * differently can land a sub-pixel apart). Use `fill` where the target takes
85
+ * text only (`aria-label`, `title`, ICS). Empty pieces are dropped.
86
+ */
87
+ function fillParts(template, values) {
88
+ return (typeof template === "string" ? [template] : template).map((piece) => fillOne(piece, values)).filter((piece) => piece !== "");
89
+ }
90
+ //#endregion
91
+ export { LABEL_PACKS, fill, fillParts, isBookingLabel, labelPackFor, labelText, resolveLabels };
92
+
93
+ //# sourceMappingURL=labels.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labels.mjs","names":[],"sources":["../../src/core/labels.ts"],"sourcesContent":["/**\n * The words the pure core puts into strings it builds itself — the sticky\n * summary line, a day said relative to today, an age.\n *\n * Everything else a booking screen says belongs to the screen (the meda\n * `/booking` screens take their copy through a `labels` prop). These are the\n * few sentences that are assembled out of state, so the rule that assembles\n * them and the words it uses have to travel together.\n *\n * A template has `{name}` holes and is a `BookingLabel`: a string or an array\n * of strings, so a label pack stays serialisable (it rides in\n * `BookingConfig.labels`, which is a page prop). Rendered as element children\n * (`fillParts`), a string is ONE text node once filled and an array is one\n * text node per element; as text (`fill`), an array's elements are joined.\n */\n\n/**\n * One label. A `string` renders as one text node once filled, like a template\n * literal; a `string[]` renders one text node per element, each filled with\n * the same values: `['Steg ', '{step}', ' av ', '{total}']` is four nodes.\n * Text-only targets (`aria-label`, ICS, a file name) read it joined.\n */\nexport type BookingLabel = string | readonly string[];\n\n/** Whether `value` is a `BookingLabel`: a string, or an array of strings. */\nexport function isBookingLabel(value: unknown): value is BookingLabel {\n return (\n typeof value === 'string' ||\n (Array.isArray(value) && value.every((piece) => typeof piece === 'string'))\n );\n}\n\n/** The labels the core reads. `/react` (P3) adds the screen copy on top. */\nexport interface CoreLabels {\n /** `i dag` — mid-sentence, for «Neste ledige i dag 14:15». */\n 'clock.today': BookingLabel;\n 'clock.tomorrow': BookingLabel;\n /** `I dag` — the same day, sized for a chip in the date strip. */\n 'clock.todayChip': BookingLabel;\n 'clock.tomorrowChip': BookingLabel;\n /** `{day} {time}` — «i dag 14:15». */\n 'clock.when': BookingLabel;\n /** `{date} kl. {time}` — the manage page's whole appointment in one line. */\n 'clock.dateTime': BookingLabel;\n /** `{min} år`. */\n 'age.exact': BookingLabel;\n /** `{min}–{max} år`, when only the birth year is known. */\n 'age.range': BookingLabel;\n /** The summary bar's stylist part for a null preference. */\n 'summary.firstAvailable': BookingLabel;\n /** The summary bar's time part before an hour is picked. */\n 'summary.pickTime': BookingLabel;\n /** `{count} tjenester` — a family's summary. */\n 'summary.services': BookingLabel;\n /** `{minutes} min totalt`. */\n 'summary.totalMinutes': BookingLabel;\n /** `{count} barn` — guest children on step 1. */\n 'people.children': BookingLabel;\n /** The logged-in parent's own seat. */\n 'people.self': BookingLabel;\n /** A guest grown-up's seat. */\n 'people.adult': BookingLabel;\n}\n\n/** Per-key copy for config-defined groups: `daypart.<key>`, `category.<key>`. */\nexport type KeyedLabels = {\n [key: `daypart.${string}`]: BookingLabel;\n [key: `category.${string}`]: BookingLabel;\n};\n\nexport type BookingLabels = CoreLabels & KeyedLabels;\n\nconst NB: BookingLabels = {\n 'clock.today': 'i dag',\n 'clock.tomorrow': 'i morgen',\n 'clock.todayChip': 'I dag',\n 'clock.tomorrowChip': 'I morgen',\n 'clock.when': '{day} {time}',\n 'clock.dateTime': '{date} kl. {time}',\n 'age.exact': '{min} år',\n 'age.range': '{min}–{max} år',\n 'summary.firstAvailable': 'Første ledige',\n 'summary.pickTime': 'Velg tid',\n 'summary.services': '{count} tjenester',\n 'summary.totalMinutes': '{minutes} min totalt',\n 'people.children': '{count} barn',\n 'people.self': 'Meg selv',\n 'people.adult': 'Voksen',\n 'daypart.formiddag': 'Formiddag',\n 'daypart.ettermiddag': 'Ettermiddag',\n 'daypart.kveld': 'Kveld',\n};\n\nconst EN: BookingLabels = {\n 'clock.today': 'today',\n 'clock.tomorrow': 'tomorrow',\n 'clock.todayChip': 'Today',\n 'clock.tomorrowChip': 'Tomorrow',\n 'clock.when': '{day} {time}',\n 'clock.dateTime': '{date} at {time}',\n 'age.exact': '{min} yrs',\n 'age.range': '{min}–{max} yrs',\n 'summary.firstAvailable': 'First available',\n 'summary.pickTime': 'Pick a time',\n 'summary.services': '{count} services',\n 'summary.totalMinutes': '{minutes} min in total',\n 'people.children': '{count} children',\n 'people.self': 'Me',\n 'people.adult': 'Adult',\n 'daypart.morning': 'Morning',\n 'daypart.afternoon': 'Afternoon',\n 'daypart.evening': 'Evening',\n // The default day parts keep their Norwegian keys in any locale.\n 'daypart.formiddag': 'Morning',\n 'daypart.ettermiddag': 'Afternoon',\n 'daypart.kveld': 'Evening',\n};\n\n/** The built-in packs, by language subtag. */\nexport const LABEL_PACKS: Readonly<Record<'nb' | 'en', Readonly<BookingLabels>>> = {\n nb: NB,\n en: EN,\n};\n\n/** Norwegian in any of its three spellings reads the `nb` pack; anything else `en`. */\nexport function labelPackFor(locale: string): 'nb' | 'en' {\n const language = locale.toLowerCase().split('-')[0];\n return language === 'nb' || language === 'no' || language === 'nn' ? 'nb' : 'en';\n}\n\n/** The built-in pack for `locale`, with the site's own words over it. */\nexport function resolveLabels(\n locale: string,\n overrides?: Partial<BookingLabels> | null\n): Readonly<BookingLabels> {\n const merged: BookingLabels = { ...LABEL_PACKS[labelPackFor(locale)] };\n for (const [key, value] of Object.entries(overrides ?? {})) {\n if (isBookingLabel(value)) (merged as unknown as Record<string, BookingLabel>)[key] = value;\n }\n return merged;\n}\n\nfunction fillOne(template: string, values: Readonly<Record<string, string | number>>): string {\n return template.replace(/\\{(\\w+)\\}/g, (hole, name: string) =>\n Object.hasOwn(values, name) ? String(values[name]) : hole\n );\n}\n\n/** A label as plain text: an array's elements joined, a string as it is. */\nexport function labelText(label: BookingLabel): string {\n return typeof label === 'string' ? label : label.join('');\n}\n\n/**\n * `fill('{count} barn', { count: 2 })` → «2 barn», as plain text: an array is\n * filled element by element and joined. An unknown hole stays as written.\n */\nexport function fill(\n template: BookingLabel,\n values: Readonly<Record<string, string | number>>\n): string {\n return typeof template === 'string'\n ? fillOne(template, values)\n : template.map((piece) => fillOne(piece, values)).join('');\n}\n\n/**\n * A filled label as the text nodes it renders as: one for a string (like a\n * template literal), one per element for an array, each element filled with\n * the same values — `fillParts(['Steg ', '{step}', ' av {total}'], { step: 3,\n * total: 4 })` → `['Steg ', '3', ' av 4']`. Rendered as element children,\n * each entry becomes its own text node, so a pack decides where a sentence\n * breaks (a browser lays text out per text node, and the same sentence split\n * differently can land a sub-pixel apart). Use `fill` where the target takes\n * text only (`aria-label`, `title`, ICS). Empty pieces are dropped.\n */\nexport function fillParts(\n template: BookingLabel,\n values: Readonly<Record<string, string | number>>\n): string[] {\n const pieces = typeof template === 'string' ? [template] : template;\n return pieces.map((piece) => fillOne(piece, values)).filter((piece) => piece !== '');\n}\n"],"mappings":";;AAyBA,SAAgB,eAAe,OAAuC;CACpE,OACE,OAAO,UAAU,YAChB,MAAM,QAAQ,KAAK,KAAK,MAAM,OAAO,UAAU,OAAO,UAAU,QAAQ;AAE7E;;AAyFA,MAAa,cAAsE;CACjF,IAAI;EA/CJ,eAAe;EACf,kBAAkB;EAClB,mBAAmB;EACnB,sBAAsB;EACtB,cAAc;EACd,kBAAkB;EAClB,aAAa;EACb,aAAa;EACb,0BAA0B;EAC1B,oBAAoB;EACpB,oBAAoB;EACpB,wBAAwB;EACxB,mBAAmB;EACnB,eAAe;EACf,gBAAgB;EAChB,qBAAqB;EACrB,uBAAuB;EACvB,iBAAiB;CA8Bb;CACJ,IAAI;EA3BJ,eAAe;EACf,kBAAkB;EAClB,mBAAmB;EACnB,sBAAsB;EACtB,cAAc;EACd,kBAAkB;EAClB,aAAa;EACb,aAAa;EACb,0BAA0B;EAC1B,oBAAoB;EACpB,oBAAoB;EACpB,wBAAwB;EACxB,mBAAmB;EACnB,eAAe;EACf,gBAAgB;EAChB,mBAAmB;EACnB,qBAAqB;EACrB,mBAAmB;EAEnB,qBAAqB;EACrB,uBAAuB;EACvB,iBAAiB;CAMb;AACN;;AAGA,SAAgB,aAAa,QAA6B;CACxD,MAAM,WAAW,OAAO,YAAY,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;CACjD,OAAO,aAAa,QAAQ,aAAa,QAAQ,aAAa,OAAO,OAAO;AAC9E;;AAGA,SAAgB,cACd,QACA,WACyB;CACzB,MAAM,SAAwB,EAAE,GAAG,YAAY,aAAa,MAAM,GAAG;CACrE,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,aAAa,CAAC,CAAC,GACvD,IAAI,eAAe,KAAK,GAAG,OAAoD,OAAO;CAExF,OAAO;AACT;AAEA,SAAS,QAAQ,UAAkB,QAA2D;CAC5F,OAAO,SAAS,QAAQ,eAAe,MAAM,SAC3C,OAAO,OAAO,QAAQ,IAAI,IAAI,OAAO,OAAO,KAAK,IAAI,IACvD;AACF;;AAGA,SAAgB,UAAU,OAA6B;CACrD,OAAO,OAAO,UAAU,WAAW,QAAQ,MAAM,KAAK,EAAE;AAC1D;;;;;AAMA,SAAgB,KACd,UACA,QACQ;CACR,OAAO,OAAO,aAAa,WACvB,QAAQ,UAAU,MAAM,IACxB,SAAS,KAAK,UAAU,QAAQ,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE;AAC7D;;;;;;;;;;;AAYA,SAAgB,UACd,UACA,QACU;CAEV,QADe,OAAO,aAAa,WAAW,CAAC,QAAQ,IAAI,SAAA,CAC7C,KAAK,UAAU,QAAQ,OAAO,MAAM,CAAC,CAAC,CAAC,QAAQ,UAAU,UAAU,EAAE;AACrF"}