@krizaka/ui 3.23.0 → 3.25.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 (223) hide show
  1. package/README.md +9 -1
  2. package/dist/account-menu.d.ts +163 -0
  3. package/dist/account-menu.js +211 -0
  4. package/dist/account-menu.js.map +1 -0
  5. package/dist/app-shell.js +4 -4
  6. package/dist/changelog-page.d.ts +13 -1
  7. package/dist/changelog-page.js +20 -0
  8. package/dist/changelog-page.js.map +1 -1
  9. package/dist/{chunk-CEJDH6SA.js → chunk-2PNLHZLT.js} +23 -3
  10. package/dist/chunk-2PNLHZLT.js.map +1 -0
  11. package/dist/{chunk-YCIBJ6OQ.js → chunk-4DH2HW3D.js} +3 -1
  12. package/dist/chunk-C7LTF5CC.js +21 -0
  13. package/dist/chunk-C7LTF5CC.js.map +1 -0
  14. package/dist/{chunk-2L67YTNV.js → chunk-FTX66XZY.js} +2 -2
  15. package/dist/{chunk-F7FVPPBB.js → chunk-LYOPPBMC.js} +2 -2
  16. package/dist/{chunk-AFY35UCB.js → chunk-P7GZR5CU.js} +3 -1
  17. package/dist/{chunk-AFY35UCB.js.map → chunk-P7GZR5CU.js.map} +1 -1
  18. package/dist/code-block.js +1 -1
  19. package/dist/cover-hero.d.ts +125 -0
  20. package/dist/cover-hero.js +82 -0
  21. package/dist/cover-hero.js.map +1 -0
  22. package/dist/data-table.js +2 -2
  23. package/dist/docs-nav.js +3 -16
  24. package/dist/docs-nav.js.map +1 -1
  25. package/dist/dropdown-menu.d.ts +12 -1
  26. package/dist/dropdown-menu.js +25 -2
  27. package/dist/dropdown-menu.js.map +1 -1
  28. package/dist/filter-bar.js +1 -1
  29. package/dist/genui.js +2 -2
  30. package/dist/index.d.ts +15 -1
  31. package/dist/index.js +62 -2
  32. package/dist/index.js.map +1 -1
  33. package/dist/media-gallery.d.ts +73 -0
  34. package/dist/media-gallery.js +42 -0
  35. package/dist/media-gallery.js.map +1 -0
  36. package/dist/nav-bar.js +2 -2
  37. package/dist/opening-hours.d.ts +124 -0
  38. package/dist/opening-hours.js +48 -0
  39. package/dist/opening-hours.js.map +1 -0
  40. package/dist/rail-nav.js +1 -1
  41. package/dist/service-card.d.ts +168 -0
  42. package/dist/service-card.js +112 -0
  43. package/dist/service-card.js.map +1 -0
  44. package/dist/site-footer.js +2 -2
  45. package/dist/slot-picker.d.ts +119 -0
  46. package/dist/slot-picker.js +46 -0
  47. package/dist/slot-picker.js.map +1 -0
  48. package/dist/sticky-action.d.ts +12 -0
  49. package/dist/sticky-action.js +14 -0
  50. package/dist/sticky-action.js.map +1 -0
  51. package/package.json +106 -6
  52. package/registry/account-menu.json +280 -0
  53. package/registry/achievement.json +1 -1
  54. package/registry/action-bar.json +1 -1
  55. package/registry/activity-feed.json +3 -3
  56. package/registry/alert.json +1 -1
  57. package/registry/app-header.json +1 -1
  58. package/registry/app-shell.json +1 -1
  59. package/registry/argument-card.json +1 -1
  60. package/registry/audio-player.json +1 -1
  61. package/registry/auth-layout.json +1 -1
  62. package/registry/avatar.json +1 -1
  63. package/registry/badge.json +1 -1
  64. package/registry/button.json +1 -1
  65. package/registry/captcha.json +1 -1
  66. package/registry/card-grid.json +1 -1
  67. package/registry/card.json +3 -3
  68. package/registry/catalogue-page.json +1 -1
  69. package/registry/changelog-page.json +3 -3
  70. package/registry/chat.json +1 -1
  71. package/registry/checkbox.json +1 -1
  72. package/registry/chip-link.json +1 -1
  73. package/registry/chip.json +1 -1
  74. package/registry/choice-grid.json +1 -1
  75. package/registry/code-block.json +1 -1
  76. package/registry/combobox.json +1 -1
  77. package/registry/command.json +1 -1
  78. package/registry/comparison-table.json +1 -1
  79. package/registry/component-page.json +1 -1
  80. package/registry/confirm-button.json +1 -1
  81. package/registry/contact-page.json +1 -1
  82. package/registry/continue-prompt.json +1 -1
  83. package/registry/countdown.json +1 -1
  84. package/registry/cover-hero.json +246 -0
  85. package/registry/cta-panel.json +1 -1
  86. package/registry/dashboard-layout.json +1 -1
  87. package/registry/data-table.json +1 -1
  88. package/registry/date-picker.json +1 -1
  89. package/registry/decision-list.json +1 -1
  90. package/registry/description-list.json +1 -1
  91. package/registry/detail-layout.json +1 -1
  92. package/registry/dialog.json +1 -1
  93. package/registry/docs-article-page.json +1 -1
  94. package/registry/docs-hub-page.json +1 -1
  95. package/registry/docs-layout.json +1 -1
  96. package/registry/docs-nav.json +1 -1
  97. package/registry/document-viewer.json +1 -1
  98. package/registry/dropdown-menu.json +33 -3
  99. package/registry/empty-state.json +1 -1
  100. package/registry/error-page.json +1 -1
  101. package/registry/examples/account-menu/default.d.ts +4 -0
  102. package/registry/examples/account-menu/default.js +28 -0
  103. package/registry/examples/account-menu/default.tsx +22 -0
  104. package/registry/examples/cover-hero/plain.d.ts +4 -0
  105. package/registry/examples/cover-hero/plain.js +12 -0
  106. package/registry/examples/cover-hero/plain.tsx +7 -0
  107. package/registry/examples/cover-hero/shop.d.ts +4 -0
  108. package/registry/examples/cover-hero/shop.js +27 -0
  109. package/registry/examples/cover-hero/shop.tsx +20 -0
  110. package/registry/examples/media-gallery/shop.d.ts +4 -0
  111. package/registry/examples/media-gallery/shop.js +22 -0
  112. package/registry/examples/media-gallery/shop.tsx +18 -0
  113. package/registry/examples/opening-hours/week.d.ts +4 -0
  114. package/registry/examples/opening-hours/week.js +28 -0
  115. package/registry/examples/opening-hours/week.tsx +22 -0
  116. package/registry/examples/service-card/services.d.ts +4 -0
  117. package/registry/examples/service-card/services.js +15 -0
  118. package/registry/examples/service-card/services.tsx +12 -0
  119. package/registry/examples/slot-picker/times.d.ts +4 -0
  120. package/registry/examples/slot-picker/times.js +27 -0
  121. package/registry/examples/slot-picker/times.tsx +23 -0
  122. package/registry/examples/sticky-action/phone.d.ts +4 -0
  123. package/registry/examples/sticky-action/phone.js +15 -0
  124. package/registry/examples/sticky-action/phone.tsx +14 -0
  125. package/registry/field.json +1 -1
  126. package/registry/file-upload.json +1 -1
  127. package/registry/filter-bar.json +1 -1
  128. package/registry/force-update-screen.json +1 -1
  129. package/registry/form.json +3 -3
  130. package/registry/gallery-page.json +1 -1
  131. package/registry/genui.json +1 -1
  132. package/registry/glow-card.json +1 -1
  133. package/registry/goal-gauge.json +1 -1
  134. package/registry/how-it-works-page.json +1 -1
  135. package/registry/image-cropper.json +1 -1
  136. package/registry/index.json +226 -5
  137. package/registry/iso.json +1 -1
  138. package/registry/kanban.json +1 -1
  139. package/registry/kbd.json +1 -1
  140. package/registry/landing-page.json +1 -1
  141. package/registry/landscape.json +1 -1
  142. package/registry/legal-page.json +1 -1
  143. package/registry/link-card.json +3 -3
  144. package/registry/list.json +1 -1
  145. package/registry/loader.json +1 -1
  146. package/registry/longform-page.json +1 -1
  147. package/registry/maintenance-screen.json +1 -1
  148. package/registry/map.json +2 -2
  149. package/registry/markdown.json +1 -1
  150. package/registry/marketing-layout.json +1 -1
  151. package/registry/marquee.json +1 -1
  152. package/registry/mascot-quote.json +1 -1
  153. package/registry/mascot-state.json +1 -1
  154. package/registry/mascots.json +1 -1
  155. package/registry/match-dialog.json +1 -1
  156. package/registry/media-capture.json +3 -3
  157. package/registry/media-gallery.json +144 -0
  158. package/registry/media-grid.json +3 -3
  159. package/registry/meta-row.json +1 -1
  160. package/registry/meter.json +1 -1
  161. package/registry/nav-bar.json +2 -2
  162. package/registry/number-ticker.json +1 -1
  163. package/registry/opening-hours.json +178 -0
  164. package/registry/page-header.json +1 -1
  165. package/registry/page-hero.json +3 -3
  166. package/registry/page.json +7 -7
  167. package/registry/pagination.json +1 -1
  168. package/registry/photo-grid.json +1 -1
  169. package/registry/pointer-light.json +1 -1
  170. package/registry/popover.json +1 -1
  171. package/registry/pricing-page.json +1 -1
  172. package/registry/product-page.json +1 -1
  173. package/registry/profile-card.json +1 -1
  174. package/registry/progress.json +1 -1
  175. package/registry/promise-card.json +1 -1
  176. package/registry/radio-group.json +1 -1
  177. package/registry/rail-nav.json +2 -2
  178. package/registry/recipes.json +1 -1
  179. package/registry/scroll-stage.json +1 -1
  180. package/registry/search-field.json +1 -1
  181. package/registry/section-backdrop.json +1 -1
  182. package/registry/separator.json +1 -1
  183. package/registry/service-card.json +358 -0
  184. package/registry/settings-layout.json +1 -1
  185. package/registry/showcase.json +1 -1
  186. package/registry/site-footer.json +2 -2
  187. package/registry/sites.json +1 -1
  188. package/registry/skeleton.json +3 -3
  189. package/registry/skip-link.json +1 -1
  190. package/registry/slider.json +1 -1
  191. package/registry/slot-picker.json +222 -0
  192. package/registry/slot.json +1 -1
  193. package/registry/spinner.json +1 -1
  194. package/registry/split-view.json +1 -1
  195. package/registry/stage-banner.json +1 -1
  196. package/registry/stat.json +1 -1
  197. package/registry/status-page.json +1 -1
  198. package/registry/step-progress.json +1 -1
  199. package/registry/step-showcase.json +1 -1
  200. package/registry/stepper.json +1 -1
  201. package/registry/sticky-action.json +108 -0
  202. package/registry/swipe-deck.json +1 -1
  203. package/registry/switch.json +1 -1
  204. package/registry/tabs.json +1 -1
  205. package/registry/tag-input.json +1 -1
  206. package/registry/tag-selector.json +1 -1
  207. package/registry/terminal-replay.json +1 -1
  208. package/registry/theme.json +1 -1
  209. package/registry/toast.json +1 -1
  210. package/registry/toc.json +1 -1
  211. package/registry/tooltip.json +1 -1
  212. package/registry/tree-view.json +1 -1
  213. package/registry/update-prompt.json +1 -1
  214. package/registry/verified-badge.json +1 -1
  215. package/registry/vertical-video.json +5 -5
  216. package/registry/video-player.json +3 -3
  217. package/registry/video-trimmer.json +1 -1
  218. package/registry/virtual.json +1 -1
  219. package/registry/waveform.json +1 -1
  220. package/dist/chunk-CEJDH6SA.js.map +0 -1
  221. /package/dist/{chunk-YCIBJ6OQ.js.map → chunk-4DH2HW3D.js.map} +0 -0
  222. /package/dist/{chunk-2L67YTNV.js.map → chunk-FTX66XZY.js.map} +0 -0
  223. /package/dist/{chunk-F7FVPPBB.js.map → chunk-LYOPPBMC.js.map} +0 -0
