@krizaka/ui 3.4.0 → 3.6.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 (337) hide show
  1. package/README.md +1 -0
  2. package/dist/app-shell.d.ts +24 -22
  3. package/dist/app-shell.js +9 -3
  4. package/dist/app-shell.js.map +1 -1
  5. package/dist/argument-card.js +3 -3
  6. package/dist/chat.d.ts +187 -0
  7. package/dist/chat.js +132 -0
  8. package/dist/chat.js.map +1 -0
  9. package/dist/chunk-3GODX3D6.js +25 -0
  10. package/dist/chunk-3GODX3D6.js.map +1 -0
  11. package/dist/chunk-ECSCA3CN.js +128 -0
  12. package/dist/chunk-ECSCA3CN.js.map +1 -0
  13. package/dist/chunk-F2GKFIYK.js +47 -0
  14. package/dist/chunk-F2GKFIYK.js.map +1 -0
  15. package/dist/{chunk-R6SDS7JM.js → chunk-ND3L5VHT.js} +25 -2
  16. package/dist/chunk-ND3L5VHT.js.map +1 -0
  17. package/dist/command.js +1 -1
  18. package/dist/data-table.js +20 -134
  19. package/dist/data-table.js.map +1 -1
  20. package/dist/dialog.js +7 -3
  21. package/dist/dialog.js.map +1 -1
  22. package/dist/loader.d.ts +21 -1
  23. package/dist/loader.js +7 -3
  24. package/dist/loader.js.map +1 -1
  25. package/dist/map.js +7 -1
  26. package/dist/map.js.map +1 -1
  27. package/dist/marketing-layout.d.ts +3 -9
  28. package/dist/marketing-layout.js +24 -7
  29. package/dist/marketing-layout.js.map +1 -1
  30. package/dist/match-dialog.d.ts +32 -0
  31. package/dist/match-dialog.js +168 -0
  32. package/dist/match-dialog.js.map +1 -0
  33. package/dist/native.d.ts +484 -106
  34. package/dist/native.js +1967 -1323
  35. package/dist/native.js.map +1 -1
  36. package/dist/page-hero.js +3 -3
  37. package/dist/photo-grid.d.ts +122 -0
  38. package/dist/photo-grid.js +94 -0
  39. package/dist/photo-grid.js.map +1 -0
  40. package/dist/skip-link.d.ts +15 -0
  41. package/dist/skip-link.js +10 -0
  42. package/dist/skip-link.js.map +1 -0
  43. package/dist/step-progress.d.ts +20 -0
  44. package/dist/step-progress.js +17 -0
  45. package/dist/step-progress.js.map +1 -0
  46. package/dist/swipe-deck.d.ts +183 -0
  47. package/dist/swipe-deck.js +144 -0
  48. package/dist/swipe-deck.js.map +1 -0
  49. package/dist/tag-selector.d.ts +26 -0
  50. package/dist/tag-selector.js +21 -0
  51. package/dist/tag-selector.js.map +1 -0
  52. package/dist/tree-view.d.ts +2 -0
  53. package/dist/tree-view.js.map +1 -1
  54. package/dist/verified-badge.d.ts +16 -0
  55. package/dist/verified-badge.js +23 -0
  56. package/dist/verified-badge.js.map +1 -0
  57. package/package.json +120 -8
  58. package/registry/achievement.json +2 -1
  59. package/registry/action-bar.json +2 -1
  60. package/registry/action-sheet.json +1 -0
  61. package/registry/activity-feed.json +2 -1
  62. package/registry/alert.json +2 -1
  63. package/registry/app-shell.json +27 -5
  64. package/registry/argument-card.json +2 -1
  65. package/registry/audio-player.json +2 -1
  66. package/registry/auth-layout.json +7 -2
  67. package/registry/avatar.json +2 -1
  68. package/registry/badge.json +2 -1
  69. package/registry/button.json +2 -1
  70. package/registry/card-grid.json +2 -1
  71. package/registry/card.json +2 -1
  72. package/registry/catalogue-page.json +5 -2
  73. package/registry/changelog-page.json +5 -2
  74. package/registry/chat.json +918 -0
  75. package/registry/checkbox.json +2 -1
  76. package/registry/chip-link.json +2 -1
  77. package/registry/chip.json +2 -1
  78. package/registry/choice-grid.json +2 -1
  79. package/registry/cn.json +1 -0
  80. package/registry/code-block.json +2 -1
  81. package/registry/combobox.json +2 -1
  82. package/registry/command.json +2 -1
  83. package/registry/component-page.json +5 -2
  84. package/registry/confirm-button.json +2 -1
  85. package/registry/contact-page.json +5 -2
  86. package/registry/continue-prompt.json +2 -1
  87. package/registry/countdown.json +2 -1
  88. package/registry/cta-panel.json +2 -1
  89. package/registry/dashboard-layout.json +5 -2
  90. package/registry/data-table.json +2 -1
  91. package/registry/date-picker.json +3 -2
  92. package/registry/decision-list.json +2 -1
  93. package/registry/description-list.json +2 -1
  94. package/registry/detail-layout.json +16 -2
  95. package/registry/dialog.json +10 -4
  96. package/registry/docs-article-page.json +16 -2
  97. package/registry/docs-hub-page.json +5 -2
  98. package/registry/docs-layout.json +2 -1
  99. package/registry/dropdown-menu.json +2 -1
  100. package/registry/empty-state.json +14 -2
  101. package/registry/error-page.json +5 -2
  102. package/registry/examples/chat/conversation.d.ts +4 -0
  103. package/registry/examples/chat/conversation.js +22 -0
  104. package/registry/examples/chat/conversation.tsx +21 -0
  105. package/registry/examples/chat/inbox.d.ts +4 -0
  106. package/registry/examples/chat/inbox.js +15 -0
  107. package/registry/examples/chat/inbox.tsx +12 -0
  108. package/registry/examples/chat/native/conversation.d.ts +4 -0
  109. package/registry/examples/chat/native/conversation.dark.png +0 -0
  110. package/registry/examples/chat/native/conversation.js +23 -0
  111. package/registry/examples/chat/native/conversation.light.png +0 -0
  112. package/registry/examples/chat/native/conversation.tsx +20 -0
  113. package/registry/examples/chat/native/inbox.d.ts +4 -0
  114. package/registry/examples/chat/native/inbox.dark.png +0 -0
  115. package/registry/examples/chat/native/inbox.js +16 -0
  116. package/registry/examples/chat/native/inbox.light.png +0 -0
  117. package/registry/examples/chat/native/inbox.tsx +13 -0
  118. package/registry/examples/detail-layout/checkout.d.ts +4 -0
  119. package/registry/examples/detail-layout/checkout.js +37 -0
  120. package/registry/examples/detail-layout/checkout.tsx +44 -0
  121. package/registry/examples/docs-article-page/faq.d.ts +4 -0
  122. package/registry/examples/docs-article-page/faq.js +31 -0
  123. package/registry/examples/docs-article-page/faq.tsx +40 -0
  124. package/registry/examples/empty-state/library.d.ts +4 -0
  125. package/registry/examples/empty-state/library.js +25 -0
  126. package/registry/examples/empty-state/library.tsx +20 -0
  127. package/registry/examples/loader/branded.d.ts +4 -0
  128. package/registry/examples/loader/branded.js +12 -0
  129. package/registry/examples/loader/branded.tsx +7 -0
  130. package/registry/examples/loader/native/branded.d.ts +4 -0
  131. package/registry/examples/loader/native/branded.dark.png +0 -0
  132. package/registry/examples/loader/native/branded.js +12 -0
  133. package/registry/examples/loader/native/branded.light.png +0 -0
  134. package/registry/examples/loader/native/branded.tsx +11 -0
  135. package/registry/examples/longform-page/case-study.d.ts +4 -0
  136. package/registry/examples/longform-page/case-study.js +41 -0
  137. package/registry/examples/longform-page/case-study.tsx +40 -0
  138. package/registry/examples/match-dialog/its-a-match.d.ts +4 -0
  139. package/registry/examples/match-dialog/its-a-match.js +29 -0
  140. package/registry/examples/match-dialog/its-a-match.tsx +25 -0
  141. package/registry/examples/match-dialog/native/its-a-match.d.ts +4 -0
  142. package/registry/examples/match-dialog/native/its-a-match.dark.png +0 -0
  143. package/registry/examples/match-dialog/native/its-a-match.js +28 -0
  144. package/registry/examples/match-dialog/native/its-a-match.light.png +0 -0
  145. package/registry/examples/match-dialog/native/its-a-match.tsx +22 -0
  146. package/registry/examples/photo-grid/native/profile-photos.d.ts +4 -0
  147. package/registry/examples/photo-grid/native/profile-photos.dark.png +0 -0
  148. package/registry/examples/photo-grid/native/profile-photos.js +27 -0
  149. package/registry/examples/photo-grid/native/profile-photos.light.png +0 -0
  150. package/registry/examples/photo-grid/native/profile-photos.tsx +24 -0
  151. package/registry/examples/photo-grid/profile-photos.d.ts +4 -0
  152. package/registry/examples/photo-grid/profile-photos.js +28 -0
  153. package/registry/examples/photo-grid/profile-photos.tsx +25 -0
  154. package/registry/examples/pull-to-refresh/native/branded.d.ts +4 -0
  155. package/registry/examples/pull-to-refresh/native/branded.dark.png +0 -0
  156. package/registry/examples/pull-to-refresh/native/branded.js +14 -0
  157. package/registry/examples/pull-to-refresh/native/branded.light.png +0 -0
  158. package/registry/examples/pull-to-refresh/native/branded.tsx +23 -0
  159. package/registry/examples/screen/native/app-shell.d.ts +4 -0
  160. package/registry/examples/screen/native/app-shell.dark.png +0 -0
  161. package/registry/examples/screen/native/app-shell.js +25 -0
  162. package/registry/examples/screen/native/app-shell.light.png +0 -0
  163. package/registry/examples/screen/native/app-shell.tsx +33 -0
  164. package/registry/examples/screen/native/auth.d.ts +4 -0
  165. package/registry/examples/screen/native/auth.dark.png +0 -0
  166. package/registry/examples/screen/native/auth.js +24 -0
  167. package/registry/examples/screen/native/auth.light.png +0 -0
  168. package/registry/examples/screen/native/auth.tsx +20 -0
  169. package/registry/examples/screen/native/chat.d.ts +4 -0
  170. package/registry/examples/screen/native/chat.dark.png +0 -0
  171. package/registry/examples/screen/native/chat.js +27 -0
  172. package/registry/examples/screen/native/chat.light.png +0 -0
  173. package/registry/examples/screen/native/chat.tsx +22 -0
  174. package/registry/examples/screen/native/feed.d.ts +4 -0
  175. package/registry/examples/screen/native/feed.dark.png +0 -0
  176. package/registry/examples/screen/native/feed.js +24 -0
  177. package/registry/examples/screen/native/feed.light.png +0 -0
  178. package/registry/examples/screen/native/feed.tsx +34 -0
  179. package/registry/examples/screen/native/map.d.ts +4 -0
  180. package/registry/examples/screen/native/map.dark.png +0 -0
  181. package/registry/examples/screen/native/map.js +26 -0
  182. package/registry/examples/screen/native/map.light.png +0 -0
  183. package/registry/examples/screen/native/map.tsx +31 -0
  184. package/registry/examples/screen/native/notifications.d.ts +4 -0
  185. package/registry/examples/screen/native/notifications.dark.png +0 -0
  186. package/registry/examples/screen/native/notifications.js +21 -0
  187. package/registry/examples/screen/native/notifications.light.png +0 -0
  188. package/registry/examples/screen/native/notifications.tsx +21 -0
  189. package/registry/examples/screen/native/profile.d.ts +4 -0
  190. package/registry/examples/screen/native/profile.dark.png +0 -0
  191. package/registry/examples/screen/native/profile.js +29 -0
  192. package/registry/examples/screen/native/profile.light.png +0 -0
  193. package/registry/examples/screen/native/profile.tsx +27 -0
  194. package/registry/examples/screen/native/settings.d.ts +4 -0
  195. package/registry/examples/screen/native/settings.dark.png +0 -0
  196. package/registry/examples/screen/native/settings.js +20 -0
  197. package/registry/examples/screen/native/settings.light.png +0 -0
  198. package/registry/examples/screen/native/settings.tsx +19 -0
  199. package/registry/examples/skip-link/default.d.ts +4 -0
  200. package/registry/examples/skip-link/default.js +19 -0
  201. package/registry/examples/skip-link/default.tsx +12 -0
  202. package/registry/examples/splash-handoff/native/default.d.ts +4 -0
  203. package/registry/examples/splash-handoff/native/default.dark.png +0 -0
  204. package/registry/examples/splash-handoff/native/default.js +12 -0
  205. package/registry/examples/splash-handoff/native/default.light.png +0 -0
  206. package/registry/examples/splash-handoff/native/default.tsx +11 -0
  207. package/registry/examples/split-view/nearby.d.ts +4 -0
  208. package/registry/examples/split-view/nearby.js +33 -0
  209. package/registry/examples/split-view/nearby.tsx +45 -0
  210. package/registry/examples/split-view/notifications.d.ts +4 -0
  211. package/registry/examples/split-view/notifications.js +33 -0
  212. package/registry/examples/split-view/notifications.tsx +31 -0
  213. package/registry/examples/status-page/maintenance.d.ts +4 -0
  214. package/registry/examples/status-page/maintenance.js +29 -0
  215. package/registry/examples/status-page/maintenance.tsx +24 -0
  216. package/registry/examples/step-progress/native/sign-up.d.ts +4 -0
  217. package/registry/examples/step-progress/native/sign-up.dark.png +0 -0
  218. package/registry/examples/step-progress/native/sign-up.js +12 -0
  219. package/registry/examples/step-progress/native/sign-up.light.png +0 -0
  220. package/registry/examples/step-progress/native/sign-up.tsx +11 -0
  221. package/registry/examples/step-progress/sign-up.d.ts +4 -0
  222. package/registry/examples/step-progress/sign-up.js +22 -0
  223. package/registry/examples/step-progress/sign-up.tsx +16 -0
  224. package/registry/examples/swipe-deck/discover.d.ts +4 -0
  225. package/registry/examples/swipe-deck/discover.js +41 -0
  226. package/registry/examples/swipe-deck/discover.tsx +37 -0
  227. package/registry/examples/swipe-deck/native/discover.d.ts +4 -0
  228. package/registry/examples/swipe-deck/native/discover.dark.png +0 -0
  229. package/registry/examples/swipe-deck/native/discover.js +39 -0
  230. package/registry/examples/swipe-deck/native/discover.light.png +0 -0
  231. package/registry/examples/swipe-deck/native/discover.tsx +35 -0
  232. package/registry/examples/tag-selector/interests.d.ts +4 -0
  233. package/registry/examples/tag-selector/interests.js +14 -0
  234. package/registry/examples/tag-selector/interests.tsx +12 -0
  235. package/registry/examples/tag-selector/native/interests.d.ts +4 -0
  236. package/registry/examples/tag-selector/native/interests.dark.png +0 -0
  237. package/registry/examples/tag-selector/native/interests.js +15 -0
  238. package/registry/examples/tag-selector/native/interests.light.png +0 -0
  239. package/registry/examples/tag-selector/native/interests.tsx +15 -0
  240. package/registry/examples/verified-badge/native/verified.d.ts +4 -0
  241. package/registry/examples/verified-badge/native/verified.dark.png +0 -0
  242. package/registry/examples/verified-badge/native/verified.js +18 -0
  243. package/registry/examples/verified-badge/native/verified.light.png +0 -0
  244. package/registry/examples/verified-badge/native/verified.tsx +15 -0
  245. package/registry/examples/verified-badge/verified.d.ts +4 -0
  246. package/registry/examples/verified-badge/verified.js +17 -0
  247. package/registry/examples/verified-badge/verified.tsx +13 -0
  248. package/registry/fab.json +1 -0
  249. package/registry/field.json +2 -1
  250. package/registry/file-upload.json +3 -2
  251. package/registry/filter-bar.json +2 -1
  252. package/registry/form.json +3 -2
  253. package/registry/gallery-page.json +5 -2
  254. package/registry/glow-card.json +2 -1
  255. package/registry/goal-gauge.json +2 -1
  256. package/registry/header.json +1 -0
  257. package/registry/how-it-works-page.json +5 -2
  258. package/registry/image-cropper.json +2 -1
  259. package/registry/image-viewer.json +1 -0
  260. package/registry/index.json +608 -19
  261. package/registry/iso.json +2 -1
  262. package/registry/kanban.json +2 -1
  263. package/registry/kbd.json +2 -1
  264. package/registry/landing-page.json +5 -2
  265. package/registry/landscape.json +2 -1
  266. package/registry/legal-page.json +5 -2
  267. package/registry/link-card.json +2 -1
  268. package/registry/list.json +2 -1
  269. package/registry/loader.json +47 -3
  270. package/registry/longform-page.json +16 -2
  271. package/registry/map.json +4 -3
  272. package/registry/marketing-layout.json +13 -4
  273. package/registry/marquee.json +2 -1
  274. package/registry/mascot-quote.json +2 -1
  275. package/registry/mascot-state.json +2 -1
  276. package/registry/mascots.json +2 -1
  277. package/registry/match-dialog.json +315 -0
  278. package/registry/media-capture.json +2 -1
  279. package/registry/media-picker.json +1 -0
  280. package/registry/meter.json +2 -1
  281. package/registry/nav-bar.json +2 -1
  282. package/registry/number-ticker.json +2 -1
  283. package/registry/onboarding.json +1 -0
  284. package/registry/otp-input.json +1 -0
  285. package/registry/page-header.json +2 -1
  286. package/registry/page-hero.json +2 -1
  287. package/registry/page.json +2 -1
  288. package/registry/pagination.json +2 -1
  289. package/registry/permission-prompt.json +1 -0
  290. package/registry/photo-grid.json +303 -0
  291. package/registry/pointer-light.json +2 -1
  292. package/registry/popover.json +2 -1
  293. package/registry/pricing-page.json +5 -2
  294. package/registry/product-page.json +5 -2
  295. package/registry/profile-card.json +2 -1
  296. package/registry/progress.json +2 -1
  297. package/registry/promise-card.json +2 -1
  298. package/registry/pull-to-refresh.json +68 -4
  299. package/registry/radio-group.json +2 -1
  300. package/registry/screen.json +233 -0
  301. package/registry/scroll-stage.json +2 -1
  302. package/registry/search-field.json +2 -1
  303. package/registry/section-backdrop.json +2 -1
  304. package/registry/segmented.json +1 -0
  305. package/registry/separator.json +2 -1
  306. package/registry/settings-layout.json +5 -2
  307. package/registry/skeleton.json +2 -1
  308. package/registry/skip-link.json +122 -0
  309. package/registry/slider.json +2 -1
  310. package/registry/slot.json +2 -1
  311. package/registry/spinner.json +2 -1
  312. package/registry/splash-handoff.json +138 -0
  313. package/registry/split-view.json +27 -2
  314. package/registry/stage-banner.json +2 -1
  315. package/registry/stat.json +2 -1
  316. package/registry/status-page.json +18 -2
  317. package/registry/step-progress.json +256 -0
  318. package/registry/stepper.json +2 -1
  319. package/registry/swipe-deck.json +513 -0
  320. package/registry/swipeable-row.json +1 -0
  321. package/registry/switch.json +2 -1
  322. package/registry/tab-bar.json +1 -0
  323. package/registry/tabs.json +2 -1
  324. package/registry/tag-input.json +2 -1
  325. package/registry/tag-selector.json +277 -0
  326. package/registry/terminal-replay.json +2 -1
  327. package/registry/theme.json +2 -1
  328. package/registry/toast.json +2 -1
  329. package/registry/toc.json +2 -1
  330. package/registry/tooltip.json +2 -1
  331. package/registry/tree-view.json +10 -4
  332. package/registry/txt.json +1 -0
  333. package/registry/verified-badge.json +203 -0
  334. package/registry/video-player.json +2 -1
  335. package/registry/video-trimmer.json +2 -1
  336. package/registry/virtual.json +2 -1
  337. package/dist/chunk-R6SDS7JM.js.map +0 -1