@@ -0,0 +1,48 @@
1
+ import {
2
+ ChevronGlyph
3
+ } from "./chunk-XCBVFMNV.js";
4
+
5
+ // src/opening-hours/opening-hours.tsx
6
+ import { tv } from "tailwind-variants";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ var openingHours = tv({
9
+ slots: {
10
+ root: "group/hours rounded-xl border border-border-default bg-surface-1 text-fg",
11
+ summary: "flex min-h-14 cursor-pointer list-none items-center gap-3 rounded-xl px-4 py-3 outline-hidden marker:hidden focus-visible:ring-2 focus-visible:ring-ring [&::-webkit-details-marker]:hidden",
12
+ dot: "h-2.5 w-2.5 shrink-0 rounded-full bg-fg-muted data-[open=true]:bg-success data-[open=true]:motion-safe:animate-pulse",
13
+ status: "flex min-w-0 flex-1 flex-col leading-tight",
14
+ state: "text-sm font-bold text-fg",
15
+ detail: "text-xs text-fg-secondary",
16
+ today: "hidden shrink-0 text-sm tabular-nums text-fg-secondary sm:block",
17
+ chevron: "h-4 w-4 shrink-0 text-fg-secondary transition-transform group-open/hours:rotate-180 motion-reduce:transition-none",
18
+ list: "m-0 flex list-none flex-col gap-0.5 border-t border-border-subtle p-2 sm:p-3",
19
+ row: "flex items-baseline justify-between gap-4 rounded-lg px-2 py-1.5 text-sm text-fg-secondary data-[today]:bg-accent-soft data-[today]:font-bold data-[today]:text-fg",
20
+ day: "shrink-0",
21
+ hours: "text-right tabular-nums"
22
+ }
23
+ });
24
+ var s = openingHours();
25
+ function OpeningHours({ days, today, status, label, className, ...props }) {
26
+ const todayHours = days.find((d) => d.id === today)?.hours;
27
+ return /* @__PURE__ */ jsxs("details", { className: s.root({ className }), ...props, children: [
28
+ /* @__PURE__ */ jsxs("summary", { className: s.summary(), children: [
29
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, "data-open": status.open, className: s.dot() }),
30
+ /* @__PURE__ */ jsxs("span", { className: s.status(), children: [
31
+ /* @__PURE__ */ jsx("span", { className: s.state(), children: status.label }),
32
+ status.detail && /* @__PURE__ */ jsx("span", { className: s.detail(), children: status.detail })
33
+ ] }),
34
+ todayHours && /* @__PURE__ */ jsx("span", { className: s.today(), children: todayHours }),
35
+ /* @__PURE__ */ jsx(ChevronGlyph, { className: s.chevron() }),
36
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: label })
37
+ ] }),
38
+ /* @__PURE__ */ jsx("ul", { className: s.list(), "aria-label": label, children: days.map((day) => /* @__PURE__ */ jsxs("li", { className: s.row(), "data-today": day.id === today ? "" : void 0, "aria-current": day.id === today ? "date" : void 0, children: [
39
+ /* @__PURE__ */ jsx("span", { className: s.day(), children: day.label }),
40
+ /* @__PURE__ */ jsx("span", { className: s.hours(), children: day.hours })
41
+ ] }, day.id)) })
42
+ ] });
43
+ }
44
+ export {
45
+ OpeningHours,
46
+ openingHours
47
+ };
48
+ //# sourceMappingURL=opening-hours.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/opening-hours/opening-hours.tsx"],"sourcesContent":["// Server-safe: no hook, no context (a native <details>). A shop's weekly hours in one quiet row — « Open now · closes at 5 pm »\n// and today's hours — that unfolds into the whole week with today marked. The app computes the status and formats the\n// times (the shop's zone and the reader's locale are its business); this block only lays them out.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { ChevronGlyph } from \"../glyphs\";\n\nexport const openingHours = tv({\n slots: {\n root: \"group/hours rounded-xl border border-border-default bg-surface-1 text-fg\",\n summary:\n \"flex min-h-14 cursor-pointer list-none items-center gap-3 rounded-xl px-4 py-3 outline-hidden marker:hidden focus-visible:ring-2 focus-visible:ring-ring [&::-webkit-details-marker]:hidden\",\n dot: \"h-2.5 w-2.5 shrink-0 rounded-full bg-fg-muted data-[open=true]:bg-success data-[open=true]:motion-safe:animate-pulse\",\n status: \"flex min-w-0 flex-1 flex-col leading-tight\",\n state: \"text-sm font-bold text-fg\",\n detail: \"text-xs text-fg-secondary\",\n today: \"hidden shrink-0 text-sm tabular-nums text-fg-secondary sm:block\",\n chevron: \"h-4 w-4 shrink-0 text-fg-secondary transition-transform group-open/hours:rotate-180 motion-reduce:transition-none\",\n list: \"m-0 flex list-none flex-col gap-0.5 border-t border-border-subtle p-2 sm:p-3\",\n row: \"flex items-baseline justify-between gap-4 rounded-lg px-2 py-1.5 text-sm text-fg-secondary data-[today]:bg-accent-soft data-[today]:font-bold data-[today]:text-fg\",\n day: \"shrink-0\",\n hours: \"text-right tabular-nums\",\n },\n});\nconst s = openingHours();\n\nexport type OpeningDay = {\n /** A stable id (`mon`, `1`). */\n id: string;\n /** The day's name, translated (`Lundi`). */\n label: string;\n /** The day's hours, formatted by the app (`8 h – 12 h, 13 h – 17 h`); the app's word for closed when there are none. */\n hours: string;\n};\n\nexport type OpeningStatus = {\n /** Open at this moment. */\n open: boolean;\n /** The state in words (`Open now`, `Closed`). */\n label: string;\n /** What follows (`closes at 5 pm`, `opens tomorrow at 8 am`). */\n detail?: string;\n};\n\nexport type OpeningHoursProps = Omit<React.ComponentProps<\"details\">, \"children\"> & {\n /** The week, in reading order. */\n days: readonly OpeningDay[];\n /** The id of today's day: its row is marked (`aria-current=\"date\"`) and its hours show on the closed row. */\n today?: string;\n /** Open or closed right now, in words. */\n status: OpeningStatus;\n /** The name of the list (`Hours`), passed translated. */\n label: string;\n};\n\n/** The weekly hours in one row — open now, closes at… — that unfolds into the week, today highlighted. */\nexport function OpeningHours({ days, today, status, label, className, ...props }: OpeningHoursProps) {\n const todayHours = days.find((d) => d.id === today)?.hours;\n return (\n <details className={s.root({ className })} {...props}>\n <summary className={s.summary()}>\n <span aria-hidden data-open={status.open} className={s.dot()} />\n <span className={s.status()}>\n <span className={s.state()}>{status.label}</span>\n {status.detail && <span className={s.detail()}>{status.detail}</span>}\n </span>\n {todayHours && <span className={s.today()}>{todayHours}</span>}\n <ChevronGlyph className={s.chevron()} />\n <span className=\"sr-only\">{label}</span>\n </summary>\n <ul className={s.list()} aria-label={label}>\n {days.map((day) => (\n <li key={day.id} className={s.row()} data-today={day.id === today ? \"\" : undefined} aria-current={day.id === today ? \"date\" : undefined}>\n <span className={s.day()}>{day.label}</span>\n <span className={s.hours()}>{day.hours}</span>\n </li>\n ))}\n </ul>\n </details>\n );\n}\n"],"mappings":";;;;;AAIA,SAAS,UAAU;AA0DX,cACA,YADA;AAtDD,IAAM,eAAe,GAAG;AAAA,EAC7B,OAAO;AAAA,IACL,MAAM;AAAA,IACN,SACE;AAAA,IACF,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,KAAK;AAAA,IACL,KAAK;AAAA,IACL,OAAO;AAAA,EACT;AACF,CAAC;AACD,IAAM,IAAI,aAAa;AAgChB,SAAS,aAAa,EAAE,MAAM,OAAO,QAAQ,OAAO,WAAW,GAAG,MAAM,GAAsB;AACnG,QAAM,aAAa,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,GAAG;AACrD,SACE,qBAAC,aAAQ,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAC7C;AAAA,yBAAC,aAAQ,WAAW,EAAE,QAAQ,GAC5B;AAAA,0BAAC,UAAK,eAAW,MAAC,aAAW,OAAO,MAAM,WAAW,EAAE,IAAI,GAAG;AAAA,MAC9D,qBAAC,UAAK,WAAW,EAAE,OAAO,GACxB;AAAA,4BAAC,UAAK,WAAW,EAAE,MAAM,GAAI,iBAAO,OAAM;AAAA,QACzC,OAAO,UAAU,oBAAC,UAAK,WAAW,EAAE,OAAO,GAAI,iBAAO,QAAO;AAAA,SAChE;AAAA,MACC,cAAc,oBAAC,UAAK,WAAW,EAAE,MAAM,GAAI,sBAAW;AAAA,MACvD,oBAAC,gBAAa,WAAW,EAAE,QAAQ,GAAG;AAAA,MACtC,oBAAC,UAAK,WAAU,WAAW,iBAAM;AAAA,OACnC;AAAA,IACA,oBAAC,QAAG,WAAW,EAAE,KAAK,GAAG,cAAY,OAClC,eAAK,IAAI,CAAC,QACT,qBAAC,QAAgB,WAAW,EAAE,IAAI,GAAG,cAAY,IAAI,OAAO,QAAQ,KAAK,QAAW,gBAAc,IAAI,OAAO,QAAQ,SAAS,QAC5H;AAAA,0BAAC,UAAK,WAAW,EAAE,IAAI,GAAI,cAAI,OAAM;AAAA,MACrC,oBAAC,UAAK,WAAW,EAAE,MAAM,GAAI,cAAI,OAAM;AAAA,SAFhC,IAAI,EAGb,CACD,GACH;AAAA,KACF;AAEJ;","names":[]}
package/dist/rail-nav.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  ChevronGlyph
4
- } from "./chunk-AFY35UCB.js";
4
+ } from "./chunk-P7GZR5CU.js";
5
5
  import {
6
6
  cn
7
7
  } from "./chunk-WU4VLSEA.js";
@@ -0,0 +1,168 @@
1
+ import * as tailwind_variants from 'tailwind-variants';
2
+ import * as React from 'react';
3
+ import { L as LinkComponent } from './block-link-BAg4eoZk.js';
4
+
5
+ declare const serviceCard: tailwind_variants.TVReturnType<{
6
+ [key: string]: {
7
+ [key: string]: tailwind_variants.ClassValue | {
8
+ body?: tailwind_variants.ClassValue;
9
+ head?: tailwind_variants.ClassValue;
10
+ media?: tailwind_variants.ClassValue;
11
+ action?: tailwind_variants.ClassValue;
12
+ icon?: tailwind_variants.ClassValue;
13
+ titles?: tailwind_variants.ClassValue;
14
+ name?: tailwind_variants.ClassValue;
15
+ facts?: tailwind_variants.ClassValue;
16
+ tag?: tailwind_variants.ClassValue;
17
+ description?: tailwind_variants.ClassValue;
18
+ included?: tailwind_variants.ClassValue;
19
+ includedItem?: tailwind_variants.ClassValue;
20
+ foot?: tailwind_variants.ClassValue;
21
+ priceBlock?: tailwind_variants.ClassValue;
22
+ from?: tailwind_variants.ClassValue;
23
+ price?: tailwind_variants.ClassValue;
24
+ root?: tailwind_variants.ClassValue;
25
+ };
26
+ };
27
+ } | {
28
+ [x: string]: {
29
+ [x: string]: tailwind_variants.ClassValue | {
30
+ body?: tailwind_variants.ClassValue;
31
+ head?: tailwind_variants.ClassValue;
32
+ media?: tailwind_variants.ClassValue;
33
+ action?: tailwind_variants.ClassValue;
34
+ icon?: tailwind_variants.ClassValue;
35
+ titles?: tailwind_variants.ClassValue;
36
+ name?: tailwind_variants.ClassValue;
37
+ facts?: tailwind_variants.ClassValue;
38
+ tag?: tailwind_variants.ClassValue;
39
+ description?: tailwind_variants.ClassValue;
40
+ included?: tailwind_variants.ClassValue;
41
+ includedItem?: tailwind_variants.ClassValue;
42
+ foot?: tailwind_variants.ClassValue;
43
+ priceBlock?: tailwind_variants.ClassValue;
44
+ from?: tailwind_variants.ClassValue;
45
+ price?: tailwind_variants.ClassValue;
46
+ root?: tailwind_variants.ClassValue;
47
+ };
48
+ };
49
+ } | {}, {
50
+ root: string;
51
+ media: string;
52
+ body: string;
53
+ head: string;
54
+ icon: string;
55
+ titles: string;
56
+ name: string;
57
+ facts: string;
58
+ tag: string;
59
+ description: string;
60
+ included: string;
61
+ includedItem: string;
62
+ foot: string;
63
+ priceBlock: string;
64
+ from: string;
65
+ price: string;
66
+ action: string;
67
+ }, undefined, {
68
+ [key: string]: {
69
+ [key: string]: tailwind_variants.ClassValue | {
70
+ body?: tailwind_variants.ClassValue;
71
+ head?: tailwind_variants.ClassValue;
72
+ media?: tailwind_variants.ClassValue;
73
+ action?: tailwind_variants.ClassValue;
74
+ icon?: tailwind_variants.ClassValue;
75
+ titles?: tailwind_variants.ClassValue;
76
+ name?: tailwind_variants.ClassValue;
77
+ facts?: tailwind_variants.ClassValue;
78
+ tag?: tailwind_variants.ClassValue;
79
+ description?: tailwind_variants.ClassValue;
80
+ included?: tailwind_variants.ClassValue;
81
+ includedItem?: tailwind_variants.ClassValue;
82
+ foot?: tailwind_variants.ClassValue;
83
+ priceBlock?: tailwind_variants.ClassValue;
84
+ from?: tailwind_variants.ClassValue;
85
+ price?: tailwind_variants.ClassValue;
86
+ root?: tailwind_variants.ClassValue;
87
+ };
88
+ };
89
+ } | {}, {
90
+ root: string;
91
+ media: string;
92
+ body: string;
93
+ head: string;
94
+ icon: string;
95
+ titles: string;
96
+ name: string;
97
+ facts: string;
98
+ tag: string;
99
+ description: string;
100
+ included: string;
101
+ includedItem: string;
102
+ foot: string;
103
+ priceBlock: string;
104
+ from: string;
105
+ price: string;
106
+ action: string;
107
+ }, tailwind_variants.TVReturnTypeLike<unknown, {
108
+ root: string;
109
+ media: string;
110
+ body: string;
111
+ head: string;
112
+ icon: string;
113
+ titles: string;
114
+ name: string;
115
+ facts: string;
116
+ tag: string;
117
+ description: string;
118
+ included: string;
119
+ includedItem: string;
120
+ foot: string;
121
+ priceBlock: string;
122
+ from: string;
123
+ price: string;
124
+ action: string;
125
+ }>>;
126
+ type ServiceCardProps = Omit<React.ComponentProps<"article">, "title" | "children"> & {
127
+ /** The service's name (the heading). */
128
+ name: string;
129
+ /** Its price, formatted by the app (`22,00 $`): the card never formats money. */
130
+ price: string;
131
+ /** The words before the price when it varies (`From`, `À partir de`), passed translated. */
132
+ priceFrom?: string;
133
+ /** How long it takes (`30 min`) — the first fact. */
134
+ duration?: string;
135
+ /** How many it takes (`Up to 5 dogs`, `1 person`) — the second fact. */
136
+ capacity?: string;
137
+ /** One or two sentences: what it is. */
138
+ description?: string;
139
+ /** What is included, one short line each (a check precedes it). */
140
+ included?: readonly string[];
141
+ /** A tag on the top right (`Popular`): only from real data, never decoration. */
142
+ tag?: string;
143
+ /** An icon tile before the name (a signature icon): decorative. */
144
+ icon?: React.ReactNode;
145
+ /** A photo or a loop above the card (an `img`, a `video`): fills a 16:9 band. */
146
+ media?: React.ReactNode;
147
+ /** This service is the chosen one (the accent, `data-selected`). */
148
+ selected?: boolean;
149
+ /** The words of the way in (`Book`, `Choose`): the accessible name of the press. */
150
+ actionLabel: string;
151
+ /** The way in, as an address (the booking page with this service preselected). */
152
+ href?: string;
153
+ /** The app's link component for internal addresses (Next's `Link`). */
154
+ linkAs?: LinkComponent;
155
+ /** The way in, as a press (a client parent). Ignored when `href` is set. */
156
+ onSelect?: () => void;
157
+ /**
158
+ * The heading level of the name.
159
+ * @default "h3"
160
+ */
161
+ titleAs?: "h2" | "h3" | "h4";
162
+ /** Enters on scroll (MotionObserver): the index in the grid becomes the delay. */
163
+ reveal?: number;
164
+ };
165
+ /** A service as a card: icon or photo, duration, price, what is included, the capacity, a tag — one way in. */
166
+ declare function ServiceCard({ name, price, priceFrom, duration, capacity, description, included, tag, icon, media, selected, actionLabel, href, linkAs, onSelect, titleAs: Title, reveal, className, style, ...props }: ServiceCardProps): React.JSX.Element;
167
+
168
+ export { ServiceCard, type ServiceCardProps, serviceCard };
@@ -0,0 +1,112 @@
1
+ import {
2
+ BlockLink
3
+ } from "./chunk-2AGSO3WH.js";
4
+ import {
5
+ CheckGlyph
6
+ } from "./chunk-XCBVFMNV.js";
7
+
8
+ // src/service-card/service-card.tsx
9
+ import * as React from "react";
10
+ import { tv } from "tailwind-variants";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ var serviceCard = tv({
13
+ slots: {
14
+ root: "group kz-spotlight kz-lift relative flex h-full min-w-0 flex-col overflow-hidden rounded-xl border border-border-default bg-surface-1 text-fg transition-colors hover:border-accent/50 focus-within:ring-2 focus-within:ring-ring data-[selected]:border-accent data-[selected]:bg-accent-soft",
15
+ media: "relative aspect-[16/9] w-full overflow-hidden bg-media [&>img]:h-full [&>img]:w-full [&>img]:object-cover",
16
+ body: "flex flex-1 flex-col gap-3 p-4 sm:p-5",
17
+ head: "flex items-start gap-3",
18
+ icon: "inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-accent-soft text-fg-accent [&>svg]:h-5 [&>svg]:w-5",
19
+ titles: "flex min-w-0 flex-1 flex-col gap-1",
20
+ name: "m-0 font-display text-lg font-bold leading-tight tracking-tight text-fg",
21
+ facts: "m-0 flex list-none flex-wrap gap-x-2 gap-y-0.5 p-0 text-xs text-fg-secondary",
22
+ tag: "shrink-0 rounded-full bg-accent px-2 py-0.5 text-[10px] font-extrabold uppercase tracking-wide text-on-accent",
23
+ description: "m-0 text-sm leading-relaxed text-fg-secondary",
24
+ included: "m-0 flex list-none flex-col gap-1.5 p-0 text-sm text-fg-secondary",
25
+ includedItem: "flex items-start gap-2 [&>svg]:mt-0.5 [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0 [&>svg]:text-fg-accent",
26
+ foot: "mt-auto flex items-end justify-between gap-3 border-t border-border-subtle pt-3",
27
+ priceBlock: "flex flex-col leading-none",
28
+ from: "mb-1 text-[11px] font-semibold uppercase tracking-wider text-fg-secondary",
29
+ price: "font-display text-2xl font-black tabular-nums text-fg",
30
+ action: "inline-flex min-h-9 items-center gap-1 rounded-full text-sm font-bold text-fg-accent no-underline after:absolute after:inset-0 after:content-[''] focus-visible:outline-hidden group-data-[selected]:text-fg"
31
+ }
32
+ });
33
+ var s = serviceCard();
34
+ function ServiceCard({
35
+ name,
36
+ price,
37
+ priceFrom,
38
+ duration,
39
+ capacity,
40
+ description,
41
+ included,
42
+ tag,
43
+ icon,
44
+ media,
45
+ selected,
46
+ actionLabel,
47
+ href,
48
+ linkAs,
49
+ onSelect,
50
+ titleAs: Title = "h3",
51
+ reveal,
52
+ className,
53
+ style,
54
+ ...props
55
+ }) {
56
+ const id = React.useId();
57
+ const facts = [duration, capacity].filter(Boolean);
58
+ const arrow = /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\u2192" });
59
+ const revealStyle = reveal === void 0 ? style : { ...style, "--kz-delay": `${Math.min(Math.max(reveal, 0), 8) * 50}ms` };
60
+ return /* @__PURE__ */ jsxs(
61
+ "article",
62
+ {
63
+ "aria-labelledby": id,
64
+ "data-selected": selected ? "" : void 0,
65
+ "data-reveal": reveal === void 0 ? void 0 : "",
66
+ style: revealStyle,
67
+ className: s.root({ className }),
68
+ ...props,
69
+ children: [
70
+ media && /* @__PURE__ */ jsx("div", { className: s.media(), children: media }),
71
+ /* @__PURE__ */ jsxs("div", { className: s.body(), children: [
72
+ /* @__PURE__ */ jsxs("div", { className: s.head(), children: [
73
+ icon && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: s.icon(), children: icon }),
74
+ /* @__PURE__ */ jsxs("div", { className: s.titles(), children: [
75
+ /* @__PURE__ */ jsx(Title, { id, className: s.name(), children: name }),
76
+ facts.length > 0 && /* @__PURE__ */ jsx("ul", { className: s.facts(), children: facts.map((fact, i) => /* @__PURE__ */ jsxs("li", { children: [
77
+ i > 0 && /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\xB7 " }),
78
+ fact
79
+ ] }, i)) })
80
+ ] }),
81
+ tag && /* @__PURE__ */ jsx("span", { className: s.tag(), children: tag })
82
+ ] }),
83
+ description && /* @__PURE__ */ jsx("p", { className: s.description(), children: description }),
84
+ included && included.length > 0 && /* @__PURE__ */ jsx("ul", { className: s.included(), children: included.map((line) => /* @__PURE__ */ jsxs("li", { className: s.includedItem(), children: [
85
+ /* @__PURE__ */ jsx(CheckGlyph, {}),
86
+ /* @__PURE__ */ jsx("span", { children: line })
87
+ ] }, line)) }),
88
+ /* @__PURE__ */ jsxs("div", { className: s.foot(), children: [
89
+ /* @__PURE__ */ jsxs("p", { className: s.priceBlock(), children: [
90
+ priceFrom && /* @__PURE__ */ jsx("span", { className: s.from(), children: priceFrom }),
91
+ /* @__PURE__ */ jsx("span", { className: s.price(), children: price })
92
+ ] }),
93
+ href ? /* @__PURE__ */ jsxs(BlockLink, { href, linkAs, className: s.action(), "aria-describedby": id, children: [
94
+ actionLabel,
95
+ " ",
96
+ arrow
97
+ ] }) : onSelect ? /* @__PURE__ */ jsxs("button", { type: "button", onClick: onSelect, "aria-pressed": selected ?? false, "aria-describedby": id, className: s.action(), children: [
98
+ actionLabel,
99
+ " ",
100
+ arrow
101
+ ] }) : null
102
+ ] })
103
+ ] })
104
+ ]
105
+ }
106
+ );
107
+ }
108
+ export {
109
+ ServiceCard,
110
+ serviceCard
111
+ };
112
+ //# sourceMappingURL=service-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/service-card/service-card.tsx"],"sourcesContent":["// Server-safe: no hook, no context. A service a shop sells, as a card that argues: icon or photo, name, duration, price\n// (« from » when it varies), what is included, the capacity, a « Popular » tag — and one way in: a link (the booking page\n// with this service chosen) or a button (`onSelect`, from a client parent). The whole card takes the press.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { CheckGlyph } from \"../glyphs\";\n\nexport const serviceCard = tv({\n slots: {\n root:\n \"group kz-spotlight kz-lift relative flex h-full min-w-0 flex-col overflow-hidden rounded-xl border border-border-default bg-surface-1 text-fg transition-colors \" +\n \"hover:border-accent/50 focus-within:ring-2 focus-within:ring-ring data-[selected]:border-accent data-[selected]:bg-accent-soft\",\n media: \"relative aspect-[16/9] w-full overflow-hidden bg-media [&>img]:h-full [&>img]:w-full [&>img]:object-cover\",\n body: \"flex flex-1 flex-col gap-3 p-4 sm:p-5\",\n head: \"flex items-start gap-3\",\n icon: \"inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-accent-soft text-fg-accent [&>svg]:h-5 [&>svg]:w-5\",\n titles: \"flex min-w-0 flex-1 flex-col gap-1\",\n name: \"m-0 font-display text-lg font-bold leading-tight tracking-tight text-fg\",\n facts: \"m-0 flex list-none flex-wrap gap-x-2 gap-y-0.5 p-0 text-xs text-fg-secondary\",\n tag: \"shrink-0 rounded-full bg-accent px-2 py-0.5 text-[10px] font-extrabold uppercase tracking-wide text-on-accent\",\n description: \"m-0 text-sm leading-relaxed text-fg-secondary\",\n included: \"m-0 flex list-none flex-col gap-1.5 p-0 text-sm text-fg-secondary\",\n includedItem: \"flex items-start gap-2 [&>svg]:mt-0.5 [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0 [&>svg]:text-fg-accent\",\n foot: \"mt-auto flex items-end justify-between gap-3 border-t border-border-subtle pt-3\",\n priceBlock: \"flex flex-col leading-none\",\n from: \"mb-1 text-[11px] font-semibold uppercase tracking-wider text-fg-secondary\",\n price: \"font-display text-2xl font-black tabular-nums text-fg\",\n action:\n \"inline-flex min-h-9 items-center gap-1 rounded-full text-sm font-bold text-fg-accent no-underline \" +\n \"after:absolute after:inset-0 after:content-[''] focus-visible:outline-hidden group-data-[selected]:text-fg\",\n },\n});\nconst s = serviceCard();\n\nexport type ServiceCardProps = Omit<React.ComponentProps<\"article\">, \"title\" | \"children\"> & {\n /** The service's name (the heading). */\n name: string;\n /** Its price, formatted by the app (`22,00 $`): the card never formats money. */\n price: string;\n /** The words before the price when it varies (`From`, `À partir de`), passed translated. */\n priceFrom?: string;\n /** How long it takes (`30 min`) — the first fact. */\n duration?: string;\n /** How many it takes (`Up to 5 dogs`, `1 person`) — the second fact. */\n capacity?: string;\n /** One or two sentences: what it is. */\n description?: string;\n /** What is included, one short line each (a check precedes it). */\n included?: readonly string[];\n /** A tag on the top right (`Popular`): only from real data, never decoration. */\n tag?: string;\n /** An icon tile before the name (a signature icon): decorative. */\n icon?: React.ReactNode;\n /** A photo or a loop above the card (an `img`, a `video`): fills a 16:9 band. */\n media?: React.ReactNode;\n /** This service is the chosen one (the accent, `data-selected`). */\n selected?: boolean;\n /** The words of the way in (`Book`, `Choose`): the accessible name of the press. */\n actionLabel: string;\n /** The way in, as an address (the booking page with this service preselected). */\n href?: string;\n /** The app's link component for internal addresses (Next's `Link`). */\n linkAs?: LinkComponent;\n /** The way in, as a press (a client parent). Ignored when `href` is set. */\n onSelect?: () => void;\n /**\n * The heading level of the name.\n * @default \"h3\"\n */\n titleAs?: \"h2\" | \"h3\" | \"h4\";\n /** Enters on scroll (MotionObserver): the index in the grid becomes the delay. */\n reveal?: number;\n};\n\n/** A service as a card: icon or photo, duration, price, what is included, the capacity, a tag — one way in. */\nexport function ServiceCard({\n name, price, priceFrom, duration, capacity, description, included, tag, icon, media, selected, actionLabel, href, linkAs, onSelect, titleAs: Title = \"h3\", reveal, className, style, ...props\n}: ServiceCardProps) {\n const id = React.useId();\n const facts = [duration, capacity].filter(Boolean);\n const arrow = <span aria-hidden>→</span>;\n const revealStyle = reveal === undefined ? style : ({ ...style, \"--kz-delay\": `${Math.min(Math.max(reveal, 0), 8) * 50}ms` } as React.CSSProperties);\n return (\n <article\n aria-labelledby={id}\n data-selected={selected ? \"\" : undefined}\n data-reveal={reveal === undefined ? undefined : \"\"}\n style={revealStyle}\n className={s.root({ className })}\n {...props}\n >\n {media && <div className={s.media()}>{media}</div>}\n <div className={s.body()}>\n <div className={s.head()}>\n {icon && <span aria-hidden className={s.icon()}>{icon}</span>}\n <div className={s.titles()}>\n <Title id={id} className={s.name()}>{name}</Title>\n {facts.length > 0 && (\n <ul className={s.facts()}>\n {facts.map((fact, i) => (\n <li key={i}>{i > 0 && <span aria-hidden>· </span>}{fact}</li>\n ))}\n </ul>\n )}\n </div>\n {tag && <span className={s.tag()}>{tag}</span>}\n </div>\n {description && <p className={s.description()}>{description}</p>}\n {included && included.length > 0 && (\n <ul className={s.included()}>\n {included.map((line) => (\n <li key={line} className={s.includedItem()}>\n <CheckGlyph />\n <span>{line}</span>\n </li>\n ))}\n </ul>\n )}\n <div className={s.foot()}>\n <p className={s.priceBlock()}>\n {priceFrom && <span className={s.from()}>{priceFrom}</span>}\n <span className={s.price()}>{price}</span>\n </p>\n {href ? (\n <BlockLink href={href} linkAs={linkAs} className={s.action()} aria-describedby={id}>\n {actionLabel} {arrow}\n </BlockLink>\n ) : onSelect ? (\n <button type=\"button\" onClick={onSelect} aria-pressed={selected ?? false} aria-describedby={id} className={s.action()}>\n {actionLabel} {arrow}\n </button>\n ) : null}\n </div>\n </div>\n </article>\n );\n}\n"],"mappings":";;;;;;;;AAGA,YAAY,WAAW;AACvB,SAAS,UAAU;AA8EH,cAoBE,YApBF;AAzET,IAAM,cAAc,GAAG;AAAA,EAC5B,OAAO;AAAA,IACL,MACE;AAAA,IAEF,OAAO;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,IACP,KAAK;AAAA,IACL,aAAa;AAAA,IACb,UAAU;AAAA,IACV,cAAc;AAAA,IACd,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QACE;AAAA,EAEJ;AACF,CAAC;AACD,IAAM,IAAI,YAAY;AA2Cf,SAAS,YAAY;AAAA,EAC1B;AAAA,EAAM;AAAA,EAAO;AAAA,EAAW;AAAA,EAAU;AAAA,EAAU;AAAA,EAAa;AAAA,EAAU;AAAA,EAAK;AAAA,EAAM;AAAA,EAAO;AAAA,EAAU;AAAA,EAAa;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAU,SAAS,QAAQ;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAO,GAAG;AAC1L,GAAqB;AACnB,QAAM,KAAW,YAAM;AACvB,QAAM,QAAQ,CAAC,UAAU,QAAQ,EAAE,OAAO,OAAO;AACjD,QAAM,QAAQ,oBAAC,UAAK,eAAW,MAAC,oBAAC;AACjC,QAAM,cAAc,WAAW,SAAY,QAAS,EAAE,GAAG,OAAO,cAAc,GAAG,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK;AAC3H,SACE;AAAA,IAAC;AAAA;AAAA,MACC,mBAAiB;AAAA,MACjB,iBAAe,WAAW,KAAK;AAAA,MAC/B,eAAa,WAAW,SAAY,SAAY;AAAA,MAChD,OAAO;AAAA,MACP,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC;AAAA,MAC9B,GAAG;AAAA,MAEH;AAAA,iBAAS,oBAAC,SAAI,WAAW,EAAE,MAAM,GAAI,iBAAM;AAAA,QAC5C,qBAAC,SAAI,WAAW,EAAE,KAAK,GACrB;AAAA,+BAAC,SAAI,WAAW,EAAE,KAAK,GACpB;AAAA,oBAAQ,oBAAC,UAAK,eAAW,MAAC,WAAW,EAAE,KAAK,GAAI,gBAAK;AAAA,YACtD,qBAAC,SAAI,WAAW,EAAE,OAAO,GACvB;AAAA,kCAAC,SAAM,IAAQ,WAAW,EAAE,KAAK,GAAI,gBAAK;AAAA,cACzC,MAAM,SAAS,KACd,oBAAC,QAAG,WAAW,EAAE,MAAM,GACpB,gBAAM,IAAI,CAAC,MAAM,MAChB,qBAAC,QAAY;AAAA,oBAAI,KAAK,oBAAC,UAAK,eAAW,MAAC,mBAAE;AAAA,gBAAS;AAAA,mBAA1C,CAA+C,CACzD,GACH;AAAA,eAEJ;AAAA,YACC,OAAO,oBAAC,UAAK,WAAW,EAAE,IAAI,GAAI,eAAI;AAAA,aACzC;AAAA,UACC,eAAe,oBAAC,OAAE,WAAW,EAAE,YAAY,GAAI,uBAAY;AAAA,UAC3D,YAAY,SAAS,SAAS,KAC7B,oBAAC,QAAG,WAAW,EAAE,SAAS,GACvB,mBAAS,IAAI,CAAC,SACb,qBAAC,QAAc,WAAW,EAAE,aAAa,GACvC;AAAA,gCAAC,cAAW;AAAA,YACZ,oBAAC,UAAM,gBAAK;AAAA,eAFL,IAGT,CACD,GACH;AAAA,UAEF,qBAAC,SAAI,WAAW,EAAE,KAAK,GACrB;AAAA,iCAAC,OAAE,WAAW,EAAE,WAAW,GACxB;AAAA,2BAAa,oBAAC,UAAK,WAAW,EAAE,KAAK,GAAI,qBAAU;AAAA,cACpD,oBAAC,UAAK,WAAW,EAAE,MAAM,GAAI,iBAAM;AAAA,eACrC;AAAA,YACC,OACC,qBAAC,aAAU,MAAY,QAAgB,WAAW,EAAE,OAAO,GAAG,oBAAkB,IAC7E;AAAA;AAAA,cAAY;AAAA,cAAE;AAAA,eACjB,IACE,WACF,qBAAC,YAAO,MAAK,UAAS,SAAS,UAAU,gBAAc,YAAY,OAAO,oBAAkB,IAAI,WAAW,EAAE,OAAO,GACjH;AAAA;AAAA,cAAY;AAAA,cAAE;AAAA,eACjB,IACE;AAAA,aACN;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -4,10 +4,10 @@ import {
4
4
  familyLabels,