package/README.md CHANGED
@@ -144,6 +144,7 @@ light or a product's — is a set of tokens.
144
144
  | `@krizaka/ui/page-header` | `PageHeader` (`title`, `description`, `actions`, `breadcrumb`, `as` h1·h2) | server |
145
145
  | `@krizaka/ui/alert` | `Alert` (`tone` info·success·warning·danger, `icon`, `title`, `action`; `role="alert"` for danger and warning, `status` otherwise), `alertVariants` | server |
146
146
  | `@krizaka/ui/separator` | `Separator` (`orientation`, `decorative`, Radix), `separatorVariants` | server |
147
+ | `@krizaka/ui/skip-link` | `SkipLink` (`href`, default `#content`; the label as children): the first tab stop, hidden until focused, Enter moves focus to the content (WCAG 2.4.1) | client |
147
148
  | `@krizaka/ui/kbd` | `Kbd` (`size` sm·md), `kbdVariants` | server |
148
149
  | `@krizaka/ui/tabs` | `Tabs.Root/List/Trigger/Content` (`variant` underline·segmented·pills on the root, `orientation`, `value`/`defaultValue` + `onValueChange`; one light glides to the active tab), `tabs` (the slots) | client |
149
150
  | `@krizaka/ui/nav-bar` | `NavBar.Root` (`label`, `compactAfter`, `floating`) `/Brand/Items/Link` (`active`) `/Menu` (`label`, `columns`) `/MenuLink` (`title`, `description`, `icon`) `/Actions/Mobile` (`openLabel`, `closeLabel`) `/MobileLink`, `navBar` (the slots) | client |
@@ -4,19 +4,19 @@ import * as React from 'react';
4
4
  declare const appShell: tailwind_variants.TVReturnType<{
5
5
  [key: string]: {
6
6
  [key: string]: tailwind_variants.ClassValue | {
7
+ body?: tailwind_variants.ClassValue;
8
+ footer?: tailwind_variants.ClassValue;
9
+ header?: tailwind_variants.ClassValue;
10
+ main?: tailwind_variants.ClassValue;
11
+ nav?: tailwind_variants.ClassValue;
12
+ title?: tailwind_variants.ClassValue;
13
+ group?: tailwind_variants.ClassValue;
7
14
  root?: tailwind_variants.ClassValue;
8
15
  drawer?: tailwind_variants.ClassValue;
9
16
  overlay?: tailwind_variants.ClassValue;
10
17
  brand?: tailwind_variants.ClassValue;
11
- nav?: tailwind_variants.ClassValue;
12
- group?: tailwind_variants.ClassValue;
13
18
  itemLabel?: tailwind_variants.ClassValue;
14
- footer?: tailwind_variants.ClassValue;
15
- body?: tailwind_variants.ClassValue;
16
- header?: tailwind_variants.ClassValue;
17
- title?: tailwind_variants.ClassValue;
18
19
  actions?: tailwind_variants.ClassValue;
19
- main?: tailwind_variants.ClassValue;
20
20
  sidebar?: tailwind_variants.ClassValue;
21
21
  item?: tailwind_variants.ClassValue;
22
22
  itemBadge?: tailwind_variants.ClassValue;
@@ -27,19 +27,19 @@ declare const appShell: tailwind_variants.TVReturnType<{
27
27
  } | {
28
28
  [x: string]: {
29
29
  [x: string]: tailwind_variants.ClassValue | {
30
+ body?: tailwind_variants.ClassValue;
31
+ footer?: tailwind_variants.ClassValue;
32
+ header?: tailwind_variants.ClassValue;
33
+ main?: tailwind_variants.ClassValue;
34
+ nav?: tailwind_variants.ClassValue;
35
+ title?: tailwind_variants.ClassValue;
36
+ group?: tailwind_variants.ClassValue;
30
37
  root?: tailwind_variants.ClassValue;
31
38
  drawer?: tailwind_variants.ClassValue;
32
39
  overlay?: tailwind_variants.ClassValue;
33
40
  brand?: tailwind_variants.ClassValue;
34
- nav?: tailwind_variants.ClassValue;
35
- group?: tailwind_variants.ClassValue;
36
41
  itemLabel?: tailwind_variants.ClassValue;
37
- footer?: tailwind_variants.ClassValue;
38
- body?: tailwind_variants.ClassValue;
39
- header?: tailwind_variants.ClassValue;
40
- title?: tailwind_variants.ClassValue;
41
42
  actions?: tailwind_variants.ClassValue;
42
- main?: tailwind_variants.ClassValue;
43
43
  sidebar?: tailwind_variants.ClassValue;
44
44
  item?: tailwind_variants.ClassValue;
45
45
  itemBadge?: tailwind_variants.ClassValue;
@@ -69,19 +69,19 @@ declare const appShell: tailwind_variants.TVReturnType<{
69
69
  }, undefined, {
70
70
  [key: string]: {
71
71
  [key: string]: tailwind_variants.ClassValue | {
72
+ body?: tailwind_variants.ClassValue;
73
+ footer?: tailwind_variants.ClassValue;
74
+ header?: tailwind_variants.ClassValue;
75
+ main?: tailwind_variants.ClassValue;
76
+ nav?: tailwind_variants.ClassValue;
77
+ title?: tailwind_variants.ClassValue;
78
+ group?: tailwind_variants.ClassValue;
72
79
  root?: tailwind_variants.ClassValue;
73
80
  drawer?: tailwind_variants.ClassValue;
74
81
  overlay?: tailwind_variants.ClassValue;
75
82
  brand?: tailwind_variants.ClassValue;
76
- nav?: tailwind_variants.ClassValue;
77
- group?: tailwind_variants.ClassValue;
78
83
  itemLabel?: tailwind_variants.ClassValue;
79
- footer?: tailwind_variants.ClassValue;
80
- body?: tailwind_variants.ClassValue;
81
- header?: tailwind_variants.ClassValue;
82
- title?: tailwind_variants.ClassValue;
83
84
  actions?: tailwind_variants.ClassValue;
84
- main?: tailwind_variants.ClassValue;
85
85
  sidebar?: tailwind_variants.ClassValue;
86
86
  item?: tailwind_variants.ClassValue;
87
87
  itemBadge?: tailwind_variants.ClassValue;
@@ -135,9 +135,11 @@ type AppShellRootProps = React.ComponentProps<"div"> & {
135
135
  collapsed?: boolean;
136
136
  /** Called when the sidebar collapses or expands. */
137
137
  onCollapsedChange?: (collapsed: boolean) => void;
138
+ /** The words of the "skip to content" link (`Skip to content`), the first tab stop; it lands on `AppShell.Main`. Without it, no link. */
139
+ skipLabel?: string;
138
140
  };
139
141
  /** The shell: holds the collapse (desktop) and the drawer (phone) state. Children: Sidebar, then Body. */
140
- declare function AppShellRoot({ defaultCollapsed, collapsed: controlled, onCollapsedChange, className, ...props }: AppShellRootProps): React.JSX.Element;
142
+ declare function AppShellRoot({ defaultCollapsed, collapsed: controlled, onCollapsedChange, skipLabel, className, children, ...props }: AppShellRootProps): React.JSX.Element;
141
143
  type AppShellSidebarProps = Omit<React.ComponentProps<"nav">, "children"> & {
142
144
  /** The navigation's accessible name (`Studio`, `Administration`). */
143
145
  label: string;
package/dist/app-shell.js CHANGED
@@ -6,6 +6,9 @@ import {
6
6
  import {
7
7
  cn
8
8
  } from "./chunk-G3ETON2A.js";
9
+ import {
10
+ SkipLink
11
+ } from "./chunk-3GODX3D6.js";
9
12
 
10
13
  // src/app-shell/app-shell.tsx
11
14
  import { Dialog as DialogPrimitive, Slot } from "radix-ui";
@@ -41,7 +44,7 @@ var useShell = () => {
41
44
  if (!ctx) throw new Error("AppShell parts must be inside <AppShell.Root>");
42
45
  return ctx;
43
46
  };
44
- function AppShellRoot({ defaultCollapsed = false, collapsed: controlled, onCollapsedChange, className, ...props }) {
47
+ function AppShellRoot({ defaultCollapsed = false, collapsed: controlled, onCollapsedChange, skipLabel, className, children, ...props }) {
45
48
  const [own, setOwn] = React.useState(defaultCollapsed);
46
49
  const [open, setOpen] = React.useState(false);
47
50
  const collapsed = controlled ?? own;
@@ -53,7 +56,10 @@ function AppShellRoot({ defaultCollapsed = false, collapsed: controlled, onColla
53
56
  [controlled, onCollapsedChange]
54
57
  );
55
58
  const value = React.useMemo(() => ({ collapsed, setCollapsed, open, setOpen, inDrawer: false }), [collapsed, setCollapsed, open]);
56
- return /* @__PURE__ */ jsx(ShellContext.Provider, { value, children: /* @__PURE__ */ jsx("div", { className: s.root({ className }), ...props }) });
59
+ return /* @__PURE__ */ jsx(ShellContext.Provider, { value, children: /* @__PURE__ */ jsxs("div", { className: s.root({ className }), ...props, children: [
60
+ skipLabel && /* @__PURE__ */ jsx(SkipLink, { href: "#content", children: skipLabel }),
61
+ children
62
+ ] }) });
57
63
  }
58
64
  function SidebarContent({ brand, children, footer, collapseLabels, closeLabel }) {
59
65
  const { collapsed, setCollapsed, inDrawer } = useShell();
@@ -112,7 +118,7 @@ function AppShellHeader({ title, menuLabel, actions, className, children, ...pro
112
118
  ] });
113
119
  }
114
120
  function AppShellMain({ className, ...props }) {
115
- return /* @__PURE__ */ jsx("main", { className: s.main({ className }), ...props });
121
+ return /* @__PURE__ */ jsx("main", { id: "content", tabIndex: -1, className: s.main({ className }), ...props });
116
122
  }
117
123
  var AppShell = {
118
124
  Root: AppShellRoot,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/app-shell/app-shell.tsx"],"sourcesContent":["// Client (the collapse, the drawer). The shell of an application screen, read from the products' consoles (Orochia\n// admin and studio, Orazaka web-admin and web-client): a sidebar of sections that collapses to its icons on a desktop,\n// a header with the screen's title and actions, the content — and below `md` the sidebar becomes a drawer opened from\n// the header (Radix Dialog: focus trap, Escape, focus return). Words arrive as props; links through `asChild`.\nimport { Dialog as DialogPrimitive, Slot } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { ChevronGlyph, CloseGlyph } from \"../glyphs\";\n\nexport const appShell = tv({\n slots: {\n root: \"flex min-h-dvh w-full bg-surface-0 text-fg\",\n sidebar:\n \"sticky top-0 hidden h-dvh shrink-0 flex-col border-r border-border-subtle bg-surface-1 transition-[width] duration-300 motion-reduce:transition-none md:flex \" +\n \"w-64 data-collapsed:w-[4.25rem]\",\n drawer:\n \"kz-dialog fixed inset-y-0 left-0 z-50 flex w-[min(20rem,88vw)] flex-col border-r border-border-default bg-surface-1 pl-[env(safe-area-inset-left)] shadow-lg focus:outline-hidden\",\n overlay: \"kz-overlay fixed inset-0 z-50 bg-overlay backdrop-blur-xs\",\n brand: \"flex h-14 shrink-0 items-center gap-2.5 px-4 font-display text-[15px] font-bold tracking-tight [[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0\",\n nav: \"flex min-h-0 flex-1 flex-col gap-0.5 overflow-y-auto px-2 py-2\",\n group: \"mt-3 mb-1 px-3 font-mono text-[10.5px] font-semibold uppercase tracking-[0.14em] text-fg-secondary [[data-collapsed]_&]:sr-only\",\n item:\n \"group/item relative flex h-10 items-center gap-3 rounded-lg px-3 text-sm font-medium text-fg-secondary no-underline transition-colors \" +\n \"hover:bg-surface-2 hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring \" +\n \"aria-[current=page]:bg-accent-soft aria-[current=page]:text-fg \" +\n \"aria-[current=page]:before:absolute aria-[current=page]:before:inset-y-2 aria-[current=page]:before:-left-2 aria-[current=page]:before:w-1 aria-[current=page]:before:rounded-full aria-[current=page]:before:bg-accent \" +\n \"[[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0 [&>svg]:h-[18px] [&>svg]:w-[18px] [&>svg]:shrink-0\",\n itemLabel: \"min-w-0 flex-1 truncate [[data-collapsed]_&]:sr-only\",\n itemBadge:\n \"ml-auto inline-flex min-w-5 items-center justify-center rounded-full bg-accent px-1.5 text-[11px] font-bold text-on-accent \" +\n \"[[data-collapsed]_&]:absolute [[data-collapsed]_&]:right-1.5 [[data-collapsed]_&]:top-1 [[data-collapsed]_&]:min-w-4 [[data-collapsed]_&]:px-1 [[data-collapsed]_&]:text-[10px]\",\n footer: \"shrink-0 border-t border-border-subtle p-2\",\n collapse:\n \"hidden h-9 w-full items-center gap-2 rounded-lg px-3 text-xs font-medium text-fg-secondary hover:bg-surface-2 hover:text-fg md:flex \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring [[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0\",\n body: \"flex min-w-0 flex-1 flex-col\",\n header:\n \"sticky top-0 z-30 flex h-14 shrink-0 items-center gap-3 border-b border-border-subtle bg-surface-0/85 px-4 backdrop-blur-xl md:px-6\",\n menuButton:\n \"inline-flex h-9 w-9 items-center justify-center rounded-lg border border-border-subtle bg-surface-1 text-fg md:hidden \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n title: \"min-w-0 flex-1 truncate font-display text-base font-bold tracking-tight\",\n actions: \"ml-auto flex shrink-0 items-center gap-2\",\n main: \"min-w-0 flex-1 px-4 py-6 md:px-6 md:py-8\",\n },\n});\nconst s = appShell();\n\ninterface ShellState {\n collapsed: boolean;\n setCollapsed: (value: boolean) => void;\n open: boolean;\n setOpen: (value: boolean) => void;\n inDrawer: boolean;\n}\nconst ShellContext = React.createContext<ShellState | null>(null);\nconst useShell = () => {\n const ctx = React.useContext(ShellContext);\n if (!ctx) throw new Error(\"AppShell parts must be inside <AppShell.Root>\");\n return ctx;\n};\n\nexport type AppShellRootProps = React.ComponentProps<\"div\"> & {\n /** The sidebar starts collapsed to its icons (desktop). */\n defaultCollapsed?: boolean;\n /** Controlled collapse (desktop): pair with `onCollapsedChange` to persist the choice. */\n collapsed?: boolean;\n /** Called when the sidebar collapses or expands. */\n onCollapsedChange?: (collapsed: boolean) => void;\n};\n\n/** The shell: holds the collapse (desktop) and the drawer (phone) state. Children: Sidebar, then Body. */\nexport function AppShellRoot({ defaultCollapsed = false, collapsed: controlled, onCollapsedChange, className, ...props }: AppShellRootProps) {\n const [own, setOwn] = React.useState(defaultCollapsed);\n const [open, setOpen] = React.useState(false);\n const collapsed = controlled ?? own;\n const setCollapsed = React.useCallback(\n (value: boolean) => {\n if (controlled === undefined) setOwn(value);\n onCollapsedChange?.(value);\n },\n [controlled, onCollapsedChange],\n );\n const value = React.useMemo(() => ({ collapsed, setCollapsed, open, setOpen, inDrawer: false }), [collapsed, setCollapsed, open]);\n return (\n <ShellContext.Provider value={value}>\n <div className={s.root({ className })} {...props} />\n </ShellContext.Provider>\n );\n}\n\nexport type AppShellSidebarProps = Omit<React.ComponentProps<\"nav\">, \"children\"> & {\n /** The navigation's accessible name (`Studio`, `Administration`). */\n label: string;\n /** The brand at the top: the product's mark and name (a link home through `asChild` of your router, or plain). */\n brand?: React.ReactNode;\n /** The sections: `AppShell.Group` and `AppShell.Item`. */\n children: React.ReactNode;\n /** At the bottom, above the collapse button: the account, a plan, the version. */\n footer?: React.ReactNode;\n /** The collapse button's words, `[collapse, expand]` (desktop only). Without it the sidebar cannot collapse. */\n collapseLabels?: readonly [string, string];\n /** The drawer's close button words (phone). */\n closeLabel: string;\n};\n\nfunction SidebarContent({ brand, children, footer, collapseLabels, closeLabel }: Pick<AppShellSidebarProps, \"brand\" | \"children\" | \"footer\" | \"collapseLabels\" | \"closeLabel\">) {\n const { collapsed, setCollapsed, inDrawer } = useShell();\n return (\n <>\n <div className={s.brand()}>\n {brand}\n {inDrawer && (\n <DialogPrimitive.Close className={cn(s.menuButton(), \"ml-auto flex\")} aria-label={closeLabel}>\n <CloseGlyph className=\"h-4 w-4\" />\n </DialogPrimitive.Close>\n )}\n </div>\n <div className={s.nav()}>{children}</div>\n {(footer || (collapseLabels && !inDrawer)) && (\n <div className={s.footer()}>\n {footer}\n {collapseLabels && !inDrawer && (\n <button type=\"button\" className={s.collapse()} aria-expanded={!collapsed} onClick={() => setCollapsed(!collapsed)}>\n <ChevronGlyph direction={collapsed ? \"right\" : \"left\"} className=\"h-4 w-4\" />\n <span className={collapsed ? \"sr-only\" : undefined}>{collapsed ? collapseLabels[1] : collapseLabels[0]}</span>\n </button>\n )}\n </div>\n )}\n </>\n );\n}\n\n/** The sidebar: on a desktop a column that collapses to its icons; on a phone the same content in a drawer. */\nexport function AppShellSidebar({ label, brand, children, footer, collapseLabels, closeLabel, className, ...props }: AppShellSidebarProps) {\n const shell = useShell();\n const content = { brand, children, footer, collapseLabels, closeLabel };\n return (\n <>\n <nav aria-label={label} data-collapsed={shell.collapsed ? \"\" : undefined} className={s.sidebar({ className })} {...props}>\n <SidebarContent {...content} />\n </nav>\n <DialogPrimitive.Root open={shell.open} onOpenChange={shell.setOpen}>\n <DialogPrimitive.Portal>\n <DialogPrimitive.Overlay className={s.overlay()} />\n <DialogPrimitive.Content className={s.drawer()} aria-describedby={undefined}>\n <DialogPrimitive.Title className=\"sr-only\">{label}</DialogPrimitive.Title>\n <ShellContext.Provider value={{ ...shell, collapsed: false, inDrawer: true }}>\n <nav aria-label={label} className=\"flex min-h-0 flex-1 flex-col\" onClick={(e) => (e.target as Element).closest(\"a\") && shell.setOpen(false)}>\n <SidebarContent {...content} />\n </nav>\n </ShellContext.Provider>\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n </DialogPrimitive.Root>\n </>\n );\n}\n\nexport type AppShellGroupProps = React.ComponentProps<\"p\"> & {\n /** The group's name, above its items (hidden when the sidebar is collapsed). */\n children: React.ReactNode;\n};\n\n/** A heading between groups of items. */\nexport function AppShellGroup({ className, ...props }: AppShellGroupProps) {\n return <p className={s.group({ className })} {...props} />;\n}\n\nexport type AppShellItemProps = Omit<React.ComponentProps<\"a\">, \"children\"> & {\n /** The item's icon (a signature icon): it stays when the sidebar collapses. */\n icon?: React.ReactNode;\n /** Its words: the accessible name, hidden visually when collapsed (the icon carries a tooltip through `title`). */\n label: string;\n /** The screen it opens is the current one: `aria-current=\"page\"`. */\n current?: boolean;\n /** A count beside it (unread, pending): a number or a few characters. */\n badge?: React.ReactNode;\n /** Renders your link (Next's `Link`) with the item's look: pass it as the only child. */\n asChild?: boolean;\n /** Your link element when `asChild`. */\n children?: React.ReactElement;\n};\n\n/** A destination of the sidebar: icon, words, a count. */\nexport function AppShellItem({ icon, label, current, badge, asChild, children, className, ...props }: AppShellItemProps) {\n const { collapsed } = useShell();\n const Comp = asChild ? Slot.Root : \"a\";\n const inner = (\n <>\n {icon}\n <span className={s.itemLabel()}>{label}</span>\n {badge != null && <span className={s.itemBadge()}>{badge}</span>}\n </>\n );\n return (\n <Comp aria-current={current ? \"page\" : undefined} title={collapsed ? label : undefined} className={s.item({ className })} {...props}>\n {asChild && children ? React.cloneElement(children, undefined, inner) : inner}\n </Comp>\n );\n}\n\n/** The column right of the sidebar: Header, then Main. */\nexport function AppShellBody({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.body({ className })} {...props} />;\n}\n\nexport type AppShellHeaderProps = Omit<React.ComponentProps<\"header\">, \"title\"> & {\n /** The screen's title (an `h1`). */\n title?: React.ReactNode;\n /** The button that opens the drawer on a phone: its words. */\n menuLabel: string;\n /** The screen's actions, on the right. */\n actions?: React.ReactNode;\n};\n\n/** The top of the screen: the drawer's button (phone), the title, the actions. Sticky. */\nexport function AppShellHeader({ title, menuLabel, actions, className, children, ...props }: AppShellHeaderProps) {\n const { open, setOpen } = useShell();\n return (\n <header className={s.header({ className })} {...props}>\n <button type=\"button\" className={s.menuButton()} aria-label={menuLabel} aria-expanded={open} aria-haspopup=\"dialog\" onClick={() => setOpen(true)}>\n <svg viewBox=\"0 0 24 24\" className=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M4 7h16M4 12h16M4 17h10\" />\n </svg>\n </button>\n {title && <h1 className={s.title()}>{title}</h1>}\n {children}\n {actions && <div className={s.actions()}>{actions}</div>}\n </header>\n );\n}\n\n/** The screen's content. */\nexport function AppShellMain({ className, ...props }: React.ComponentProps<\"main\">) {\n return <main className={s.main({ className })} {...props} />;\n}\n\nexport const AppShell = {\n Root: AppShellRoot,\n Sidebar: AppShellSidebar,\n Group: AppShellGroup,\n Item: AppShellItem,\n Body: AppShellBody,\n Header: AppShellHeader,\n Main: AppShellMain,\n};\n"],"mappings":";;;;;;;;;;AAIA,SAAS,UAAU,iBAAiB,YAAY;AAChD,YAAY,WAAW;AACvB,SAAS,UAAU;AAkFb,SAuBF,UAvBE,KAwBA,YAxBA;AA7EC,IAAM,WAAW,GAAG;AAAA,EACzB,OAAO;AAAA,IACL,MAAM;AAAA,IACN,SACE;AAAA,IAEF,QACE;AAAA,IACF,SAAS;AAAA,IACT,OAAO;AAAA,IACP,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MACE;AAAA,IAKF,WAAW;AAAA,IACX,WACE;AAAA,IAEF,QAAQ;AAAA,IACR,UACE;AAAA,IAEF,MAAM;AAAA,IACN,QACE;AAAA,IACF,YACE;AAAA,IAEF,OAAO;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,EACR;AACF,CAAC;AACD,IAAM,IAAI,SAAS;AASnB,IAAM,eAAqB,oBAAiC,IAAI;AAChE,IAAM,WAAW,MAAM;AACrB,QAAM,MAAY,iBAAW,YAAY;AACzC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,+CAA+C;AACzE,SAAO;AACT;AAYO,SAAS,aAAa,EAAE,mBAAmB,OAAO,WAAW,YAAY,mBAAmB,WAAW,GAAG,MAAM,GAAsB;AAC3I,QAAM,CAAC,KAAK,MAAM,IAAU,eAAS,gBAAgB;AACrD,QAAM,CAAC,MAAM,OAAO,IAAU,eAAS,KAAK;AAC5C,QAAM,YAAY,cAAc;AAChC,QAAM,eAAqB;AAAA,IACzB,CAACA,WAAmB;AAClB,UAAI,eAAe,OAAW,QAAOA,MAAK;AAC1C,0BAAoBA,MAAK;AAAA,IAC3B;AAAA,IACA,CAAC,YAAY,iBAAiB;AAAA,EAChC;AACA,QAAM,QAAc,cAAQ,OAAO,EAAE,WAAW,cAAc,MAAM,SAAS,UAAU,MAAM,IAAI,CAAC,WAAW,cAAc,IAAI,CAAC;AAChI,SACE,oBAAC,aAAa,UAAb,EAAsB,OACrB,8BAAC,SAAI,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO,GACpD;AAEJ;AAiBA,SAAS,eAAe,EAAE,OAAO,UAAU,QAAQ,gBAAgB,WAAW,GAAkG;AAC9K,QAAM,EAAE,WAAW,cAAc,SAAS,IAAI,SAAS;AACvD,SACE,iCACE;AAAA,yBAAC,SAAI,WAAW,EAAE,MAAM,GACrB;AAAA;AAAA,MACA,YACC,oBAAC,gBAAgB,OAAhB,EAAsB,WAAW,GAAG,EAAE,WAAW,GAAG,cAAc,GAAG,cAAY,YAChF,8BAAC,cAAW,WAAU,WAAU,GAClC;AAAA,OAEJ;AAAA,IACA,oBAAC,SAAI,WAAW,EAAE,IAAI,GAAI,UAAS;AAAA,KACjC,UAAW,kBAAkB,CAAC,aAC9B,qBAAC,SAAI,WAAW,EAAE,OAAO,GACtB;AAAA;AAAA,MACA,kBAAkB,CAAC,YAClB,qBAAC,YAAO,MAAK,UAAS,WAAW,EAAE,SAAS,GAAG,iBAAe,CAAC,WAAW,SAAS,MAAM,aAAa,CAAC,SAAS,GAC9G;AAAA,4BAAC,gBAAa,WAAW,YAAY,UAAU,QAAQ,WAAU,WAAU;AAAA,QAC3E,oBAAC,UAAK,WAAW,YAAY,YAAY,QAAY,sBAAY,eAAe,CAAC,IAAI,eAAe,CAAC,GAAE;AAAA,SACzG;AAAA,OAEJ;AAAA,KAEJ;AAEJ;AAGO,SAAS,gBAAgB,EAAE,OAAO,OAAO,UAAU,QAAQ,gBAAgB,YAAY,WAAW,GAAG,MAAM,GAAyB;AACzI,QAAM,QAAQ,SAAS;AACvB,QAAM,UAAU,EAAE,OAAO,UAAU,QAAQ,gBAAgB,WAAW;AACtE,SACE,iCACE;AAAA,wBAAC,SAAI,cAAY,OAAO,kBAAgB,MAAM,YAAY,KAAK,QAAW,WAAW,EAAE,QAAQ,EAAE,UAAU,CAAC,GAAI,GAAG,OACjH,8BAAC,kBAAgB,GAAG,SAAS,GAC/B;AAAA,IACA,oBAAC,gBAAgB,MAAhB,EAAqB,MAAM,MAAM,MAAM,cAAc,MAAM,SAC1D,+BAAC,gBAAgB,QAAhB,EACC;AAAA,0BAAC,gBAAgB,SAAhB,EAAwB,WAAW,EAAE,QAAQ,GAAG;AAAA,MACjD,qBAAC,gBAAgB,SAAhB,EAAwB,WAAW,EAAE,OAAO,GAAG,oBAAkB,QAChE;AAAA,4BAAC,gBAAgB,OAAhB,EAAsB,WAAU,WAAW,iBAAM;AAAA,QAClD,oBAAC,aAAa,UAAb,EAAsB,OAAO,EAAE,GAAG,OAAO,WAAW,OAAO,UAAU,KAAK,GACzE,8BAAC,SAAI,cAAY,OAAO,WAAU,gCAA+B,SAAS,CAAC,MAAO,EAAE,OAAmB,QAAQ,GAAG,KAAK,MAAM,QAAQ,KAAK,GACxI,8BAAC,kBAAgB,GAAG,SAAS,GAC/B,GACF;AAAA,SACF;AAAA,OACF,GACF;AAAA,KACF;AAEJ;AAQO,SAAS,cAAc,EAAE,WAAW,GAAG,MAAM,GAAuB;AACzE,SAAO,oBAAC,OAAE,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO;AAC1D;AAkBO,SAAS,aAAa,EAAE,MAAM,OAAO,SAAS,OAAO,SAAS,UAAU,WAAW,GAAG,MAAM,GAAsB;AACvH,QAAM,EAAE,UAAU,IAAI,SAAS;AAC/B,QAAM,OAAO,UAAU,KAAK,OAAO;AACnC,QAAM,QACJ,iCACG;AAAA;AAAA,IACD,oBAAC,UAAK,WAAW,EAAE,UAAU,GAAI,iBAAM;AAAA,IACtC,SAAS,QAAQ,oBAAC,UAAK,WAAW,EAAE,UAAU,GAAI,iBAAM;AAAA,KAC3D;AAEF,SACE,oBAAC,QAAK,gBAAc,UAAU,SAAS,QAAW,OAAO,YAAY,QAAQ,QAAW,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAC3H,qBAAW,WAAiB,mBAAa,UAAU,QAAW,KAAK,IAAI,OAC1E;AAEJ;AAGO,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAgC;AACjF,SAAO,oBAAC,SAAI,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO;AAC3D;AAYO,SAAS,eAAe,EAAE,OAAO,WAAW,SAAS,WAAW,UAAU,GAAG,MAAM,GAAwB;AAChH,QAAM,EAAE,MAAM,QAAQ,IAAI,SAAS;AACnC,SACE,qBAAC,YAAO,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,GAAI,GAAG,OAC9C;AAAA,wBAAC,YAAO,MAAK,UAAS,WAAW,EAAE,WAAW,GAAG,cAAY,WAAW,iBAAe,MAAM,iBAAc,UAAS,SAAS,MAAM,QAAQ,IAAI,GAC7I,8BAAC,SAAI,SAAQ,aAAY,WAAU,WAAU,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,eAAW,MAC9H,8BAAC,UAAK,GAAE,2BAA0B,GACpC,GACF;AAAA,IACC,SAAS,oBAAC,QAAG,WAAW,EAAE,MAAM,GAAI,iBAAM;AAAA,IAC1C;AAAA,IACA,WAAW,oBAAC,SAAI,WAAW,EAAE,QAAQ,GAAI,mBAAQ;AAAA,KACpD;AAEJ;AAGO,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAiC;AAClF,SAAO,oBAAC,UAAK,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO;AAC5D;AAEO,IAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,MAAM;AACR;","names":["value"]}
1
+ {"version":3,"sources":["../src/app-shell/app-shell.tsx"],"sourcesContent":["// Client (the collapse, the drawer). The shell of an application screen, read from the products' consoles (Orochia\n// admin and studio, Orazaka web-admin and web-client): a sidebar of sections that collapses to its icons on a desktop,\n// a header with the screen's title and actions, the content — and below `md` the sidebar becomes a drawer opened from\n// the header (Radix Dialog: focus trap, Escape, focus return). Words arrive as props; links through `asChild`.\nimport { SkipLink } from \"@krizaka/ui/skip-link\";\nimport { Dialog as DialogPrimitive, Slot } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { ChevronGlyph, CloseGlyph } from \"../glyphs\";\n\nexport const appShell = tv({\n slots: {\n root: \"flex min-h-dvh w-full bg-surface-0 text-fg\",\n sidebar:\n \"sticky top-0 hidden h-dvh shrink-0 flex-col border-r border-border-subtle bg-surface-1 transition-[width] duration-300 motion-reduce:transition-none md:flex \" +\n \"w-64 data-collapsed:w-[4.25rem]\",\n drawer:\n \"kz-dialog fixed inset-y-0 left-0 z-50 flex w-[min(20rem,88vw)] flex-col border-r border-border-default bg-surface-1 pl-[env(safe-area-inset-left)] shadow-lg focus:outline-hidden\",\n overlay: \"kz-overlay fixed inset-0 z-50 bg-overlay backdrop-blur-xs\",\n brand: \"flex h-14 shrink-0 items-center gap-2.5 px-4 font-display text-[15px] font-bold tracking-tight [[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0\",\n nav: \"flex min-h-0 flex-1 flex-col gap-0.5 overflow-y-auto px-2 py-2\",\n group: \"mt-3 mb-1 px-3 font-mono text-[10.5px] font-semibold uppercase tracking-[0.14em] text-fg-secondary [[data-collapsed]_&]:sr-only\",\n item:\n \"group/item relative flex h-10 items-center gap-3 rounded-lg px-3 text-sm font-medium text-fg-secondary no-underline transition-colors \" +\n \"hover:bg-surface-2 hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring \" +\n \"aria-[current=page]:bg-accent-soft aria-[current=page]:text-fg \" +\n \"aria-[current=page]:before:absolute aria-[current=page]:before:inset-y-2 aria-[current=page]:before:-left-2 aria-[current=page]:before:w-1 aria-[current=page]:before:rounded-full aria-[current=page]:before:bg-accent \" +\n \"[[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0 [&>svg]:h-[18px] [&>svg]:w-[18px] [&>svg]:shrink-0\",\n itemLabel: \"min-w-0 flex-1 truncate [[data-collapsed]_&]:sr-only\",\n itemBadge:\n \"ml-auto inline-flex min-w-5 items-center justify-center rounded-full bg-accent px-1.5 text-[11px] font-bold text-on-accent \" +\n \"[[data-collapsed]_&]:absolute [[data-collapsed]_&]:right-1.5 [[data-collapsed]_&]:top-1 [[data-collapsed]_&]:min-w-4 [[data-collapsed]_&]:px-1 [[data-collapsed]_&]:text-[10px]\",\n footer: \"shrink-0 border-t border-border-subtle p-2\",\n collapse:\n \"hidden h-9 w-full items-center gap-2 rounded-lg px-3 text-xs font-medium text-fg-secondary hover:bg-surface-2 hover:text-fg md:flex \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring [[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0\",\n body: \"flex min-w-0 flex-1 flex-col\",\n header:\n \"sticky top-0 z-30 flex h-14 shrink-0 items-center gap-3 border-b border-border-subtle bg-surface-0/85 px-4 backdrop-blur-xl md:px-6\",\n menuButton:\n \"inline-flex h-9 w-9 items-center justify-center rounded-lg border border-border-subtle bg-surface-1 text-fg md:hidden \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n title: \"min-w-0 flex-1 truncate font-display text-base font-bold tracking-tight\",\n actions: \"ml-auto flex shrink-0 items-center gap-2\",\n main: \"min-w-0 flex-1 px-4 py-6 md:px-6 md:py-8\",\n },\n});\nconst s = appShell();\n\ninterface ShellState {\n collapsed: boolean;\n setCollapsed: (value: boolean) => void;\n open: boolean;\n setOpen: (value: boolean) => void;\n inDrawer: boolean;\n}\nconst ShellContext = React.createContext<ShellState | null>(null);\nconst useShell = () => {\n const ctx = React.useContext(ShellContext);\n if (!ctx) throw new Error(\"AppShell parts must be inside <AppShell.Root>\");\n return ctx;\n};\n\nexport type AppShellRootProps = React.ComponentProps<\"div\"> & {\n /** The sidebar starts collapsed to its icons (desktop). */\n defaultCollapsed?: boolean;\n /** Controlled collapse (desktop): pair with `onCollapsedChange` to persist the choice. */\n collapsed?: boolean;\n /** Called when the sidebar collapses or expands. */\n onCollapsedChange?: (collapsed: boolean) => void;\n /** The words of the \"skip to content\" link (`Skip to content`), the first tab stop; it lands on `AppShell.Main`. Without it, no link. */\n skipLabel?: string;\n};\n\n/** The shell: holds the collapse (desktop) and the drawer (phone) state. Children: Sidebar, then Body. */\nexport function AppShellRoot({ defaultCollapsed = false, collapsed: controlled, onCollapsedChange, skipLabel, className, children, ...props }: AppShellRootProps) {\n const [own, setOwn] = React.useState(defaultCollapsed);\n const [open, setOpen] = React.useState(false);\n const collapsed = controlled ?? own;\n const setCollapsed = React.useCallback(\n (value: boolean) => {\n if (controlled === undefined) setOwn(value);\n onCollapsedChange?.(value);\n },\n [controlled, onCollapsedChange],\n );\n const value = React.useMemo(() => ({ collapsed, setCollapsed, open, setOpen, inDrawer: false }), [collapsed, setCollapsed, open]);\n return (\n <ShellContext.Provider value={value}>\n <div className={s.root({ className })} {...props}>\n {skipLabel && <SkipLink href=\"#content\">{skipLabel}</SkipLink>}\n {children}\n </div>\n </ShellContext.Provider>\n );\n}\n\nexport type AppShellSidebarProps = Omit<React.ComponentProps<\"nav\">, \"children\"> & {\n /** The navigation's accessible name (`Studio`, `Administration`). */\n label: string;\n /** The brand at the top: the product's mark and name (a link home through `asChild` of your router, or plain). */\n brand?: React.ReactNode;\n /** The sections: `AppShell.Group` and `AppShell.Item`. */\n children: React.ReactNode;\n /** At the bottom, above the collapse button: the account, a plan, the version. */\n footer?: React.ReactNode;\n /** The collapse button's words, `[collapse, expand]` (desktop only). Without it the sidebar cannot collapse. */\n collapseLabels?: readonly [string, string];\n /** The drawer's close button words (phone). */\n closeLabel: string;\n};\n\nfunction SidebarContent({ brand, children, footer, collapseLabels, closeLabel }: Pick<AppShellSidebarProps, \"brand\" | \"children\" | \"footer\" | \"collapseLabels\" | \"closeLabel\">) {\n const { collapsed, setCollapsed, inDrawer } = useShell();\n return (\n <>\n <div className={s.brand()}>\n {brand}\n {inDrawer && (\n <DialogPrimitive.Close className={cn(s.menuButton(), \"ml-auto flex\")} aria-label={closeLabel}>\n <CloseGlyph className=\"h-4 w-4\" />\n </DialogPrimitive.Close>\n )}\n </div>\n <div className={s.nav()}>{children}</div>\n {(footer || (collapseLabels && !inDrawer)) && (\n <div className={s.footer()}>\n {footer}\n {collapseLabels && !inDrawer && (\n <button type=\"button\" className={s.collapse()} aria-expanded={!collapsed} onClick={() => setCollapsed(!collapsed)}>\n <ChevronGlyph direction={collapsed ? \"right\" : \"left\"} className=\"h-4 w-4\" />\n <span className={collapsed ? \"sr-only\" : undefined}>{collapsed ? collapseLabels[1] : collapseLabels[0]}</span>\n </button>\n )}\n </div>\n )}\n </>\n );\n}\n\n/** The sidebar: on a desktop a column that collapses to its icons; on a phone the same content in a drawer. */\nexport function AppShellSidebar({ label, brand, children, footer, collapseLabels, closeLabel, className, ...props }: AppShellSidebarProps) {\n const shell = useShell();\n const content = { brand, children, footer, collapseLabels, closeLabel };\n return (\n <>\n <nav aria-label={label} data-collapsed={shell.collapsed ? \"\" : undefined} className={s.sidebar({ className })} {...props}>\n <SidebarContent {...content} />\n </nav>\n <DialogPrimitive.Root open={shell.open} onOpenChange={shell.setOpen}>\n <DialogPrimitive.Portal>\n <DialogPrimitive.Overlay className={s.overlay()} />\n <DialogPrimitive.Content className={s.drawer()} aria-describedby={undefined}>\n <DialogPrimitive.Title className=\"sr-only\">{label}</DialogPrimitive.Title>\n <ShellContext.Provider value={{ ...shell, collapsed: false, inDrawer: true }}>\n <nav aria-label={label} className=\"flex min-h-0 flex-1 flex-col\" onClick={(e) => (e.target as Element).closest(\"a\") && shell.setOpen(false)}>\n <SidebarContent {...content} />\n </nav>\n </ShellContext.Provider>\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n </DialogPrimitive.Root>\n </>\n );\n}\n\nexport type AppShellGroupProps = React.ComponentProps<\"p\"> & {\n /** The group's name, above its items (hidden when the sidebar is collapsed). */\n children: React.ReactNode;\n};\n\n/** A heading between groups of items. */\nexport function AppShellGroup({ className, ...props }: AppShellGroupProps) {\n return <p className={s.group({ className })} {...props} />;\n}\n\nexport type AppShellItemProps = Omit<React.ComponentProps<\"a\">, \"children\"> & {\n /** The item's icon (a signature icon): it stays when the sidebar collapses. */\n icon?: React.ReactNode;\n /** Its words: the accessible name, hidden visually when collapsed (the icon carries a tooltip through `title`). */\n label: string;\n /** The screen it opens is the current one: `aria-current=\"page\"`. */\n current?: boolean;\n /** A count beside it (unread, pending): a number or a few characters. */\n badge?: React.ReactNode;\n /** Renders your link (Next's `Link`) with the item's look: pass it as the only child. */\n asChild?: boolean;\n /** Your link element when `asChild`. */\n children?: React.ReactElement;\n};\n\n/** A destination of the sidebar: icon, words, a count. */\nexport function AppShellItem({ icon, label, current, badge, asChild, children, className, ...props }: AppShellItemProps) {\n const { collapsed } = useShell();\n const Comp = asChild ? Slot.Root : \"a\";\n const inner = (\n <>\n {icon}\n <span className={s.itemLabel()}>{label}</span>\n {badge != null && <span className={s.itemBadge()}>{badge}</span>}\n </>\n );\n return (\n <Comp aria-current={current ? \"page\" : undefined} title={collapsed ? label : undefined} className={s.item({ className })} {...props}>\n {asChild && children ? React.cloneElement(children, undefined, inner) : inner}\n </Comp>\n );\n}\n\n/** The column right of the sidebar: Header, then Main. */\nexport function AppShellBody({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.body({ className })} {...props} />;\n}\n\nexport type AppShellHeaderProps = Omit<React.ComponentProps<\"header\">, \"title\"> & {\n /** The screen's title (an `h1`). */\n title?: React.ReactNode;\n /** The button that opens the drawer on a phone: its words. */\n menuLabel: string;\n /** The screen's actions, on the right. */\n actions?: React.ReactNode;\n};\n\n/** The top of the screen: the drawer's button (phone), the title, the actions. Sticky. */\nexport function AppShellHeader({ title, menuLabel, actions, className, children, ...props }: AppShellHeaderProps) {\n const { open, setOpen } = useShell();\n return (\n <header className={s.header({ className })} {...props}>\n <button type=\"button\" className={s.menuButton()} aria-label={menuLabel} aria-expanded={open} aria-haspopup=\"dialog\" onClick={() => setOpen(true)}>\n <svg viewBox=\"0 0 24 24\" className=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M4 7h16M4 12h16M4 17h10\" />\n </svg>\n </button>\n {title && <h1 className={s.title()}>{title}</h1>}\n {children}\n {actions && <div className={s.actions()}>{actions}</div>}\n </header>\n );\n}\n\n/** The screen's content. */\nexport function AppShellMain({ className, ...props }: React.ComponentProps<\"main\">) {\n return <main id=\"content\" tabIndex={-1} className={s.main({ className })} {...props} />;\n}\n\nexport const AppShell = {\n Root: AppShellRoot,\n Sidebar: AppShellSidebar,\n Group: AppShellGroup,\n Item: AppShellItem,\n Body: AppShellBody,\n Header: AppShellHeader,\n Main: AppShellMain,\n};\n"],"mappings":";;;;;;;;;;;;;AAKA,SAAS,UAAU,iBAAiB,YAAY;AAChD,YAAY,WAAW;AACvB,SAAS,UAAU;AAoFb,SA0BF,UAzBkB,KADhB;AA/EC,IAAM,WAAW,GAAG;AAAA,EACzB,OAAO;AAAA,IACL,MAAM;AAAA,IACN,SACE;AAAA,IAEF,QACE;AAAA,IACF,SAAS;AAAA,IACT,OAAO;AAAA,IACP,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MACE;AAAA,IAKF,WAAW;AAAA,IACX,WACE;AAAA,IAEF,QAAQ;AAAA,IACR,UACE;AAAA,IAEF,MAAM;AAAA,IACN,QACE;AAAA,IACF,YACE;AAAA,IAEF,OAAO;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,EACR;AACF,CAAC;AACD,IAAM,IAAI,SAAS;AASnB,IAAM,eAAqB,oBAAiC,IAAI;AAChE,IAAM,WAAW,MAAM;AACrB,QAAM,MAAY,iBAAW,YAAY;AACzC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,+CAA+C;AACzE,SAAO;AACT;AAcO,SAAS,aAAa,EAAE,mBAAmB,OAAO,WAAW,YAAY,mBAAmB,WAAW,WAAW,UAAU,GAAG,MAAM,GAAsB;AAChK,QAAM,CAAC,KAAK,MAAM,IAAU,eAAS,gBAAgB;AACrD,QAAM,CAAC,MAAM,OAAO,IAAU,eAAS,KAAK;AAC5C,QAAM,YAAY,cAAc;AAChC,QAAM,eAAqB;AAAA,IACzB,CAACA,WAAmB;AAClB,UAAI,eAAe,OAAW,QAAOA,MAAK;AAC1C,0BAAoBA,MAAK;AAAA,IAC3B;AAAA,IACA,CAAC,YAAY,iBAAiB;AAAA,EAChC;AACA,QAAM,QAAc,cAAQ,OAAO,EAAE,WAAW,cAAc,MAAM,SAAS,UAAU,MAAM,IAAI,CAAC,WAAW,cAAc,IAAI,CAAC;AAChI,SACE,oBAAC,aAAa,UAAb,EAAsB,OACrB,+BAAC,SAAI,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OACxC;AAAA,iBAAa,oBAAC,YAAS,MAAK,YAAY,qBAAU;AAAA,IAClD;AAAA,KACH,GACF;AAEJ;AAiBA,SAAS,eAAe,EAAE,OAAO,UAAU,QAAQ,gBAAgB,WAAW,GAAkG;AAC9K,QAAM,EAAE,WAAW,cAAc,SAAS,IAAI,SAAS;AACvD,SACE,iCACE;AAAA,yBAAC,SAAI,WAAW,EAAE,MAAM,GACrB;AAAA;AAAA,MACA,YACC,oBAAC,gBAAgB,OAAhB,EAAsB,WAAW,GAAG,EAAE,WAAW,GAAG,cAAc,GAAG,cAAY,YAChF,8BAAC,cAAW,WAAU,WAAU,GAClC;AAAA,OAEJ;AAAA,IACA,oBAAC,SAAI,WAAW,EAAE,IAAI,GAAI,UAAS;AAAA,KACjC,UAAW,kBAAkB,CAAC,aAC9B,qBAAC,SAAI,WAAW,EAAE,OAAO,GACtB;AAAA;AAAA,MACA,kBAAkB,CAAC,YAClB,qBAAC,YAAO,MAAK,UAAS,WAAW,EAAE,SAAS,GAAG,iBAAe,CAAC,WAAW,SAAS,MAAM,aAAa,CAAC,SAAS,GAC9G;AAAA,4BAAC,gBAAa,WAAW,YAAY,UAAU,QAAQ,WAAU,WAAU;AAAA,QAC3E,oBAAC,UAAK,WAAW,YAAY,YAAY,QAAY,sBAAY,eAAe,CAAC,IAAI,eAAe,CAAC,GAAE;AAAA,SACzG;AAAA,OAEJ;AAAA,KAEJ;AAEJ;AAGO,SAAS,gBAAgB,EAAE,OAAO,OAAO,UAAU,QAAQ,gBAAgB,YAAY,WAAW,GAAG,MAAM,GAAyB;AACzI,QAAM,QAAQ,SAAS;AACvB,QAAM,UAAU,EAAE,OAAO,UAAU,QAAQ,gBAAgB,WAAW;AACtE,SACE,iCACE;AAAA,wBAAC,SAAI,cAAY,OAAO,kBAAgB,MAAM,YAAY,KAAK,QAAW,WAAW,EAAE,QAAQ,EAAE,UAAU,CAAC,GAAI,GAAG,OACjH,8BAAC,kBAAgB,GAAG,SAAS,GAC/B;AAAA,IACA,oBAAC,gBAAgB,MAAhB,EAAqB,MAAM,MAAM,MAAM,cAAc,MAAM,SAC1D,+BAAC,gBAAgB,QAAhB,EACC;AAAA,0BAAC,gBAAgB,SAAhB,EAAwB,WAAW,EAAE,QAAQ,GAAG;AAAA,MACjD,qBAAC,gBAAgB,SAAhB,EAAwB,WAAW,EAAE,OAAO,GAAG,oBAAkB,QAChE;AAAA,4BAAC,gBAAgB,OAAhB,EAAsB,WAAU,WAAW,iBAAM;AAAA,QAClD,oBAAC,aAAa,UAAb,EAAsB,OAAO,EAAE,GAAG,OAAO,WAAW,OAAO,UAAU,KAAK,GACzE,8BAAC,SAAI,cAAY,OAAO,WAAU,gCAA+B,SAAS,CAAC,MAAO,EAAE,OAAmB,QAAQ,GAAG,KAAK,MAAM,QAAQ,KAAK,GACxI,8BAAC,kBAAgB,GAAG,SAAS,GAC/B,GACF;AAAA,SACF;AAAA,OACF,GACF;AAAA,KACF;AAEJ;AAQO,SAAS,cAAc,EAAE,WAAW,GAAG,MAAM,GAAuB;AACzE,SAAO,oBAAC,OAAE,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO;AAC1D;AAkBO,SAAS,aAAa,EAAE,MAAM,OAAO,SAAS,OAAO,SAAS,UAAU,WAAW,GAAG,MAAM,GAAsB;AACvH,QAAM,EAAE,UAAU,IAAI,SAAS;AAC/B,QAAM,OAAO,UAAU,KAAK,OAAO;AACnC,QAAM,QACJ,iCACG;AAAA;AAAA,IACD,oBAAC,UAAK,WAAW,EAAE,UAAU,GAAI,iBAAM;AAAA,IACtC,SAAS,QAAQ,oBAAC,UAAK,WAAW,EAAE,UAAU,GAAI,iBAAM;AAAA,KAC3D;AAEF,SACE,oBAAC,QAAK,gBAAc,UAAU,SAAS,QAAW,OAAO,YAAY,QAAQ,QAAW,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAC3H,qBAAW,WAAiB,mBAAa,UAAU,QAAW,KAAK,IAAI,OAC1E;AAEJ;AAGO,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAgC;AACjF,SAAO,oBAAC,SAAI,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO;AAC3D;AAYO,SAAS,eAAe,EAAE,OAAO,WAAW,SAAS,WAAW,UAAU,GAAG,MAAM,GAAwB;AAChH,QAAM,EAAE,MAAM,QAAQ,IAAI,SAAS;AACnC,SACE,qBAAC,YAAO,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,GAAI,GAAG,OAC9C;AAAA,wBAAC,YAAO,MAAK,UAAS,WAAW,EAAE,WAAW,GAAG,cAAY,WAAW,iBAAe,MAAM,iBAAc,UAAS,SAAS,MAAM,QAAQ,IAAI,GAC7I,8BAAC,SAAI,SAAQ,aAAY,WAAU,WAAU,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,eAAW,MAC9H,8BAAC,UAAK,GAAE,2BAA0B,GACpC,GACF;AAAA,IACC,SAAS,oBAAC,QAAG,WAAW,EAAE,MAAM,GAAI,iBAAM;AAAA,IAC1C;AAAA,IACA,WAAW,oBAAC,SAAI,WAAW,EAAE,QAAQ,GAAI,mBAAQ;AAAA,KACpD;AAEJ;AAGO,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAiC;AAClF,SAAO,oBAAC,UAAK,IAAG,WAAU,UAAU,IAAI,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAAO;AACvF;AAEO,IAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,MAAM;AACR;","names":["value"]}
@@ -1,3 +1,6 @@
1
+ import {
2
+ Facts
3
+ } from "./chunk-DGQMDJXK.js";
1
4
  import {
2
5
  BlockLink
3
6
  } from "./chunk-2AGSO3WH.js";
@@ -5,9 +8,6 @@ import {
5
8
  ArrowGlyph,
6
9
  ChevronGlyph
7
10
  } from "./chunk-XCBVFMNV.js";
8
- import {
9
- Facts
10
- } from "./chunk-DGQMDJXK.js";
11
11
  import "./chunk-XVLWVTQA.js";
12
12
  import {
13
13
  cn
package/dist/chat.d.ts ADDED
@@ -0,0 +1,187 @@
1
+ import * as tailwind_variants from 'tailwind-variants';
2
+ import * as React from 'react';
3
+
4
+ declare const chat: tailwind_variants.TVReturnType<{
5
+ [key: string]: {
6
+ [key: string]: tailwind_variants.ClassValue | {
7
+ input?: tailwind_variants.ClassValue;
8
+ time?: tailwind_variants.ClassValue;
9
+ send?: tailwind_variants.ClassValue;
10
+ row?: tailwind_variants.ClassValue;
11
+ status?: tailwind_variants.ClassValue;
12
+ bubble?: tailwind_variants.ClassValue;
13
+ context?: tailwind_variants.ClassValue;
14
+ typing?: tailwind_variants.ClassValue;
15
+ dot?: tailwind_variants.ClassValue;
16
+ composer?: tailwind_variants.ClassValue;
17
+ item?: tailwind_variants.ClassValue;
18
+ count?: tailwind_variants.ClassValue;
19
+ };
20
+ };
21
+ } | {
22
+ [x: string]: {
23
+ [x: string]: tailwind_variants.ClassValue | {
24
+ input?: tailwind_variants.ClassValue;
25
+ time?: tailwind_variants.ClassValue;
26
+ send?: tailwind_variants.ClassValue;
27
+ row?: tailwind_variants.ClassValue;
28
+ status?: tailwind_variants.ClassValue;
29
+ bubble?: tailwind_variants.ClassValue;
30
+ context?: tailwind_variants.ClassValue;
31
+ typing?: tailwind_variants.ClassValue;
32
+ dot?: tailwind_variants.ClassValue;
33
+ composer?: tailwind_variants.ClassValue;
34
+ item?: tailwind_variants.ClassValue;
35
+ count?: tailwind_variants.ClassValue;
36
+ };
37
+ };
38
+ } | {}, {
39
+ row: string;
40
+ bubble: string;
41
+ context: string;
42
+ time: string;
43
+ status: string;
44
+ typing: string;
45
+ dot: string;
46
+ composer: string;
47
+ input: string;
48
+ send: string;
49
+ item: string;
50
+ count: string;
51
+ }, undefined, {
52
+ [key: string]: {
53
+ [key: string]: tailwind_variants.ClassValue | {
54
+ input?: tailwind_variants.ClassValue;
55
+ time?: tailwind_variants.ClassValue;
56
+ send?: tailwind_variants.ClassValue;
57
+ row?: tailwind_variants.ClassValue;
58
+ status?: tailwind_variants.ClassValue;
59
+ bubble?: tailwind_variants.ClassValue;
60
+ context?: tailwind_variants.ClassValue;
61
+ typing?: tailwind_variants.ClassValue;
62
+ dot?: tailwind_variants.ClassValue;
63
+ composer?: tailwind_variants.ClassValue;
64
+ item?: tailwind_variants.ClassValue;
65
+ count?: tailwind_variants.ClassValue;
66
+ };
67
+ };
68
+ } | {}, {
69
+ row: string;
70
+ bubble: string;
71
+ context: string;
72
+ time: string;
73
+ status: string;
74
+ typing: string;
75
+ dot: string;
76
+ composer: string;
77
+ input: string;
78
+ send: string;
79
+ item: string;
80
+ count: string;
81
+ }, tailwind_variants.TVReturnTypeLike<unknown, {
82
+ row: string;
83
+ bubble: string;
84
+ context: string;
85
+ time: string;
86
+ status: string;
87
+ typing: string;
88
+ dot: string;
89
+ composer: string;
90
+ input: string;
91
+ send: string;
92
+ item: string;
93
+ count: string;
94
+ }>>;
95
+ type MessageStatus = "sending" | "sent" | "delivered" | "read" | "failed";
96
+ type MessageBubbleLabels = {
97
+ /** The words of each status, shown under the last message of yours. */
98
+ status?: Partial<Record<MessageStatus, string>>;
99
+ /** A failed message's retry button (“Retry”). */
100
+ retry?: string;
101
+ };
102
+ type MessageBubbleProps = {
103
+ /** The text. */
104
+ text: string;
105
+ /** Yours (right, in the accent) or theirs (left). */
106
+ mine?: boolean;
107
+ /** The time, written by the app (“14:02”). */
108
+ time?: string;
109
+ /** Where a message of yours stands; shown when `showStatus`. */
110
+ status?: MessageStatus;
111
+ /** Show the status under the bubble (the last message of yours). */
112
+ showStatus?: boolean;
113
+ /** A line above the text: what it answers. */
114
+ context?: string;
115
+ /** Sends a failed message again (a button under it). */
116
+ onRetry?: () => void;
117
+ /** Part of a run of the same sender: the corner towards the next one is tighter. */
118
+ grouped?: "first" | "middle" | "last" | "single";
119
+ /** The words, passed translated. */
120
+ labels?: MessageBubbleLabels;
121
+ /** Classes merged on the row. */
122
+ className?: string;
123
+ };
124
+ /** A message: yours in the accent on the right, theirs on the left, the time inside, the receipt under the last of yours. */
125
+ declare function MessageBubble({ text, mine, time, status, showStatus, context, onRetry, grouped, labels, className }: MessageBubbleProps): React.JSX.Element;
126
+ type TypingIndicatorProps = {
127
+ /** What is read and shown beside the dots (“Elena is typing”) — passed translated. */
128
+ label: string;
129
+ /** Show the words beside the dots. */
130
+ showLabel?: boolean;
131
+ /** Classes merged on the root. */
132
+ className?: string;
133
+ };
134
+ /** Three dots that breathe while the other person types, announced politely; still under reduced motion. */
135
+ declare function TypingIndicator({ label, showLabel, className }: TypingIndicatorProps): React.JSX.Element;
136
+ type MessageComposerProps = {
137
+ /** The draft (controlled). */
138
+ value: string;
139
+ /** Called on every key. */
140
+ onValueChange: (text: string) => void;
141
+ /** Sends the draft (Enter, or the button); Shift+Enter adds a line. */
142
+ onSend: () => void;
143
+ /** The input's name and placeholder (“Message”). */
144
+ placeholder: string;
145
+ /** The send button's name (“Send”). */
146
+ sendLabel: string;
147
+ /** Before the input: an attach button. */
148
+ leading?: React.ReactNode;
149
+ /** Cannot write: the reason in its place. */
150
+ disabledReason?: string;
151
+ /** The longest message. Default 4000. */
152
+ maxLength?: number;
153
+ /** Classes merged on the root. */
154
+ className?: string;
155
+ };
156
+ /** Where a message is written: a growing textarea (Enter sends, Shift+Enter a new line), an attach slot, a send button. */
157
+ declare function MessageComposer({ value, onValueChange, onSend, placeholder, sendLabel, leading, disabledReason, maxLength, className }: MessageComposerProps): React.JSX.Element;
158
+ type ConversationItemProps = {
159
+ /** The other person's name. */
160
+ name: string;
161
+ /** Their avatar's URL. */
162
+ avatar?: string | null;
163
+ /** The last message, or “typing…”. */
164
+ preview: string;
165
+ /** The preview is the typing signal: in the accent. */
166
+ typing?: boolean;
167
+ /** When (“14:02”, “Mon”). */
168
+ time?: string;
169
+ /** Unread messages: a count, the name in bold. */
170
+ unread?: number;
171
+ /** The unread count in words (“3 unread”). */
172
+ unreadLabel?: string;
173
+ /** Before the name: a presence dot (online). */
174
+ online?: boolean;
175
+ /** The presence in words (“online”). */
176
+ onlineLabel?: string;
177
+ /** Opens the conversation (a link). */
178
+ href?: string;
179
+ /** Opens the conversation (a button). */
180
+ onSelect?: () => void;
181
+ /** Classes merged on the row. */
182
+ className?: string;
183
+ };
184
+ /** A conversation in the list: avatar, name, the last message or typing, the time, the unread count. */
185
+ declare function ConversationItem({ name, avatar, preview, typing, time, unread, unreadLabel, online, onlineLabel, href, onSelect, className }: ConversationItemProps): React.JSX.Element;
186
+
187
+ export { ConversationItem, type ConversationItemProps, MessageBubble, type MessageBubbleLabels, type MessageBubbleProps, MessageComposer, type MessageComposerProps, type MessageStatus, TypingIndicator, type TypingIndicatorProps, chat };
package/dist/chat.js ADDED
@@ -0,0 +1,132 @@
1
+ "use client";
2
+ import {
3
+ Avatar
4
+ } from "./chunk-F2GKFIYK.js";
5
+
6
+ // src/chat/chat.tsx
7
+ import { tv } from "tailwind-variants";
8
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
+ var chat = tv({
10
+ slots: {
11
+ row: "flex flex-col gap-0.5",
12
+ bubble: "max-w-[80%] rounded-2xl px-3 py-2 text-sm leading-snug",
13
+ context: "mb-0.5 block text-xs opacity-85",
14
+ time: "mt-0.5 block text-right text-[11px] tabular-nums",
15
+ status: "flex items-center gap-1 text-xs text-fg-secondary data-[status=failed]:text-fg-danger [&>svg]:h-3.5 [&>svg]:w-3.5",
16
+ typing: "inline-flex items-center gap-1 self-start rounded-2xl bg-surface-2 px-3.5 py-3",
17
+ dot: "h-[7px] w-[7px] rounded-full bg-fg-secondary motion-safe:animate-[kz-typing_1.2s_ease-in-out_infinite]",
18
+ composer: "flex items-end gap-2 border-t border-border-subtle bg-surface-0 px-3 py-2",
19
+ input: "max-h-32 min-h-10 flex-1 resize-none rounded-2xl border border-border-default bg-surface-1 px-3.5 py-2.5 text-sm text-fg placeholder:text-fg-muted focus-visible:border-accent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
20
+ send: "inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-accent text-on-accent hover:bg-accent-hover focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring disabled:bg-surface-3 disabled:text-fg-muted [&>svg]:h-[18px] [&>svg]:w-[18px]",
21
+ item: "flex w-full items-center gap-3 px-4 py-2.5 text-left hover:bg-surface-2 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
22
+ count: "inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-accent px-1.5 text-xs font-extrabold text-on-accent"
23
+ }
24
+ });
25
+ var STATUS = { sending: "Sending", sent: "Sent", delivered: "Delivered", read: "Read", failed: "Not sent" };
26
+ var CORNERS = {
27
+ mine: { first: "rounded-br-md", middle: "rounded-r-md", last: "rounded-tr-md", single: "" },
28
+ theirs: { first: "rounded-bl-md", middle: "rounded-l-md", last: "rounded-tl-md", single: "" }
29
+ };
30
+ function MessageBubble({ text, mine, time, status, showStatus, context, onRetry, grouped = "single", labels, className }) {
31
+ const s = chat();
32
+ const words = { ...STATUS, ...labels?.status };
33
+ return /* @__PURE__ */ jsxs("div", { className: s.row({ className: [mine ? "items-end" : "items-start", className].filter(Boolean).join(" ") }), children: [
34
+ /* @__PURE__ */ jsxs(
35
+ "p",
36
+ {
37
+ className: s.bubble({
38
+ className: [mine ? "bg-accent text-on-accent" : "bg-surface-2 text-fg", CORNERS[mine ? "mine" : "theirs"][grouped], status === "failed" ? "ring-2 ring-danger" : "", status === "sending" ? "opacity-70" : ""].join(" ")
39
+ }),
40
+ children: [
41
+ context ? /* @__PURE__ */ jsx("span", { className: s.context(), children: context }) : null,
42
+ text,
43
+ time ? /* @__PURE__ */ jsx("span", { className: s.time(), children: time }) : null
44
+ ]
45
+ }
46
+ ),
47
+ showStatus && status ? /* @__PURE__ */ jsxs("p", { className: s.status(), "data-status": status, children: [
48
+ status === "sent" || status === "delivered" || status === "read" ? /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, className: status === "read" ? "stroke-accent" : "stroke-fg-secondary", children: /* @__PURE__ */ jsx("path", { d: status === "sent" ? "M5 12.5l4.5 4.5L19 7.5" : "M2 12.5l4.5 4.5L16 7.5M9 15l2 2 9.5-9.5" }) }) : null,
49
+ words[status],
50
+ status === "failed" && onRetry ? /* @__PURE__ */ jsx("button", { type: "button", onClick: onRetry, className: "ml-1 font-semibold text-fg underline underline-offset-2 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring", children: labels?.retry ?? "Retry" }) : null
51
+ ] }) : null
52
+ ] });
53
+ }
54
+ function TypingIndicator({ label, showLabel, className }) {
55
+ const s = chat();
56
+ return /* @__PURE__ */ jsxs("div", { role: "status", className: ["flex items-center gap-2", className].filter(Boolean).join(" "), children: [
57
+ /* @__PURE__ */ jsx("style", { href: "krizaka-typing", precedence: "default", children: "@keyframes kz-typing{0%,60%,100%{opacity:.35;transform:none}30%{opacity:1;transform:translateY(-2px)}}" }),
58
+ /* @__PURE__ */ jsx("span", { className: s.typing(), "aria-hidden": true, children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx("span", { className: s.dot(), style: { animationDelay: `${i * 0.18}s` } }, i)) }),
59
+ /* @__PURE__ */ jsx("span", { className: showLabel ? "text-xs text-fg-secondary" : "sr-only", children: label })
60
+ ] });
61
+ }
62
+ function MessageComposer({ value, onValueChange, onSend, placeholder, sendLabel, leading, disabledReason, maxLength = 4e3, className }) {
63
+ const s = chat();
64
+ const ready = value.trim().length > 0;
65
+ return /* @__PURE__ */ jsx(
66
+ "form",
67
+ {
68
+ className: s.composer({ className }),
69
+ onSubmit: (e) => {
70
+ e.preventDefault();
71
+ if (ready) onSend();
72
+ },
73
+ children: disabledReason ? /* @__PURE__ */ jsx("p", { className: "m-0 flex-1 py-2 text-sm text-fg-secondary", children: disabledReason }) : /* @__PURE__ */ jsxs(Fragment, { children: [
74
+ leading,
75
+ /* @__PURE__ */ jsx(
76
+ "textarea",
77
+ {
78
+ "aria-label": placeholder,
79
+ placeholder,
80
+ rows: 1,
81
+ maxLength,
82
+ value,
83
+ onChange: (e) => onValueChange(e.target.value),
84
+ onKeyDown: (e) => {
85
+ if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
86
+ e.preventDefault();
87
+ if (ready) onSend();
88
+ }
89
+ },
90
+ className: s.input(),
91
+ style: { fieldSizing: "content" }
92
+ }
93
+ ),
94
+ /* @__PURE__ */ jsx("button", { type: "submit", "aria-label": sendLabel, disabled: !ready, className: s.send(), children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M4 12l16-8-6 16-2.5-6.5L4 12z" }) }) })
95
+ ] })
96
+ }
97
+ );
98
+ }
99
+ function ConversationItem({ name, avatar, preview, typing, time, unread = 0, unreadLabel, online, onlineLabel, href, onSelect, className }) {
100
+ const s = chat();
101
+ const body = /* @__PURE__ */ jsxs(Fragment, { children: [
102
+ /* @__PURE__ */ jsxs("span", { className: "relative shrink-0", children: [
103
+ /* @__PURE__ */ jsx(Avatar, { src: avatar ?? null, alt: "", fallback: name.charAt(0), size: "lg" }),
104
+ online ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "absolute bottom-0 right-0 h-3 w-3 rounded-full bg-success ring-2 ring-surface-0" }) : null
105
+ ] }),
106
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [
107
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
108
+ /* @__PURE__ */ jsxs("span", { className: ["min-w-0 flex-1 truncate text-sm text-fg", unread > 0 ? "font-extrabold" : "font-semibold"].join(" "), children: [
109
+ name,
110
+ online && onlineLabel ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: `, ${onlineLabel}` }) : null
111
+ ] }),
112
+ time ? /* @__PURE__ */ jsx("span", { className: ["shrink-0 text-xs tabular-nums", unread > 0 ? "text-fg-accent" : "text-fg-secondary"].join(" "), children: time }) : null
113
+ ] }),
114
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
115
+ /* @__PURE__ */ jsx("span", { className: ["min-w-0 flex-1 truncate text-xs", typing ? "text-fg-accent" : unread > 0 ? "text-fg" : "text-fg-secondary"].join(" "), children: preview }),
116
+ unread > 0 ? /* @__PURE__ */ jsxs("span", { className: s.count(), children: [
117
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: unread > 99 ? "99+" : unread }),
118
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: unreadLabel ?? String(unread) })
119
+ ] }) : null
120
+ ] })
121
+ ] })
122
+ ] });
123
+ return href ? /* @__PURE__ */ jsx("a", { href, className: s.item({ className }), children: body }) : /* @__PURE__ */ jsx("button", { type: "button", onClick: onSelect, className: s.item({ className }), children: body });
124
+ }
125
+ export {
126
+ ConversationItem,
127
+ MessageBubble,
128
+ MessageComposer,
129
+ TypingIndicator,
130
+ chat
131
+ };
132
+ //# sourceMappingURL=chat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/chat/chat.tsx"],"sourcesContent":["// Client (the composer's draft). A conversation's parts — the bubble with its time and receipt, the typing dots, the\n// composer, a conversation in a list — the same API as React Native. Words are props.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { Avatar } from \"../avatar\";\n\nexport const chat = tv({\n slots: {\n row: \"flex flex-col gap-0.5\",\n bubble: \"max-w-[80%] rounded-2xl px-3 py-2 text-sm leading-snug\",\n context: \"mb-0.5 block text-xs opacity-85\",\n time: \"mt-0.5 block text-right text-[11px] tabular-nums\",\n status: \"flex items-center gap-1 text-xs text-fg-secondary data-[status=failed]:text-fg-danger [&>svg]:h-3.5 [&>svg]:w-3.5\",\n typing: \"inline-flex items-center gap-1 self-start rounded-2xl bg-surface-2 px-3.5 py-3\",\n dot: \"h-[7px] w-[7px] rounded-full bg-fg-secondary motion-safe:animate-[kz-typing_1.2s_ease-in-out_infinite]\",\n composer: \"flex items-end gap-2 border-t border-border-subtle bg-surface-0 px-3 py-2\",\n input:\n \"max-h-32 min-h-10 flex-1 resize-none rounded-2xl border border-border-default bg-surface-1 px-3.5 py-2.5 text-sm text-fg placeholder:text-fg-muted \" +\n \"focus-visible:border-accent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n send:\n \"inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-accent text-on-accent hover:bg-accent-hover focus-visible:outline-hidden \" +\n \"focus-visible:ring-2 focus-visible:ring-ring disabled:bg-surface-3 disabled:text-fg-muted [&>svg]:h-[18px] [&>svg]:w-[18px]\",\n item: \"flex w-full items-center gap-3 px-4 py-2.5 text-left hover:bg-surface-2 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring\",\n count: \"inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-accent px-1.5 text-xs font-extrabold text-on-accent\",\n },\n});\n\nexport type MessageStatus = \"sending\" | \"sent\" | \"delivered\" | \"read\" | \"failed\";\n\nexport type MessageBubbleLabels = {\n /** The words of each status, shown under the last message of yours. */\n status?: Partial<Record<MessageStatus, string>>;\n /** A failed message's retry button (“Retry”). */\n retry?: string;\n};\n\nconst STATUS: Record<MessageStatus, string> = { sending: \"Sending\", sent: \"Sent\", delivered: \"Delivered\", read: \"Read\", failed: \"Not sent\" };\n\nexport type MessageBubbleProps = {\n /** The text. */\n text: string;\n /** Yours (right, in the accent) or theirs (left). */\n mine?: boolean;\n /** The time, written by the app (“14:02”). */\n time?: string;\n /** Where a message of yours stands; shown when `showStatus`. */\n status?: MessageStatus;\n /** Show the status under the bubble (the last message of yours). */\n showStatus?: boolean;\n /** A line above the text: what it answers. */\n context?: string;\n /** Sends a failed message again (a button under it). */\n onRetry?: () => void;\n /** Part of a run of the same sender: the corner towards the next one is tighter. */\n grouped?: \"first\" | \"middle\" | \"last\" | \"single\";\n /** The words, passed translated. */\n labels?: MessageBubbleLabels;\n /** Classes merged on the row. */\n className?: string;\n};\n\nconst CORNERS = {\n mine: { first: \"rounded-br-md\", middle: \"rounded-r-md\", last: \"rounded-tr-md\", single: \"\" },\n theirs: { first: \"rounded-bl-md\", middle: \"rounded-l-md\", last: \"rounded-tl-md\", single: \"\" },\n} as const;\n\n/** A message: yours in the accent on the right, theirs on the left, the time inside, the receipt under the last of yours. */\nexport function MessageBubble({ text, mine, time, status, showStatus, context, onRetry, grouped = \"single\", labels, className }: MessageBubbleProps) {\n const s = chat();\n const words = { ...STATUS, ...labels?.status };\n return (\n <div className={s.row({ className: [mine ? \"items-end\" : \"items-start\", className].filter(Boolean).join(\" \") })}>\n <p\n className={s.bubble({\n className: [mine ? \"bg-accent text-on-accent\" : \"bg-surface-2 text-fg\", CORNERS[mine ? \"mine\" : \"theirs\"][grouped], status === \"failed\" ? \"ring-2 ring-danger\" : \"\", status === \"sending\" ? \"opacity-70\" : \"\"].join(\" \"),\n })}\n >\n {context ? <span className={s.context()}>{context}</span> : null}\n {text}\n {time ? <span className={s.time()}>{time}</span> : null}\n </p>\n {showStatus && status ? (\n <p className={s.status()} data-status={status}>\n {status === \"sent\" || status === \"delivered\" || status === \"read\" ? (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" strokeWidth=\"2.2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden className={status === \"read\" ? \"stroke-accent\" : \"stroke-fg-secondary\"}>\n <path d={status === \"sent\" ? \"M5 12.5l4.5 4.5L19 7.5\" : \"M2 12.5l4.5 4.5L16 7.5M9 15l2 2 9.5-9.5\"} />\n </svg>\n ) : null}\n {words[status]}\n {status === \"failed\" && onRetry ? (\n <button type=\"button\" onClick={onRetry} className=\"ml-1 font-semibold text-fg underline underline-offset-2 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\">\n {labels?.retry ?? \"Retry\"}\n </button>\n ) : null}\n </p>\n ) : null}\n </div>\n );\n}\n\nexport type TypingIndicatorProps = {\n /** What is read and shown beside the dots (“Elena is typing”) — passed translated. */\n label: string;\n /** Show the words beside the dots. */\n showLabel?: boolean;\n /** Classes merged on the root. */\n className?: string;\n};\n\n/** Three dots that breathe while the other person types, announced politely; still under reduced motion. */\nexport function TypingIndicator({ label, showLabel, className }: TypingIndicatorProps) {\n const s = chat();\n return (\n <div role=\"status\" className={[\"flex items-center gap-2\", className].filter(Boolean).join(\" \")}>\n <style href=\"krizaka-typing\" precedence=\"default\">{\"@keyframes kz-typing{0%,60%,100%{opacity:.35;transform:none}30%{opacity:1;transform:translateY(-2px)}}\"}</style>\n <span className={s.typing()} aria-hidden>\n {[0, 1, 2].map((i) => (\n <span key={i} className={s.dot()} style={{ animationDelay: `${i * 0.18}s` }} />\n ))}\n </span>\n <span className={showLabel ? \"text-xs text-fg-secondary\" : \"sr-only\"}>{label}</span>\n </div>\n );\n}\n\nexport type MessageComposerProps = {\n /** The draft (controlled). */\n value: string;\n /** Called on every key. */\n onValueChange: (text: string) => void;\n /** Sends the draft (Enter, or the button); Shift+Enter adds a line. */\n onSend: () => void;\n /** The input's name and placeholder (“Message”). */\n placeholder: string;\n /** The send button's name (“Send”). */\n sendLabel: string;\n /** Before the input: an attach button. */\n leading?: React.ReactNode;\n /** Cannot write: the reason in its place. */\n disabledReason?: string;\n /** The longest message. Default 4000. */\n maxLength?: number;\n /** Classes merged on the root. */\n className?: string;\n};\n\n/** Where a message is written: a growing textarea (Enter sends, Shift+Enter a new line), an attach slot, a send button. */\nexport function MessageComposer({ value, onValueChange, onSend, placeholder, sendLabel, leading, disabledReason, maxLength = 4000, className }: MessageComposerProps) {\n const s = chat();\n const ready = value.trim().length > 0;\n return (\n <form\n className={s.composer({ className })}\n onSubmit={(e) => {\n e.preventDefault();\n if (ready) onSend();\n }}\n >\n {disabledReason ? (\n <p className=\"m-0 flex-1 py-2 text-sm text-fg-secondary\">{disabledReason}</p>\n ) : (\n <>\n {leading}\n <textarea\n aria-label={placeholder}\n placeholder={placeholder}\n rows={1}\n maxLength={maxLength}\n value={value}\n onChange={(e) => onValueChange(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey && !e.nativeEvent.isComposing) {\n e.preventDefault();\n if (ready) onSend();\n }\n }}\n className={s.input()}\n style={{ fieldSizing: \"content\" } as React.CSSProperties}\n />\n <button type=\"submit\" aria-label={sendLabel} disabled={!ready} className={s.send()}>\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden>\n <path d=\"M4 12l16-8-6 16-2.5-6.5L4 12z\" />\n </svg>\n </button>\n </>\n )}\n </form>\n );\n}\n\nexport type ConversationItemProps = {\n /** The other person's name. */\n name: string;\n /** Their avatar's URL. */\n avatar?: string | null;\n /** The last message, or “typing…”. */\n preview: string;\n /** The preview is the typing signal: in the accent. */\n typing?: boolean;\n /** When (“14:02”, “Mon”). */\n time?: string;\n /** Unread messages: a count, the name in bold. */\n unread?: number;\n /** The unread count in words (“3 unread”). */\n unreadLabel?: string;\n /** Before the name: a presence dot (online). */\n online?: boolean;\n /** The presence in words (“online”). */\n onlineLabel?: string;\n /** Opens the conversation (a link). */\n href?: string;\n /** Opens the conversation (a button). */\n onSelect?: () => void;\n /** Classes merged on the row. */\n className?: string;\n};\n\n/** A conversation in the list: avatar, name, the last message or typing, the time, the unread count. */\nexport function ConversationItem({ name, avatar, preview, typing, time, unread = 0, unreadLabel, online, onlineLabel, href, onSelect, className }: ConversationItemProps) {\n const s = chat();\n const body = (\n <>\n <span className=\"relative shrink-0\">\n <Avatar src={avatar ?? null} alt=\"\" fallback={name.charAt(0)} size=\"lg\" />\n {online ? <span aria-hidden className=\"absolute bottom-0 right-0 h-3 w-3 rounded-full bg-success ring-2 ring-surface-0\" /> : null}\n </span>\n <span className=\"flex min-w-0 flex-1 flex-col\">\n <span className=\"flex items-center gap-2\">\n <span className={[\"min-w-0 flex-1 truncate text-sm text-fg\", unread > 0 ? \"font-extrabold\" : \"font-semibold\"].join(\" \")}>\n {name}\n {online && onlineLabel ? <span className=\"sr-only\">{`, ${onlineLabel}`}</span> : null}\n </span>\n {time ? <span className={[\"shrink-0 text-xs tabular-nums\", unread > 0 ? \"text-fg-accent\" : \"text-fg-secondary\"].join(\" \")}>{time}</span> : null}\n </span>\n <span className=\"flex items-center gap-2\">\n <span className={[\"min-w-0 flex-1 truncate text-xs\", typing ? \"text-fg-accent\" : unread > 0 ? \"text-fg\" : \"text-fg-secondary\"].join(\" \")}>{preview}</span>\n {unread > 0 ? (\n <span className={s.count()}>\n <span aria-hidden>{unread > 99 ? \"99+\" : unread}</span>\n <span className=\"sr-only\">{unreadLabel ?? String(unread)}</span>\n </span>\n ) : null}\n </span>\n </span>\n </>\n );\n return href ? (\n <a href={href} className={s.item({ className })}>\n {body}\n </a>\n ) : (\n <button type=\"button\" onClick={onSelect} className={s.item({ className })}>\n {body}\n </button>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,25 @@
1
+ "use client";
2
+
3
+ // src/skip-link/skip-link.tsx
4
+ import { tv } from "tailwind-variants";
5
+ import { jsx } from "react/jsx-runtime";
6
+ var skipLink = tv({
7
+ base: "sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-[100] focus:rounded-full focus:bg-surface-1 focus:px-4 focus:py-2 focus:text-sm focus:font-semibold focus:text-fg focus:no-underline focus:shadow-lg focus:outline-hidden focus:ring-2 focus:ring-ring"
8
+ });
9
+ function SkipLink({ href = "#content", className, onClick, children, ...props }) {
10
+ const handleClick = (event) => {
11
+ onClick?.(event);
12
+ if (event.defaultPrevented) return;
13
+ const target = document.getElementById(decodeURIComponent(href.slice(1)));
14
+ if (!target) return;
15
+ if (!target.hasAttribute("tabindex") && !target.matches("a[href], button, input, select, textarea, [contenteditable]")) target.setAttribute("tabindex", "-1");
16
+ target.focus();
17
+ };
18
+ return /* @__PURE__ */ jsx("a", { href, className: skipLink({ className }), onClick: handleClick, ...props, children });
19
+ }
20
+
21
+ export {
22
+ skipLink,
23
+ SkipLink
24
+ };
25
+ //# sourceMappingURL=chunk-3GODX3D6.js.map