5
5
  siteById,
6
6
  sitesByGroup
7
- } from "./chunk-2L67YTNV.js";
7
+ } from "./chunk-FTX66XZY.js";
8
8
  import {
9
9
  KrizakaLogo
10
- } from "./chunk-YCIBJ6OQ.js";
10
+ } from "./chunk-4DH2HW3D.js";
11
11
 
12
12
  // src/site-footer/site-footer.tsx
13
13
  import { Slot } from "radix-ui";
@@ -0,0 +1,119 @@
1
+ import * as tailwind_variants from 'tailwind-variants';
2
+ import * as React from 'react';
3
+ import { L as LinkComponent } from './block-link-BAg4eoZk.js';
4
+
5
+ declare const slotPicker: tailwind_variants.TVReturnType<{
6
+ [key: string]: {
7
+ [key: string]: tailwind_variants.ClassValue | {
8
+ time?: tailwind_variants.ClassValue;
9
+ note?: tailwind_variants.ClassValue;
10
+ root?: tailwind_variants.ClassValue;
11
+ days?: tailwind_variants.ClassValue;
12
+ dayName?: tailwind_variants.ClassValue;
13
+ daySub?: tailwind_variants.ClassValue;
14
+ times?: tailwind_variants.ClassValue;
15
+ empty?: tailwind_variants.ClassValue;
16
+ day?: tailwind_variants.ClassValue;
17
+ };
18
+ };
19
+ } | {
20
+ [x: string]: {
21
+ [x: string]: tailwind_variants.ClassValue | {
22
+ time?: tailwind_variants.ClassValue;
23
+ note?: tailwind_variants.ClassValue;
24
+ root?: tailwind_variants.ClassValue;
25
+ days?: tailwind_variants.ClassValue;
26
+ dayName?: tailwind_variants.ClassValue;
27
+ daySub?: tailwind_variants.ClassValue;
28
+ times?: tailwind_variants.ClassValue;
29
+ empty?: tailwind_variants.ClassValue;
30
+ day?: tailwind_variants.ClassValue;
31
+ };
32
+ };
33
+ } | {}, {
34
+ root: string;
35
+ days: string;
36
+ day: string;
37
+ dayName: string;
38
+ daySub: string;
39
+ times: string;
40
+ time: string;
41
+ note: string;
42
+ empty: string;
43
+ }, undefined, {
44
+ [key: string]: {
45
+ [key: string]: tailwind_variants.ClassValue | {
46
+ time?: tailwind_variants.ClassValue;
47
+ note?: tailwind_variants.ClassValue;
48
+ root?: tailwind_variants.ClassValue;
49
+ days?: tailwind_variants.ClassValue;
50
+ dayName?: tailwind_variants.ClassValue;
51
+ daySub?: tailwind_variants.ClassValue;
52
+ times?: tailwind_variants.ClassValue;
53
+ empty?: tailwind_variants.ClassValue;
54
+ day?: tailwind_variants.ClassValue;
55
+ };
56
+ };
57
+ } | {}, {
58
+ root: string;
59
+ days: string;
60
+ day: string;
61
+ dayName: string;
62
+ daySub: string;
63
+ times: string;
64
+ time: string;
65
+ note: string;
66
+ empty: string;
67
+ }, tailwind_variants.TVReturnTypeLike<unknown, {
68
+ root: string;
69
+ days: string;
70
+ day: string;
71
+ dayName: string;
72
+ daySub: string;
73
+ times: string;
74
+ time: string;
75
+ note: string;
76
+ empty: string;
77
+ }>>;
78
+ type PickerSlot = {
79
+ /** A stable id (the slot's start as an ISO string). */
80
+ id: string;
81
+ /** The time in words, formatted by the app (`9 h 30`). */
82
+ label: string;
83
+ /** What to know about it (`2 places left`), shown beside the time and read with it. */
84
+ note?: string;
85
+ /** Not offered any more: shown, disabled. */
86
+ disabled?: boolean;
87
+ /** Makes the time a link to this address (the booking page with the slot chosen). */
88
+ href?: string;
89
+ };
90
+ type PickerDay = {
91
+ /** A stable id (`2026-10-19`). */
92
+ id: string;
93
+ /** The day's name, translated (`Today`, `Mon`). */
94
+ label: string;
95
+ /** Under it (`19 Oct`). */
96
+ sub?: string;
97
+ /** Its free times, in order. */
98
+ slots: readonly PickerSlot[];
99
+ };
100
+ type SlotPickerProps = Omit<React.ComponentProps<"div">, "onChange" | "defaultValue"> & {
101
+ /** The days that have free times, in order. */
102
+ days: readonly PickerDay[];
103
+ /** The name of the group (`Available times`), passed translated. */
104
+ label: string;
105
+ /** Said when no day has a free time. */
106
+ emptyLabel: string;
107
+ /** The chosen slot's id (controlled). */
108
+ value?: string | null;
109
+ /** Called with the chosen slot's id. */
110
+ onValueChange?: (id: string) => void;
111
+ /** The first day shown. Default: the first of `days`. */
112
+ defaultDay?: string;
113
+ /** The app's link component for the times that have an `href`. */
114
+ linkAs?: LinkComponent;
115
+ };
116
+ /** Tappable free times by day: the day strip, then the day's chips — links or toggles. */
117
+ declare function SlotPicker({ days, label, emptyLabel, value, onValueChange, defaultDay, linkAs, className, ...props }: SlotPickerProps): React.JSX.Element;
118
+
119
+ export { type PickerDay, type PickerSlot, SlotPicker, type SlotPickerProps, slotPicker };
@@ -0,0 +1,46 @@
1
+ "use client";
2
+ import {
3
+ BlockLink
4
+ } from "./chunk-C7LTF5CC.js";
5
+
6
+ // src/slot-picker/slot-picker.tsx
7
+ import * as React from "react";
8
+ import { tv } from "tailwind-variants";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ var slotPicker = tv({
11
+ slots: {
12
+ root: "flex min-w-0 flex-col gap-3",
13
+ days: "flex gap-2 overflow-x-auto p-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
14
+ day: "flex min-h-12 min-w-[4.5rem] shrink-0 cursor-pointer flex-col items-center justify-center rounded-xl border border-border-default bg-surface-1 px-3 py-1.5 text-center leading-tight text-fg-secondary transition-colors hover:border-border-strong hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-pressed:border-accent aria-pressed:bg-accent-soft aria-pressed:text-fg",
15
+ dayName: "text-xs font-bold",
16
+ daySub: "text-[11px] tabular-nums text-fg-muted",
17
+ times: "m-0 flex list-none flex-wrap gap-2 p-0",
18
+ time: "inline-flex min-h-11 min-w-[4.75rem] items-center justify-center gap-1.5 rounded-full border border-border-default bg-surface-1 px-4 text-sm font-semibold tabular-nums text-fg no-underline transition-colors hover:border-accent/60 hover:bg-accent-soft focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-pressed:border-accent aria-pressed:bg-accent aria-pressed:text-on-accent disabled:cursor-not-allowed disabled:opacity-50",
19
+ note: "text-[11px] font-medium text-fg-secondary group-aria-pressed:text-on-accent",
20
+ empty: "m-0 rounded-xl border border-dashed border-border-default px-4 py-6 text-center text-sm text-fg-secondary"
21
+ }
22
+ });
23
+ var s = slotPicker();
24
+ function SlotPicker({ days, label, emptyLabel, value, onValueChange, defaultDay, linkAs, className, ...props }) {
25
+ const [dayId, setDayId] = React.useState(defaultDay ?? days[0]?.id);
26
+ const active = days.find((d) => d.id === dayId) ?? days[0];
27
+ if (!active) return /* @__PURE__ */ jsx("p", { className: s.empty({ className }), children: emptyLabel });
28
+ return /* @__PURE__ */ jsxs("div", { className: s.root({ className }), ...props, children: [
29
+ /* @__PURE__ */ jsx("div", { className: s.days(), role: "group", "aria-label": label, children: days.map((day) => /* @__PURE__ */ jsxs("button", { type: "button", "aria-pressed": day.id === active.id, onClick: () => setDayId(day.id), className: s.day(), children: [
30
+ /* @__PURE__ */ jsx("span", { className: s.dayName(), children: day.label }),
31
+ day.sub && /* @__PURE__ */ jsx("span", { className: s.daySub(), children: day.sub })
32
+ ] }, day.id)) }),
33
+ /* @__PURE__ */ jsx("ul", { className: s.times(), "aria-label": `${active.label}${active.sub ? ` ${active.sub}` : ""}`, children: active.slots.map((slot) => /* @__PURE__ */ jsx("li", { children: slot.href ? /* @__PURE__ */ jsxs(BlockLink, { href: slot.href, linkAs, className: s.time(), children: [
34
+ slot.label,
35
+ slot.note && /* @__PURE__ */ jsx("span", { className: s.note(), children: slot.note })
36
+ ] }) : /* @__PURE__ */ jsxs("button", { type: "button", "aria-pressed": value === slot.id, disabled: slot.disabled, onClick: () => onValueChange?.(slot.id), className: s.time({ className: "group" }), children: [
37
+ slot.label,
38
+ slot.note && /* @__PURE__ */ jsx("span", { className: s.note(), children: slot.note })
39
+ ] }) }, slot.id)) })
40
+ ] });
41
+ }
42
+ export {
43
+ SlotPicker,
44
+ slotPicker
45
+ };
46
+ //# sourceMappingURL=slot-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/slot-picker/slot-picker.tsx"],"sourcesContent":["// Client (the chosen day). The next free times of a service as tappable chips: a strip of days, then the day's times.\n// A time is a link (`href`: the booking page with the time chosen) or a toggle the reader presses (`value`,\n// `onValueChange`). The app computes the slots (its availability is the truth) and formats the labels.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { BlockLink, type LinkComponent } from \"../block-link\";\n\nexport const slotPicker = tv({\n slots: {\n root: \"flex min-w-0 flex-col gap-3\",\n days: \"flex gap-2 overflow-x-auto p-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\",\n day:\n \"flex min-h-12 min-w-[4.5rem] shrink-0 cursor-pointer flex-col items-center justify-center rounded-xl border border-border-default bg-surface-1 px-3 py-1.5 text-center leading-tight text-fg-secondary \" +\n \"transition-colors hover:border-border-strong hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring \" +\n \"aria-pressed:border-accent aria-pressed:bg-accent-soft aria-pressed:text-fg\",\n dayName: \"text-xs font-bold\",\n daySub: \"text-[11px] tabular-nums text-fg-muted\",\n times: \"m-0 flex list-none flex-wrap gap-2 p-0\",\n time:\n \"inline-flex min-h-11 min-w-[4.75rem] items-center justify-center gap-1.5 rounded-full border border-border-default bg-surface-1 px-4 text-sm font-semibold tabular-nums text-fg no-underline \" +\n \"transition-colors hover:border-accent/60 hover:bg-accent-soft focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring \" +\n \"aria-pressed:border-accent aria-pressed:bg-accent aria-pressed:text-on-accent disabled:cursor-not-allowed disabled:opacity-50\",\n note: \"text-[11px] font-medium text-fg-secondary group-aria-pressed:text-on-accent\",\n empty: \"m-0 rounded-xl border border-dashed border-border-default px-4 py-6 text-center text-sm text-fg-secondary\",\n },\n});\nconst s = slotPicker();\n\nexport type PickerSlot = {\n /** A stable id (the slot's start as an ISO string). */\n id: string;\n /** The time in words, formatted by the app (`9 h 30`). */\n label: string;\n /** What to know about it (`2 places left`), shown beside the time and read with it. */\n note?: string;\n /** Not offered any more: shown, disabled. */\n disabled?: boolean;\n /** Makes the time a link to this address (the booking page with the slot chosen). */\n href?: string;\n};\n\nexport type PickerDay = {\n /** A stable id (`2026-10-19`). */\n id: string;\n /** The day's name, translated (`Today`, `Mon`). */\n label: string;\n /** Under it (`19 Oct`). */\n sub?: string;\n /** Its free times, in order. */\n slots: readonly PickerSlot[];\n};\n\nexport type SlotPickerProps = Omit<React.ComponentProps<\"div\">, \"onChange\" | \"defaultValue\"> & {\n /** The days that have free times, in order. */\n days: readonly PickerDay[];\n /** The name of the group (`Available times`), passed translated. */\n label: string;\n /** Said when no day has a free time. */\n emptyLabel: string;\n /** The chosen slot's id (controlled). */\n value?: string | null;\n /** Called with the chosen slot's id. */\n onValueChange?: (id: string) => void;\n /** The first day shown. Default: the first of `days`. */\n defaultDay?: string;\n /** The app's link component for the times that have an `href`. */\n linkAs?: LinkComponent;\n};\n\n/** Tappable free times by day: the day strip, then the day's chips — links or toggles. */\nexport function SlotPicker({ days, label, emptyLabel, value, onValueChange, defaultDay, linkAs, className, ...props }: SlotPickerProps) {\n const [dayId, setDayId] = React.useState(defaultDay ?? days[0]?.id);\n const active = days.find((d) => d.id === dayId) ?? days[0];\n if (!active) return <p className={s.empty({ className })}>{emptyLabel}</p>;\n return (\n <div className={s.root({ className })} {...props}>\n <div className={s.days()} role=\"group\" aria-label={label}>\n {days.map((day) => (\n <button key={day.id} type=\"button\" aria-pressed={day.id === active.id} onClick={() => setDayId(day.id)} className={s.day()}>\n <span className={s.dayName()}>{day.label}</span>\n {day.sub && <span className={s.daySub()}>{day.sub}</span>}\n </button>\n ))}\n </div>\n <ul className={s.times()} aria-label={`${active.label}${active.sub ? ` ${active.sub}` : \"\"}`}>\n {active.slots.map((slot) => (\n <li key={slot.id}>\n {slot.href ? (\n <BlockLink href={slot.href} linkAs={linkAs} className={s.time()}>\n {slot.label}\n {slot.note && <span className={s.note()}>{slot.note}</span>}\n </BlockLink>\n ) : (\n <button type=\"button\" aria-pressed={value === slot.id} disabled={slot.disabled} onClick={() => onValueChange?.(slot.id)} className={s.time({ className: \"group\" })}>\n {slot.label}\n {slot.note && <span className={s.note()}>{slot.note}</span>}\n </button>\n )}\n </li>\n ))}\n </ul>\n </div>\n );\n}\n"],"mappings":";;;;;;AAGA,YAAY,WAAW;AACvB,SAAS,UAAU;AAsEG,cAKZ,YALY;AAlEf,IAAM,aAAa,GAAG;AAAA,EAC3B,OAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,KACE;AAAA,IAGF,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,MACE;AAAA,IAGF,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AACF,CAAC;AACD,IAAM,IAAI,WAAW;AA4Cd,SAAS,WAAW,EAAE,MAAM,OAAO,YAAY,OAAO,eAAe,YAAY,QAAQ,WAAW,GAAG,MAAM,GAAoB;AACtI,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAS,cAAc,KAAK,CAAC,GAAG,EAAE;AAClE,QAAM,SAAS,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,KAAK,KAAK,CAAC;AACzD,MAAI,CAAC,OAAQ,QAAO,oBAAC,OAAE,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,GAAI,sBAAW;AACtE,SACE,qBAAC,SAAI,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OACzC;AAAA,wBAAC,SAAI,WAAW,EAAE,KAAK,GAAG,MAAK,SAAQ,cAAY,OAChD,eAAK,IAAI,CAAC,QACT,qBAAC,YAAoB,MAAK,UAAS,gBAAc,IAAI,OAAO,OAAO,IAAI,SAAS,MAAM,SAAS,IAAI,EAAE,GAAG,WAAW,EAAE,IAAI,GACvH;AAAA,0BAAC,UAAK,WAAW,EAAE,QAAQ,GAAI,cAAI,OAAM;AAAA,MACxC,IAAI,OAAO,oBAAC,UAAK,WAAW,EAAE,OAAO,GAAI,cAAI,KAAI;AAAA,SAFvC,IAAI,EAGjB,CACD,GACH;AAAA,IACA,oBAAC,QAAG,WAAW,EAAE,MAAM,GAAG,cAAY,GAAG,OAAO,KAAK,GAAG,OAAO,MAAM,IAAI,OAAO,GAAG,KAAK,EAAE,IACvF,iBAAO,MAAM,IAAI,CAAC,SACjB,oBAAC,QACE,eAAK,OACJ,qBAAC,aAAU,MAAM,KAAK,MAAM,QAAgB,WAAW,EAAE,KAAK,GAC3D;AAAA,WAAK;AAAA,MACL,KAAK,QAAQ,oBAAC,UAAK,WAAW,EAAE,KAAK,GAAI,eAAK,MAAK;AAAA,OACtD,IAEA,qBAAC,YAAO,MAAK,UAAS,gBAAc,UAAU,KAAK,IAAI,UAAU,KAAK,UAAU,SAAS,MAAM,gBAAgB,KAAK,EAAE,GAAG,WAAW,EAAE,KAAK,EAAE,WAAW,QAAQ,CAAC,GAC9J;AAAA,WAAK;AAAA,MACL,KAAK,QAAQ,oBAAC,UAAK,WAAW,EAAE,KAAK,GAAI,eAAK,MAAK;AAAA,OACtD,KAVK,KAAK,EAYd,CACD,GACH;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,12 @@
1
+ import * as tailwind_variants from 'tailwind-variants';
2
+ import * as React from 'react';
3
+
4
+ declare const stickyAction: tailwind_variants.TVReturnType<{} | {} | {}, undefined, "sticky bottom-0 z-30 border-t border-border-subtle bg-surface-1/90 px-4 pb-[max(0.75rem,env(safe-area-inset-bottom))] pt-3 backdrop-blur-xl md:hidden", {} | {}, undefined, tailwind_variants.TVReturnTypeLike<unknown, undefined>>;
5
+ type StickyActionProps = React.ComponentProps<"div"> & {
6
+ /** The action: one `ButtonLink` (full width is added). */
7
+ children: React.ReactNode;
8
+ };
9
+ /** The one action of a page, held at the bottom edge of a phone. */
10
+ declare function StickyAction({ className, children, ...props }: StickyActionProps): React.JSX.Element;
11
+
12
+ export { StickyAction, type StickyActionProps, stickyAction };
@@ -0,0 +1,14 @@
1
+ // src/sticky-action/sticky-action.tsx
2
+ import { tv } from "tailwind-variants";
3
+ import { jsx } from "react/jsx-runtime";
4
+ var stickyAction = tv({
5
+ base: "sticky bottom-0 z-30 border-t border-border-subtle bg-surface-1/90 px-4 pb-[max(0.75rem,env(safe-area-inset-bottom))] pt-3 backdrop-blur-xl md:hidden"
6
+ });
7
+ function StickyAction({ className, children, ...props }) {
8
+ return /* @__PURE__ */ jsx("div", { className: stickyAction({ className }), ...props, children: /* @__PURE__ */ jsx("div", { className: "mx-auto flex max-w-md flex-col [&>*]:w-full", children }) });
9
+ }
10
+ export {
11
+ StickyAction,
12
+ stickyAction
13
+ };
14
+ //# sourceMappingURL=sticky-action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sticky-action/sticky-action.tsx"],"sourcesContent":["// Server-safe: pure CSS. The main action of a page, stuck to the bottom of a phone's screen while the page scrolls: put it\n// as the last child of the page's `main` (a sticky element holds the viewport's edge inside its container). From `md` up it\n// is not drawn — the page's own buttons are within reach.\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const stickyAction = tv({\n base: \"sticky bottom-0 z-30 border-t border-border-subtle bg-surface-1/90 px-4 pb-[max(0.75rem,env(safe-area-inset-bottom))] pt-3 backdrop-blur-xl md:hidden\",\n});\n\nexport type StickyActionProps = React.ComponentProps<\"div\"> & {\n /** The action: one `ButtonLink` (full width is added). */\n children: React.ReactNode;\n};\n\n/** The one action of a page, held at the bottom edge of a phone. */\nexport function StickyAction({ className, children, ...props }: StickyActionProps) {\n return (\n <div className={stickyAction({ className })} {...props}>\n <div className=\"mx-auto flex max-w-md flex-col [&>*]:w-full\">{children}</div>\n </div>\n );\n}\n"],"mappings":";AAIA,SAAS,UAAU;AAeb;AAbC,IAAM,eAAe,GAAG;AAAA,EAC7B,MAAM;AACR,CAAC;AAQM,SAAS,aAAa,EAAE,WAAW,UAAU,GAAG,MAAM,GAAsB;AACjF,SACE,oBAAC,SAAI,WAAW,aAAa,EAAE,UAAU,CAAC,GAAI,GAAG,OAC/C,8BAAC,SAAI,WAAU,+CAA+C,UAAS,GACzE;AAEJ;","names":[]}