@godxjp/ui 19.1.0 → 19.3.1

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 (437) hide show
  1. package/README.md +25 -128
  2. package/dist/app/theme-axes.d.ts +4 -15
  3. package/dist/app/timezones.d.ts +1 -4
  4. package/dist/components/charts/area-chart.d.ts +2 -3
  5. package/dist/components/charts/bar-chart.d.ts +2 -3
  6. package/dist/components/charts/chart-frame.d.ts +7 -16
  7. package/dist/components/charts/chart-summary.d.ts +2 -2
  8. package/dist/components/charts/compact-bar-trend.d.ts +9 -24
  9. package/dist/components/charts/index.d.ts +1 -6
  10. package/dist/components/data-display/badge.d.ts +20 -14
  11. package/dist/components/data-display/badge.js +18 -4
  12. package/dist/components/data-display/card.d.ts +21 -41
  13. package/dist/components/data-display/carousel.d.ts +1 -2
  14. package/dist/components/data-display/code-block.d.ts +4 -0
  15. package/dist/components/data-display/code-block.js +24 -0
  16. package/dist/components/data-display/data-table.d.ts +15 -37
  17. package/dist/components/data-display/data-table.js +1 -3
  18. package/dist/components/data-display/descriptions.d.ts +2 -6
  19. package/dist/components/data-display/descriptions.js +0 -2
  20. package/dist/components/data-display/index.d.ts +4 -0
  21. package/dist/components/data-display/index.js +4 -0
  22. package/dist/components/data-display/list-row.d.ts +3 -22
  23. package/dist/components/data-display/permission-matrix.d.ts +7 -20
  24. package/dist/components/data-display/progress.d.ts +1 -1
  25. package/dist/components/data-display/prose.d.ts +4 -0
  26. package/dist/components/data-display/prose.js +20 -0
  27. package/dist/components/data-display/service-launcher-card.d.ts +3 -10
  28. package/dist/components/data-display/table.d.ts +20 -80
  29. package/dist/components/data-display/table.js +0 -3
  30. package/dist/components/data-display/timeline.d.ts +3 -5
  31. package/dist/components/data-entry/branch-scope-picker.d.ts +2 -19
  32. package/dist/components/data-entry/branch-scope-picker.js +0 -1
  33. package/dist/components/data-entry/calendar.d.ts +3 -2
  34. package/dist/components/data-entry/calendar.js +58 -3
  35. package/dist/components/data-entry/command-palette.d.ts +8 -22
  36. package/dist/components/data-entry/date-picker.d.ts +4 -5
  37. package/dist/components/data-entry/date-picker.js +6 -1
  38. package/dist/components/data-entry/date-range-picker.d.ts +1 -1
  39. package/dist/components/data-entry/date-range-picker.js +8 -3
  40. package/dist/components/data-entry/form-errors.d.ts +7 -18
  41. package/dist/components/data-entry/form-field.js +1 -5
  42. package/dist/components/data-entry/form.d.ts +1 -9
  43. package/dist/components/data-entry/input.d.ts +16 -14
  44. package/dist/components/data-entry/input.js +3 -0
  45. package/dist/components/data-entry/month-picker.js +2 -2
  46. package/dist/components/data-entry/month-range-picker.d.ts +4 -6
  47. package/dist/components/data-entry/month-range-picker.js +2 -2
  48. package/dist/components/data-entry/number-input.d.ts +1 -5
  49. package/dist/components/data-entry/number-input.js +1 -0
  50. package/dist/components/data-entry/radio.js +0 -1
  51. package/dist/components/data-entry/search-select.d.ts +2 -4
  52. package/dist/components/data-entry/select.d.ts +11 -6
  53. package/dist/components/data-entry/select.js +4 -2
  54. package/dist/components/data-entry/switch.js +0 -1
  55. package/dist/components/data-entry/textarea.d.ts +10 -16
  56. package/dist/components/data-entry/time-picker.d.ts +3 -5
  57. package/dist/components/data-entry/upload-types.d.ts +1 -5
  58. package/dist/components/data-entry/use-upload-draft.d.ts +0 -4
  59. package/dist/components/feedback/dialog.d.ts +6 -15
  60. package/dist/components/feedback/overlay-header-tone.d.ts +1 -1
  61. package/dist/components/feedback/sheet.d.ts +3 -10
  62. package/dist/components/feedback/sheet.js +1 -1
  63. package/dist/components/feedback/sonner.js +2 -3
  64. package/dist/components/general/activity.d.ts +4 -30
  65. package/dist/components/general/button.js +1 -10
  66. package/dist/components/general/logo.d.ts +6 -28
  67. package/dist/components/general/reveal.d.ts +3 -9
  68. package/dist/components/general/typography.d.ts +1 -3
  69. package/dist/components/layout/account-chip.d.ts +8 -0
  70. package/dist/components/layout/account-chip.js +45 -0
  71. package/dist/components/layout/auth-divider.d.ts +2 -9
  72. package/dist/components/layout/auth-shell.d.ts +1 -14
  73. package/dist/components/layout/breadcrumb.d.ts +1 -5
  74. package/dist/components/layout/centered-shell.d.ts +4 -18
  75. package/dist/components/layout/error-surface.d.ts +11 -28
  76. package/dist/components/layout/error-surface.js +4 -1
  77. package/dist/components/layout/index.d.ts +2 -0
  78. package/dist/components/layout/index.js +2 -0
  79. package/dist/components/layout/legal-document-shell.d.ts +5 -28
  80. package/dist/components/layout/master-detail.d.ts +2 -18
  81. package/dist/components/layout/page-container.js +0 -1
  82. package/dist/components/layout/responsive-grid.d.ts +6 -1
  83. package/dist/components/layout/responsive-grid.js +9 -2
  84. package/dist/components/layout/service-role-panel.d.ts +7 -19
  85. package/dist/components/layout/sidebar.d.ts +5 -27
  86. package/dist/components/layout/sidebar.js +0 -1
  87. package/dist/components/layout/split-pane.d.ts +0 -7
  88. package/dist/components/layout/topbar.d.ts +1 -14
  89. package/dist/components/navigation/app-setting-picker.js +1 -3
  90. package/dist/components/navigation/filter-bar.d.ts +1 -6
  91. package/dist/components/navigation/pagination-utils.d.ts +2 -12
  92. package/dist/components/navigation/tabs-scroll.d.ts +2 -36
  93. package/dist/components/navigation/tabs.js +4 -8
  94. package/dist/components/query/data-state.d.ts +2 -2
  95. package/dist/components/ui/credential-reveal.d.ts +1 -4
  96. package/dist/components/ui/hover-card.d.ts +1 -2
  97. package/dist/components/ui/separator.d.ts +2 -15
  98. package/dist/components/ui/toggle-group.d.ts +1 -5
  99. package/dist/components/ui/toggle.d.ts +6 -31
  100. package/dist/email/brand-mark.d.ts +6 -34
  101. package/dist/email/color.d.ts +7 -17
  102. package/dist/email/geometry.d.ts +4 -8
  103. package/dist/email/index.d.ts +4 -48
  104. package/dist/email/inline-style.d.ts +3 -9
  105. package/dist/email/urgency.d.ts +2 -7
  106. package/dist/form/form-context.d.ts +2 -5
  107. package/dist/form/form-root.d.ts +1 -2
  108. package/dist/i18n/messages/en.json +4 -0
  109. package/dist/i18n/messages/ja.json +4 -0
  110. package/dist/i18n/messages/vi.json +4 -0
  111. package/dist/inertia/index.d.ts +2 -10
  112. package/dist/lib/control-styles.d.ts +10 -21
  113. package/dist/lib/control-styles.js +4 -6
  114. package/dist/lib/datetime/format-date.d.ts +1 -4
  115. package/dist/lib/field-a11y.d.ts +11 -68
  116. package/dist/lib/field-a11y.js +0 -1
  117. package/dist/lib/format.d.ts +4 -8
  118. package/dist/lib/hooks.d.ts +6 -12
  119. package/dist/lib/permission-grid.d.ts +3 -18
  120. package/dist/lib/query-error.d.ts +3 -3
  121. package/dist/props/components/app.prop.d.ts +15 -38
  122. package/dist/props/components/charts.prop.d.ts +2 -3
  123. package/dist/props/components/data-display.prop.d.ts +55 -72
  124. package/dist/props/components/data-entry.prop.d.ts +35 -40
  125. package/dist/props/components/form.prop.d.ts +2 -6
  126. package/dist/props/components/general.prop.d.ts +11 -27
  127. package/dist/props/components/layout.prop.d.ts +89 -260
  128. package/dist/props/components/navigation.prop.d.ts +27 -42
  129. package/dist/props/registry.d.ts +36 -1
  130. package/dist/props/registry.js +45 -1
  131. package/dist/props/vocabulary/data.prop.d.ts +18 -49
  132. package/dist/props/vocabulary/index.d.ts +1 -1
  133. package/dist/props/vocabulary/interaction.prop.d.ts +10 -12
  134. package/dist/props/vocabulary/layout.prop.d.ts +8 -30
  135. package/dist/props/vocabulary/shared.prop.d.ts +3 -2
  136. package/dist/styles/alert-layout.css +1 -32
  137. package/dist/styles/badge-layout.css +19 -18
  138. package/dist/styles/base.css +14 -113
  139. package/dist/styles/card-layout.css +6 -128
  140. package/dist/styles/chart-layout.css +2 -17
  141. package/dist/styles/control.css +380 -458
  142. package/dist/styles/core.css +21 -0
  143. package/dist/styles/data-display-layout.css +175 -94
  144. package/dist/styles/data-entry-layout.css +0 -45
  145. package/dist/styles/density.css +1 -30
  146. package/dist/styles/dialog-layout.css +5 -69
  147. package/dist/styles/focus-ring.css +13 -87
  148. package/dist/styles/fonts.css +1 -31
  149. package/dist/styles/form-layout.css +7 -91
  150. package/dist/styles/index.css +2 -20
  151. package/dist/styles/layout.css +38 -456
  152. package/dist/styles/logo-layout.css +1 -35
  153. package/dist/styles/motion.css +3 -42
  154. package/dist/styles/navigation-layout.css +3 -50
  155. package/dist/styles/shell-layout.css +37 -403
  156. package/dist/styles/table-layout.css +16 -274
  157. package/dist/styles/text-layout.css +7 -21
  158. package/dist/styles/toggle.css +1 -23
  159. package/dist/theme/dxs.canonical.css +1 -55
  160. package/dist/theme/example.service.css +1 -26
  161. package/dist/theme/famgia.service.css +5 -15
  162. package/dist/tokens/axes.css +3 -40
  163. package/dist/tokens/base.css +1 -4
  164. package/dist/tokens/components/activity.css +6 -42
  165. package/dist/tokens/components/badge.css +12 -13
  166. package/dist/tokens/components/banner.css +4 -9
  167. package/dist/tokens/components/card.css +32 -88
  168. package/dist/tokens/components/chart.css +9 -26
  169. package/dist/tokens/components/control.css +37 -222
  170. package/dist/tokens/components/data-display.css +28 -79
  171. package/dist/tokens/components/data-entry.css +5 -19
  172. package/dist/tokens/components/descriptions.css +7 -27
  173. package/dist/tokens/components/email.css +38 -84
  174. package/dist/tokens/components/error-surface.css +2 -18
  175. package/dist/tokens/components/feedback.css +27 -124
  176. package/dist/tokens/components/form.css +13 -45
  177. package/dist/tokens/components/legal-document.css +16 -41
  178. package/dist/tokens/components/list-row.css +11 -36
  179. package/dist/tokens/components/logo.css +2 -57
  180. package/dist/tokens/components/navigation.css +16 -94
  181. package/dist/tokens/components/scroll-area.css +2 -18
  182. package/dist/tokens/components/separator.css +8 -30
  183. package/dist/tokens/components/sheet.css +3 -14
  184. package/dist/tokens/components/shell.css +53 -307
  185. package/dist/tokens/components/sidebar.css +5 -22
  186. package/dist/tokens/components/table.css +19 -140
  187. package/dist/tokens/components/toggle.css +3 -65
  188. package/dist/tokens/components/upload.css +7 -43
  189. package/dist/tokens/foundation.css +108 -383
  190. package/dist/tokens/semantic/layout.css +33 -207
  191. package/docs/CANONICAL-CONTRACTS.md +206 -0
  192. package/docs/COMPONENTS.md +157 -0
  193. package/docs/COMPOSITION-VS-COMPONENT.md +105 -0
  194. package/docs/CONSUMER-RULES.md +14 -0
  195. package/docs/CUSTOMER-THEMING.md +162 -0
  196. package/docs/DATETIME.md +69 -0
  197. package/docs/DEVELOPMENT.md +173 -0
  198. package/docs/FORMS.md +143 -0
  199. package/docs/FRAME-A11Y-CI.md +267 -0
  200. package/docs/FRAME-COVERAGE-LEDGER.md +123 -0
  201. package/docs/FRAME-COVERAGE-REPORT.md +140 -0
  202. package/docs/FRAME-COVERAGE-STANDARD.md +65 -0
  203. package/docs/FRAME-COVERAGE.md +29 -0
  204. package/docs/PROPS-REGISTRY.md +61 -0
  205. package/docs/PROPS-VOCABULARY.md +100 -0
  206. package/docs/README.md +60 -0
  207. package/docs/REGISTRY.md +70 -0
  208. package/docs/SPACING.md +56 -0
  209. package/docs/STANDARDS-vocabulary-tokens.md +48 -0
  210. package/docs/TESTING.md +98 -0
  211. package/docs/TOKENS.md +347 -0
  212. package/docs/asset-modules.d.ts +46 -0
  213. package/docs/assets/cover-terrain.svg +6 -0
  214. package/docs/assets/portrait-amber.svg +5 -0
  215. package/docs/assets/portrait-clay.svg +5 -0
  216. package/docs/assets/portrait-indigo.svg +5 -0
  217. package/docs/assets/portrait-iris.svg +5 -0
  218. package/docs/assets/portrait-moss.svg +5 -0
  219. package/docs/assets/portrait-plum.svg +5 -0
  220. package/docs/assets/portrait-slate.svg +5 -0
  221. package/docs/charts/compact-bar-trend.tsx +328 -0
  222. package/docs/charts/size-tiers.tsx +192 -0
  223. package/docs/data-display/accordion.tsx +99 -0
  224. package/docs/data-display/avatar.tsx +355 -0
  225. package/docs/data-display/badge.tsx +283 -0
  226. package/docs/data-display/card/examples/detail-panel.tsx +106 -0
  227. package/docs/data-display/card/index.md +3 -0
  228. package/docs/data-display/card/index.tsx +285 -0
  229. package/docs/data-display/carousel.tsx +160 -0
  230. package/docs/data-display/charts.tsx +198 -0
  231. package/docs/data-display/code-block.tsx +90 -0
  232. package/docs/data-display/collapsible.tsx +162 -0
  233. package/docs/data-display/credential-reveal.tsx +145 -0
  234. package/docs/data-display/data-table/examples/approval-queue.tsx +212 -0
  235. package/docs/data-display/data-table/examples/grid-features.tsx +158 -0
  236. package/docs/data-display/data-table/examples/list-page.tsx +175 -0
  237. package/docs/data-display/data-table/index.md +5 -0
  238. package/docs/data-display/data-table/index.tsx +295 -0
  239. package/docs/data-display/descriptions.tsx +143 -0
  240. package/docs/data-display/empty-state.tsx +164 -0
  241. package/docs/data-display/hover-card.tsx +181 -0
  242. package/docs/data-display/list-row.tsx +420 -0
  243. package/docs/data-display/permission-matrix.tsx +188 -0
  244. package/docs/data-display/popover-touch.tsx +27 -0
  245. package/docs/data-display/popover.tsx +246 -0
  246. package/docs/data-display/progress.tsx +84 -0
  247. package/docs/data-display/prose.tsx +105 -0
  248. package/docs/data-display/qr-code.tsx +68 -0
  249. package/docs/data-display/scroll-area.tsx +303 -0
  250. package/docs/data-display/service-launcher-card.tsx +145 -0
  251. package/docs/data-display/stat-card.tsx +99 -0
  252. package/docs/data-display/table.tsx +251 -0
  253. package/docs/data-display/timeline.tsx +112 -0
  254. package/docs/data-display/touch-actions.tsx +36 -0
  255. package/docs/data-display/tree-list.tsx +107 -0
  256. package/docs/data-entry/branch-scope-picker.tsx +123 -0
  257. package/docs/data-entry/calendar.tsx +180 -0
  258. package/docs/data-entry/cascader.tsx +378 -0
  259. package/docs/data-entry/checkbox.tsx +256 -0
  260. package/docs/data-entry/color-picker.tsx +117 -0
  261. package/docs/data-entry/command-palette.tsx +179 -0
  262. package/docs/data-entry/command.tsx +262 -0
  263. package/docs/data-entry/country-picker-recipe.tsx +175 -0
  264. package/docs/data-entry/date-picker.tsx +191 -0
  265. package/docs/data-entry/date-range-picker.tsx +149 -0
  266. package/docs/data-entry/form/examples/employee-settings.tsx +267 -0
  267. package/docs/data-entry/form/examples/invoice-form.tsx +387 -0
  268. package/docs/data-entry/form-field/examples/a11y-contract.tsx +240 -0
  269. package/docs/data-entry/form-field/examples/create-form.tsx +105 -0
  270. package/docs/data-entry/form-field/index.md +8 -0
  271. package/docs/data-entry/form-field/index.tsx +330 -0
  272. package/docs/data-entry/form-inertia.tsx +80 -0
  273. package/docs/data-entry/form-root.tsx +102 -0
  274. package/docs/data-entry/form.tsx +724 -0
  275. package/docs/data-entry/input-otp.tsx +231 -0
  276. package/docs/data-entry/input.tsx +114 -0
  277. package/docs/data-entry/label.tsx +100 -0
  278. package/docs/data-entry/month-picker.tsx +101 -0
  279. package/docs/data-entry/month-range-picker.tsx +115 -0
  280. package/docs/data-entry/number-input.tsx +240 -0
  281. package/docs/data-entry/password-input.tsx +102 -0
  282. package/docs/data-entry/password-strength.tsx +97 -0
  283. package/docs/data-entry/radio-group.tsx +95 -0
  284. package/docs/data-entry/rating.tsx +87 -0
  285. package/docs/data-entry/search-input.tsx +120 -0
  286. package/docs/data-entry/select-async-contracts.tsx +108 -0
  287. package/docs/data-entry/select-matrix.tsx +204 -0
  288. package/docs/data-entry/select.tsx +369 -0
  289. package/docs/data-entry/slider.tsx +130 -0
  290. package/docs/data-entry/switch.tsx +67 -0
  291. package/docs/data-entry/tag-input.tsx +108 -0
  292. package/docs/data-entry/textarea.tsx +209 -0
  293. package/docs/data-entry/time-picker.tsx +99 -0
  294. package/docs/data-entry/toggle-count.tsx +293 -0
  295. package/docs/data-entry/toggle-group.tsx +225 -0
  296. package/docs/data-entry/toggle.tsx +127 -0
  297. package/docs/data-entry/transfer.tsx +97 -0
  298. package/docs/data-entry/tree-select.tsx +204 -0
  299. package/docs/data-entry/upload.tsx +184 -0
  300. package/docs/feedback/alert-dialog.tsx +270 -0
  301. package/docs/feedback/alert.tsx +248 -0
  302. package/docs/feedback/banner.tsx +183 -0
  303. package/docs/feedback/danger-confirm.tsx +88 -0
  304. package/docs/feedback/dialog-touch.tsx +24 -0
  305. package/docs/feedback/dialog.tsx +193 -0
  306. package/docs/feedback/sheet.tsx +420 -0
  307. package/docs/feedback/skeleton.tsx +111 -0
  308. package/docs/feedback/toast.tsx +135 -0
  309. package/docs/feedback/tooltip.tsx +171 -0
  310. package/docs/feedback/two-factor-setup.tsx +68 -0
  311. package/docs/foundation/_email-specimen.ts +274 -0
  312. package/docs/foundation/colors.tsx +204 -0
  313. package/docs/foundation/density.tsx +232 -0
  314. package/docs/foundation/email-tokens.tsx +323 -0
  315. package/docs/foundation/radius-shadow.tsx +154 -0
  316. package/docs/foundation/spacing.tsx +211 -0
  317. package/docs/foundation/typography.tsx +147 -0
  318. package/docs/general/activity.tsx +224 -0
  319. package/docs/general/button/examples/destructive-confirm.tsx +90 -0
  320. package/docs/general/button/examples/form-actions.tsx +85 -0
  321. package/docs/general/button/examples/toolbar.tsx +160 -0
  322. package/docs/general/button/index.md +12 -0
  323. package/docs/general/button/index.tsx +211 -0
  324. package/docs/general/logo.tsx +131 -0
  325. package/docs/general/reveal.tsx +41 -0
  326. package/docs/general/typography.tsx +152 -0
  327. package/docs/layout/account-chip.tsx +72 -0
  328. package/docs/layout/admin-collection.tsx +122 -0
  329. package/docs/layout/app-shell.tsx +374 -0
  330. package/docs/layout/aspect-ratio.tsx +135 -0
  331. package/docs/layout/auth-account-summary.tsx +103 -0
  332. package/docs/layout/auth-divider.tsx +28 -0
  333. package/docs/layout/auth-footer.tsx +37 -0
  334. package/docs/layout/auth-identity.tsx +36 -0
  335. package/docs/layout/auth-recovery/examples/mfa-challenge.md +41 -0
  336. package/docs/layout/auth-recovery/examples/mfa-challenge.tsx +254 -0
  337. package/docs/layout/auth-recovery/examples/mobile-390.md +25 -0
  338. package/docs/layout/auth-recovery/examples/mobile-390.tsx +82 -0
  339. package/docs/layout/auth-recovery/examples/password-recovery.md +24 -0
  340. package/docs/layout/auth-recovery/examples/password-recovery.tsx +197 -0
  341. package/docs/layout/auth-recovery/examples/wrapping-ja-en-vi.md +27 -0
  342. package/docs/layout/auth-recovery/examples/wrapping-ja-en-vi.tsx +156 -0
  343. package/docs/layout/auth-recovery/index.md +140 -0
  344. package/docs/layout/auth-recovery/index.tsx +96 -0
  345. package/docs/layout/auth-shell-context.tsx +103 -0
  346. package/docs/layout/auth-shell-device.tsx +112 -0
  347. package/docs/layout/auth-shell-registration.tsx +335 -0
  348. package/docs/layout/auth-shell-variants.tsx +139 -0
  349. package/docs/layout/auth-shell.tsx +59 -0
  350. package/docs/layout/auth-stack.tsx +40 -0
  351. package/docs/layout/centered-shell.tsx +102 -0
  352. package/docs/layout/error-surface/examples/application-403.tsx +84 -0
  353. package/docs/layout/error-surface/examples/application-404.tsx +77 -0
  354. package/docs/layout/error-surface/examples/system-500.tsx +47 -0
  355. package/docs/layout/error-surface/examples/system-503.tsx +48 -0
  356. package/docs/layout/error-surface/index.md +153 -0
  357. package/docs/layout/error-surface/index.tsx +239 -0
  358. package/docs/layout/flex.tsx +266 -0
  359. package/docs/layout/legal-document-shell/_data.ts +110 -0
  360. package/docs/layout/legal-document-shell/examples/desktop-1440.md +12 -0
  361. package/docs/layout/legal-document-shell/examples/desktop-1440.tsx +83 -0
  362. package/docs/layout/legal-document-shell/examples/mobile-390.md +22 -0
  363. package/docs/layout/legal-document-shell/examples/mobile-390.tsx +70 -0
  364. package/docs/layout/legal-document-shell/examples/tablet-1024.md +3 -0
  365. package/docs/layout/legal-document-shell/examples/tablet-1024.tsx +84 -0
  366. package/docs/layout/legal-document-shell/examples/wrapping-ja-en-vi.md +18 -0
  367. package/docs/layout/legal-document-shell/examples/wrapping-ja-en-vi.tsx +61 -0
  368. package/docs/layout/legal-document-shell/index.md +51 -0
  369. package/docs/layout/legal-document-shell/index.tsx +101 -0
  370. package/docs/layout/master-detail.tsx +387 -0
  371. package/docs/layout/org-switcher.tsx +131 -0
  372. package/docs/layout/page-container.tsx +954 -0
  373. package/docs/layout/resizable-panel.tsx +419 -0
  374. package/docs/layout/responsive-grid.tsx +243 -0
  375. package/docs/layout/separator.tsx +327 -0
  376. package/docs/layout/service-role-panel.tsx +144 -0
  377. package/docs/layout/sidebar.tsx +609 -0
  378. package/docs/layout/split-pane.tsx +468 -0
  379. package/docs/layout/topbar.tsx +349 -0
  380. package/docs/navigation/app-setting-picker.tsx +142 -0
  381. package/docs/navigation/breadcrumb.tsx +135 -0
  382. package/docs/navigation/context-menu.tsx +123 -0
  383. package/docs/navigation/dropdown-menu.tsx +213 -0
  384. package/docs/navigation/filter-bar.tsx +227 -0
  385. package/docs/navigation/menubar.tsx +141 -0
  386. package/docs/navigation/navigation-menu.tsx +158 -0
  387. package/docs/navigation/pagination.tsx +241 -0
  388. package/docs/navigation/steps.tsx +265 -0
  389. package/docs/navigation/tabs-rtl.tsx +28 -0
  390. package/docs/navigation/tabs.tsx +314 -0
  391. package/docs/navigation/toolbar.tsx +339 -0
  392. package/docs/providers/app-provider.tsx +191 -0
  393. package/docs/providers/format-date.tsx +230 -0
  394. package/docs/query/button-refetch.tsx +160 -0
  395. package/docs/query/data-state.tsx +192 -0
  396. package/docs/query/infinite-query-state.tsx +93 -0
  397. package/docs/query/mutation-feedback.tsx +70 -0
  398. package/docs/query/prefetch-link.tsx +262 -0
  399. package/docs/query/touch-actions.tsx +68 -0
  400. package/docs/showcase/acme-portal.tsx +455 -0
  401. package/docs/showcase/acme-website.tsx +461 -0
  402. package/docs/showcase/case1-warehouse-dashboard.tsx +359 -0
  403. package/docs/showcase/case2-employee-me.tsx +444 -0
  404. package/docs/showcase/case3-approval-workflow.tsx +529 -0
  405. package/docs/showcase/case4-login.tsx +313 -0
  406. package/docs/showcase/case5-shift-calendar.tsx +795 -0
  407. package/docs/showcase/case6-agency-handy.tsx +1113 -0
  408. package/docs/showcase/futurelastic-web.tsx +392 -0
  409. package/docs/showcase/org-switcher.tsx +313 -0
  410. package/docs/showcase/permission-matrix.tsx +346 -0
  411. package/docs/showcase/public-landing.tsx +210 -0
  412. package/docs/showcase/settings-account-sections.tsx +559 -0
  413. package/docs/showcase/settings-security-mfa.tsx +145 -0
  414. package/docs/showcase/table-approval-queue.tsx +199 -0
  415. package/docs/showcase/table-bulk-actions.tsx +460 -0
  416. package/docs/showcase/table-compact-kintone.tsx +438 -0
  417. package/docs/showcase/table-conditional-format.tsx +367 -0
  418. package/docs/showcase/table-crud-list.tsx +544 -0
  419. package/docs/showcase/table-density.tsx +347 -0
  420. package/docs/showcase/table-expandable-rows.tsx +480 -0
  421. package/docs/showcase/table-filter-chips.tsx +456 -0
  422. package/docs/showcase/table-footer-totals.tsx +314 -0
  423. package/docs/showcase/table-grouped-subtotals.tsx +391 -0
  424. package/docs/showcase/table-master-detail.tsx +484 -0
  425. package/docs/showcase/table-pagination.tsx +327 -0
  426. package/docs/showcase/table-states.tsx +366 -0
  427. package/docs/showcase/table-sticky-columns.tsx +376 -0
  428. package/docs/showcase/table-tree-rows.tsx +444 -0
  429. package/docs/showcase/table-view-tabs.tsx +488 -0
  430. package/package.json +9 -12
  431. package/scripts/_agent-setup.mjs +16 -2
  432. package/scripts/audit-hook.mjs +3 -13
  433. package/scripts/init-agent-kit.mjs +1 -14
  434. package/scripts/postinstall.mjs +12 -12
  435. package/scripts/ui-audit.mjs +93 -31
  436. package/scripts/visual-audit-rules.mjs +51 -7
  437. package/scripts/visual-audit.mjs +118 -47
@@ -0,0 +1,795 @@
1
+ /**
2
+ * Showcase · case5 — Shift Calendar (シフトカレンダー)
3
+ *
4
+ * A standalone shift-scheduling screen, served at `/showcase/case5-shift-calendar`.
5
+ * Built ENTIRELY from real @godxjp/ui primitives — the reference-design
6
+ * `admin/[brandSlug]/employee-shifts/calendar` surface recreated as a "skeleton"
7
+ * (intent + look), not a transcription of `comp-calendar.html`.
8
+ *
9
+ * Composition map (prototype block → @godxjp/ui primitive):
10
+ * page chrome ............. AppShell + Sidebar + Topbar
11
+ * page header + nav ....... PageContainer (extra slot) + Button + ToggleGroup (月/週/日)
12
+ * month jump-to ........... Calendar (date-picker) inside a Popover
13
+ * month event grid ........ Card + a 7-col CSS grid of day cells + shift "pills" ── GAP, see below
14
+ * week time-axis + now-line Card + a 06–21 row grid + an absolutely-placed now bar ── GAP, see below
15
+ * day staff×time .......... Card + Timeline (per-staff lane) + EmptyState (understaffed)
16
+ * shift legend ............ Card + the wa-iro decorative palette swatches
17
+ * date detail (mobile) .... Sheet (master-detail collapses to a drawer < lg)
18
+ *
19
+ * GAP (documented in .design/research/screens.md §6 + ui-kit-surfaces.md §9):
20
+ * @godxjp/ui's `Calendar` is a single-date PICKER, not a month/week event grid.
21
+ * There is no `ShiftCalendar` / scheduling-grid primitive. The month grid and the
22
+ * week time-axis are therefore COMPOSED from real primitives (Card + token-driven
23
+ * CSS grid + Badge), never a hand-rolled faux component. The real Calendar is still
24
+ * used for its genuine job (jumping to a month). See `gapNotes`.
25
+ *
26
+ * DNA applied: compact density, small headings (20/18/14/13), fixed color signaling
27
+ * (success 若竹 / warning 山吹 / info 群青 / attention 朱 / danger 茜), tabular-nums,
28
+ * wa-iro DECORATIVE palette for the 7 shift types (never remapped to a semantic role),
29
+ * quiet factual JP copy, 1px-border cards with no rest shadow, no emoji.
30
+ */
31
+ import * as React from "react";
32
+ import {
33
+ CalendarDays,
34
+ CalendarRange,
35
+ ChevronLeft,
36
+ ChevronRight,
37
+ Clock3,
38
+ LayoutDashboard,
39
+ Plus,
40
+ CalendarClock,
41
+ Users,
42
+ AlertTriangle,
43
+ CalendarCheck,
44
+ } from "lucide-react";
45
+
46
+ import { Button, Text } from "@godxjp/ui/general";
47
+ import {
48
+ Badge,
49
+ Card,
50
+ CardAction,
51
+ CardContent,
52
+ CardHeader,
53
+ CardTitle,
54
+ EmptyState,
55
+ Popover,
56
+ PopoverContent,
57
+ PopoverTrigger,
58
+ Timeline,
59
+ type TimelineItem,
60
+ } from "@godxjp/ui/data-display";
61
+ import { Calendar, ToggleGroup, ToggleGroupItem } from "@godxjp/ui/data-entry";
62
+ import {
63
+ Sheet,
64
+ SheetBody,
65
+ SheetContent,
66
+ SheetDescription,
67
+ SheetHeader,
68
+ SheetTitle,
69
+ } from "@godxjp/ui/feedback";
70
+ import {
71
+ AppShell,
72
+ Flex,
73
+ PageContainer,
74
+ ResponsiveGrid,
75
+ Sidebar,
76
+ type SidebarSectionProp,
77
+ } from "@godxjp/ui/layout";
78
+
79
+ // ── Shift-type palette ────────────────────────────────────────────────────────
80
+ // The 7-color shift palette uses wa-iro DECORATIVE tokens (charts/tags/tenant) —
81
+ // never a semantic role. Each pill carries a 2px left accent border + soft tint,
82
+ // exactly as `comp-calendar.html` (.sh) specified.
83
+ type ShiftKind = "early" | "late" | "night" | "tsushi" | "ot" | "leave" | "holiday";
84
+
85
+ const SHIFT_META: Record<
86
+ ShiftKind,
87
+ { label: string; time: string; cssVar: string; muted?: boolean }
88
+ > = {
89
+ early: { label: "早番", time: "09:00–17:30", cssVar: "--primary" },
90
+ late: { label: "遅番", time: "13:00–22:00", cssVar: "--wa-gunjo" },
91
+ night: { label: "夜勤", time: "22:00–06:00", cssVar: "--wa-kon" },
92
+ tsushi: { label: "通し", time: "09:00–22:00", cssVar: "--wa-wakatake" },
93
+ ot: { label: "残業", time: "OT", cssVar: "--wa-shu" },
94
+ leave: { label: "休 / 有給", time: "終日", cssVar: "--wa-nezu", muted: true },
95
+ holiday: { label: "祝日", time: "終日", cssVar: "--wa-akane" },
96
+ };
97
+
98
+ /** A shift pill — soft tint + 2px left accent, from a decorative wa-iro token. */
99
+ function ShiftPill({
100
+ kind,
101
+ staff,
102
+ className,
103
+ }: {
104
+ kind: ShiftKind;
105
+ staff?: string;
106
+ className?: string;
107
+ }) {
108
+ const meta = SHIFT_META[kind];
109
+ return (
110
+ <span
111
+ data-shift={kind}
112
+ className={`flex items-center gap-1 truncate rounded-sm border-s-2 px-1.5 py-0.5 leading-tight ${className ?? ""}`}
113
+ style={{
114
+ borderLeftColor: `var(${meta.cssVar})`,
115
+ background: `color-mix(in oklch, var(${meta.cssVar}) 12%, transparent)`,
116
+ color: meta.muted ? "var(--muted-foreground)" : "var(--foreground)",
117
+ }}
118
+ title={`${meta.label} ${meta.time}${staff ? ` · ${staff}` : ""}`}
119
+ >
120
+ <Text as="span" size="2xs" weight="medium" style={{ color: "inherit" }}>
121
+ {meta.label}
122
+ </Text>
123
+ {staff ? (
124
+ <Text as="span" size="2xs" tone="muted" truncate>
125
+ {staff}
126
+ </Text>
127
+ ) : null}
128
+ </span>
129
+ );
130
+ }
131
+
132
+ // ── Month data (2026年5月, 6-week grid = 42 cells) ─────────────────────────────
133
+ type DayCell = {
134
+ date: number; // 1..31, or 0 for padding
135
+ dim?: boolean; // other-month
136
+ today?: boolean;
137
+ weekday: number; // 0=Sun .. 6=Sat
138
+ holiday?: string; // holiday name → 祝日 pill
139
+ shifts: Array<{ kind: ShiftKind; staff: string }>;
140
+ };
141
+
142
+ // May 2026 starts on a Friday (weekday index 5).
143
+ const MAY_HOLIDAYS: Record<number, string> = {
144
+ 3: "憲法記念日",
145
+ 4: "みどりの日",
146
+ 5: "こどもの日",
147
+ };
148
+
149
+ // A handful of representative shift assignments across the month.
150
+ const SHIFTS_BY_DATE: Record<number, Array<{ kind: ShiftKind; staff: string }>> = {
151
+ 1: [
152
+ { kind: "early", staff: "田中" },
153
+ { kind: "late", staff: "佐藤" },
154
+ ],
155
+ 2: [{ kind: "tsushi", staff: "鈴木" }],
156
+ 7: [
157
+ { kind: "early", staff: "田中" },
158
+ { kind: "late", staff: "高橋" },
159
+ { kind: "night", staff: "伊藤" },
160
+ ],
161
+ 8: [
162
+ { kind: "early", staff: "佐藤" },
163
+ { kind: "ot", staff: "田中" },
164
+ ],
165
+ 12: [{ kind: "leave", staff: "鈴木" }],
166
+ 14: [
167
+ { kind: "early", staff: "田中" },
168
+ { kind: "late", staff: "高橋" },
169
+ { kind: "tsushi", staff: "佐藤" },
170
+ { kind: "night", staff: "伊藤" },
171
+ ],
172
+ 15: [
173
+ { kind: "early", staff: "佐藤" },
174
+ { kind: "late", staff: "鈴木" },
175
+ ],
176
+ 20: [{ kind: "tsushi", staff: "高橋" }],
177
+ 22: [
178
+ { kind: "early", staff: "田中" },
179
+ { kind: "ot", staff: "佐藤" },
180
+ ],
181
+ 28: [{ kind: "night", staff: "伊藤" }],
182
+ };
183
+
184
+ const TODAY = 14; // 2026-05-14, the "today" / now-line anchor.
185
+
186
+ function buildMonth(): DayCell[] {
187
+ const startPad = 5; // May 1, 2026 = Friday
188
+ const daysInMonth = 31;
189
+ const cells: DayCell[] = [];
190
+ // Leading padding (late April), dim.
191
+ for (let i = 0; i < startPad; i++) {
192
+ const d = 27 + i; // 27..30 Apr (4 cells) + 1 spillover handled by length
193
+ cells.push({ date: d, dim: true, weekday: i, shifts: [] });
194
+ }
195
+ for (let d = 1; d <= daysInMonth; d++) {
196
+ const weekday = (startPad + d - 1) % 7;
197
+ cells.push({
198
+ date: d,
199
+ today: d === TODAY,
200
+ weekday,
201
+ holiday: MAY_HOLIDAYS[d],
202
+ shifts: SHIFTS_BY_DATE[d] ?? [],
203
+ });
204
+ }
205
+ // Trailing padding to fill the last week (early June), dim.
206
+ let next = 1;
207
+ while (cells.length % 7 !== 0) {
208
+ cells.push({
209
+ date: next,
210
+ dim: true,
211
+ weekday: cells.length % 7,
212
+ shifts: [],
213
+ });
214
+ next++;
215
+ }
216
+ return cells;
217
+ }
218
+
219
+ const MONTH_CELLS = buildMonth();
220
+ const WEEKDAY_HEAD = ["日", "月", "火", "水", "木", "金", "土"];
221
+
222
+ // ── Week view (2026-05-11 〜 05-17), time axis 06:00–22:00 ─────────────────────
223
+ const WEEK_DATES = [
224
+ { date: 11, weekday: 1 },
225
+ { date: 12, weekday: 2 },
226
+ { date: 13, weekday: 3 },
227
+ { date: 14, weekday: 4, today: true },
228
+ { date: 15, weekday: 5 },
229
+ { date: 16, weekday: 6 },
230
+ { date: 17, weekday: 0 },
231
+ ];
232
+ const HOURS = [6, 8, 10, 12, 14, 16, 18, 20, 22];
233
+
234
+ type WeekBlock = {
235
+ col: number; // 0..6 day index
236
+ kind: ShiftKind;
237
+ staff: string;
238
+ startHour: number;
239
+ endHour: number; // for night/通し may exceed 24 → clamp display
240
+ };
241
+ const WEEK_BLOCKS: WeekBlock[] = [
242
+ { col: 3, kind: "early", staff: "田中", startHour: 9, endHour: 17.5 },
243
+ { col: 3, kind: "late", staff: "高橋", startHour: 13, endHour: 22 },
244
+ { col: 3, kind: "night", staff: "伊藤", startHour: 22, endHour: 30 },
245
+ { col: 0, kind: "tsushi", staff: "佐藤", startHour: 9, endHour: 22 },
246
+ { col: 4, kind: "early", staff: "佐藤", startHour: 9, endHour: 17.5 },
247
+ { col: 4, kind: "ot", staff: "鈴木", startHour: 18, endHour: 21 },
248
+ { col: 1, kind: "leave", staff: "鈴木", startHour: 6, endHour: 22 },
249
+ ];
250
+
251
+ const AXIS_START = 6;
252
+ const AXIS_END = 22; // display window; night spills are clamped + noted
253
+ const NOW_HOUR = 14 + 35 / 60; // 14:35 now-line
254
+
255
+ function hourToPct(h: number): number {
256
+ const clamped = Math.max(AXIS_START, Math.min(AXIS_END, h));
257
+ return ((clamped - AXIS_START) / (AXIS_END - AXIS_START)) * 100;
258
+ }
259
+
260
+ // ── Day view (2026-05-14) — per-staff lanes via Timeline + understaffed gap ───
261
+ const DAY_LANES: Array<{ staff: string; role: string; items: TimelineItem[] }> = [
262
+ {
263
+ staff: "田中 美咲",
264
+ role: "店長",
265
+ items: [
266
+ { title: "早番 · 09:00 開始", time: "09:00", current: true },
267
+ { title: "休憩 12:00–13:00", time: "12:00" },
268
+ { title: "早番 · 17:30 終了", time: "17:30" },
269
+ ],
270
+ },
271
+ {
272
+ staff: "高橋 健",
273
+ role: "スタッフ",
274
+ items: [
275
+ { title: "遅番 · 13:00 開始", time: "13:00" },
276
+ { title: "休憩 17:00–17:45", time: "17:00" },
277
+ { title: "遅番 · 22:00 終了", time: "22:00" },
278
+ ],
279
+ },
280
+ {
281
+ staff: "伊藤 翔",
282
+ role: "スタッフ",
283
+ items: [
284
+ { title: "夜勤 · 22:00 開始", time: "22:00", current: true },
285
+ { title: "夜勤 · 翌 06:00 終了", time: "06:00" },
286
+ ],
287
+ },
288
+ ];
289
+
290
+ // ── Sidebar ───────────────────────────────────────────────────────────────────
291
+ const NAV_SECTIONS: SidebarSectionProp[] = [
292
+ {
293
+ label: "シフト",
294
+ items: [
295
+ { id: "calendar", label: "シフトカレンダー", icon: CalendarRange },
296
+ { id: "shifts", label: "従業員シフト", icon: Users, badge: 5 },
297
+ { id: "requests", label: "変更申請", icon: CalendarClock, badge: 3 },
298
+ ],
299
+ },
300
+ {
301
+ label: "勤怠",
302
+ items: [
303
+ { id: "dashboard", label: "ダッシュボード", icon: LayoutDashboard },
304
+ { id: "logs", label: "打刻ログ", icon: Clock3 },
305
+ ],
306
+ },
307
+ ];
308
+
309
+ type ViewMode = "month" | "week" | "day";
310
+
311
+ export default function ShiftCalendarShowcase() {
312
+ const [activeNav, setActiveNav] = React.useState("calendar");
313
+ const [view, setView] = React.useState<ViewMode>("month");
314
+ const [jumpMonth, setJumpMonth] = React.useState<Date | undefined>(new Date(2026, 4, 14));
315
+ const [pickerOpen, setPickerOpen] = React.useState(false);
316
+ // Master-detail: tapping a day opens a Sheet (works at any width; on lg the
317
+ // grid is the primary surface and the Sheet is an on-demand detail).
318
+ const [detailDate, setDetailDate] = React.useState<DayCell | null>(null);
319
+
320
+ const sidebar = (
321
+ <Sidebar
322
+ activeId={activeNav}
323
+ onSelect={setActiveNav}
324
+ sections={NAV_SECTIONS}
325
+ product={{ name: "famgia", role: "全店舗 · 5月度" }}
326
+ />
327
+ );
328
+
329
+ return (
330
+ <AppShell
331
+ sidebar={sidebar}
332
+ topbarLeft={<Text as="strong">シフト管理</Text>}
333
+ topbarRight={
334
+ <Text size="xs" tone="muted" tabular className="whitespace-nowrap">
335
+ famgia · 渋谷店
336
+ </Text>
337
+ }
338
+ >
339
+ <PageContainer
340
+ title="シフトカレンダー"
341
+ subtitle="2026年5月 · 渋谷店"
342
+ density="compact"
343
+ extra={
344
+ <Flex direction="row" gap="sm" className="flex-wrap items-center">
345
+ {/* Month nav segment */}
346
+ <Flex direction="row" gap="xs" className="items-center">
347
+ <Button variant="outline" size="sm" aria-label="前の月">
348
+ <ChevronLeft aria-hidden="true" />
349
+ </Button>
350
+ <Popover open={pickerOpen} onOpenChange={setPickerOpen}>
351
+ <PopoverTrigger asChild>
352
+ <Button variant="outline" size="sm" className="whitespace-nowrap tabular-nums">
353
+ <CalendarDays aria-hidden="true" />
354
+ 2026年5月
355
+ </Button>
356
+ </PopoverTrigger>
357
+ <PopoverContent className="w-auto" style={{ padding: 0 }} align="start">
358
+ <Calendar
359
+ mode="single"
360
+ selected={jumpMonth}
361
+ onSelect={(d) => {
362
+ setJumpMonth(d);
363
+ setPickerOpen(false);
364
+ }}
365
+ />
366
+ </PopoverContent>
367
+ </Popover>
368
+ <Button variant="outline" size="sm" aria-label="次の月">
369
+ <ChevronRight aria-hidden="true" />
370
+ </Button>
371
+ <Button variant="ghost" size="sm" className="whitespace-nowrap">
372
+ 今日
373
+ </Button>
374
+ </Flex>
375
+ {/* 月/週/日 segmented view switch */}
376
+ <ToggleGroup
377
+ type="single"
378
+ value={view}
379
+ onValueChange={(v) => {
380
+ if (v) setView(v as ViewMode);
381
+ }}
382
+ aria-label="表示単位"
383
+ >
384
+ <ToggleGroupItem value="day">日</ToggleGroupItem>
385
+ <ToggleGroupItem value="week">週</ToggleGroupItem>
386
+ <ToggleGroupItem value="month">月</ToggleGroupItem>
387
+ </ToggleGroup>
388
+ <Button size="sm" className="whitespace-nowrap">
389
+ <Plus aria-hidden="true" />
390
+ シフト追加
391
+ </Button>
392
+ </Flex>
393
+ }
394
+ >
395
+ <Flex direction="col" gap="lg">
396
+ {/* ── Shift-type legend (the 7-color wa-iro palette) ── */}
397
+ <Card>
398
+ <CardHeader>
399
+ <CardTitle level={2}>シフト区分</CardTitle>
400
+ <CardAction>
401
+ <Text size="xs" tone="muted" className="whitespace-nowrap">
402
+ 和色(装飾用)
403
+ </Text>
404
+ </CardAction>
405
+ </CardHeader>
406
+ <CardContent>
407
+ <Flex direction="row" gap="sm" wrap>
408
+ {(Object.keys(SHIFT_META) as ShiftKind[]).map((k) => (
409
+ <span
410
+ key={k}
411
+ className="inline-flex items-center gap-2 rounded-sm border px-2 py-1 text-xs"
412
+ >
413
+ <span
414
+ aria-hidden="true"
415
+ className="size-3 shrink-0 rounded-sm"
416
+ style={{ background: `var(${SHIFT_META[k].cssVar})` }}
417
+ />
418
+ <Text size="xs" weight="medium" className="whitespace-nowrap">
419
+ {SHIFT_META[k].label}
420
+ </Text>
421
+ <Text size="xs" tone="muted" tabular className="whitespace-nowrap">
422
+ {SHIFT_META[k].time}
423
+ </Text>
424
+ </span>
425
+ ))}
426
+ </Flex>
427
+ </CardContent>
428
+ </Card>
429
+
430
+ {/* ── MONTH view ── */}
431
+ {view === "month" && <MonthGrid onPick={setDetailDate} />}
432
+
433
+ {/* ── WEEK view ── */}
434
+ {view === "week" && <WeekTimeline />}
435
+
436
+ {/* ── DAY view ── */}
437
+ {view === "day" && <DayLanes />}
438
+ </Flex>
439
+ </PageContainer>
440
+
441
+ {/* Master-detail: day detail as a Sheet (drawer) */}
442
+ <Sheet open={detailDate !== null} onOpenChange={(o) => !o && setDetailDate(null)}>
443
+ <SheetContent side="right" className="w-full sm:max-w-md">
444
+ <SheetHeader>
445
+ <SheetTitle className="tabular-nums">
446
+ {detailDate ? `2026年5月${detailDate.date}日` : ""}
447
+ {detailDate ? ` (${WEEKDAY_HEAD[detailDate.weekday]})` : ""}
448
+ </SheetTitle>
449
+ <SheetDescription>
450
+ {detailDate?.holiday ? `祝日 · ${detailDate.holiday}` : "この日のシフト割り当て"}
451
+ </SheetDescription>
452
+ </SheetHeader>
453
+ <SheetBody>
454
+ {detailDate && detailDate.shifts.length > 0 ? (
455
+ <Flex direction="col" gap="sm">
456
+ {detailDate.shifts.map((s, i) => (
457
+ <Flex
458
+ key={`${s.kind}-${i}`}
459
+ align="center"
460
+ justify="between"
461
+ gap="sm"
462
+ className="rounded-lg border px-3 py-2"
463
+ >
464
+ <ShiftPill kind={s.kind} staff={s.staff} />
465
+ <Text size="xs" tone="muted" tabular className="whitespace-nowrap">
466
+ {SHIFT_META[s.kind].time}
467
+ </Text>
468
+ </Flex>
469
+ ))}
470
+ </Flex>
471
+ ) : (
472
+ <EmptyState
473
+ icon={CalendarCheck}
474
+ title="シフトなし"
475
+ description="この日に割り当てられたシフトはありません。"
476
+ />
477
+ )}
478
+ </SheetBody>
479
+ </SheetContent>
480
+ </Sheet>
481
+ </AppShell>
482
+ );
483
+ }
484
+
485
+ // ── Month grid — composed from primitives (GAP: no event-calendar primitive) ──
486
+ function MonthGrid({ onPick }: { onPick: (d: DayCell) => void }) {
487
+ return (
488
+ <Card>
489
+ <CardHeader>
490
+ <CardTitle level={2}>月</CardTitle>
491
+ <CardAction>
492
+ <Text size="xs" tone="muted" tabular className="whitespace-nowrap">
493
+ 6週間 · 31日
494
+ </Text>
495
+ </CardAction>
496
+ </CardHeader>
497
+ <CardContent flush>
498
+ <div className="overflow-x-auto">
499
+ <div className="min-w-[720px]">
500
+ {/* Weekday head — Sun→danger, Sat→info */}
501
+ <ResponsiveGrid columns={{ sm: 7, md: 7, lg: 7 }} className="bg-secondary border-b">
502
+ {WEEKDAY_HEAD.map((w, i) => (
503
+ <Text
504
+ as="div"
505
+ key={w}
506
+ size="2xs"
507
+ weight="medium"
508
+ align="center"
509
+ className="px-2 py-1.5"
510
+ style={{
511
+ color:
512
+ i === 0
513
+ ? "var(--destructive)"
514
+ : i === 6
515
+ ? "var(--info)"
516
+ : "var(--muted-foreground)",
517
+ }}
518
+ >
519
+ {w}
520
+ </Text>
521
+ ))}
522
+ </ResponsiveGrid>
523
+ {/* 42 cells */}
524
+ <ResponsiveGrid columns={{ sm: 7, md: 7, lg: 7 }}>
525
+ {MONTH_CELLS.map((cell, idx) => {
526
+ const isSun = cell.weekday === 0;
527
+ const isSat = cell.weekday === 6;
528
+ const shown = cell.shifts.slice(0, 3);
529
+ const overflow = cell.shifts.length - shown.length;
530
+ return (
531
+ <button
532
+ key={idx}
533
+ type="button"
534
+ onClick={() => onPick(cell)}
535
+ className="hover:bg-accent focus-visible:ring-ring flex min-h-[96px] flex-col gap-1 border-e border-b p-1.5 text-start transition-colors focus:outline-none focus-visible:ring-2 [&:nth-child(7n)]:border-e-0"
536
+ style={{
537
+ background: cell.today
538
+ ? "color-mix(in oklch, var(--primary) 5%, transparent)"
539
+ : cell.dim
540
+ ? "color-mix(in oklch, var(--secondary) 40%, transparent)"
541
+ : undefined,
542
+ outline: cell.today ? "2px solid var(--primary)" : undefined,
543
+ outlineOffset: cell.today ? "-2px" : undefined,
544
+ opacity: cell.dim ? 0.5 : 1,
545
+ }}
546
+ >
547
+ <Flex align="center" justify="between">
548
+ <Text
549
+ as="span"
550
+ size="sm"
551
+ weight="medium"
552
+ tabular
553
+ style={{
554
+ color: cell.holiday
555
+ ? "var(--destructive)"
556
+ : isSun
557
+ ? "var(--destructive)"
558
+ : isSat
559
+ ? "var(--info)"
560
+ : "var(--foreground)",
561
+ }}
562
+ >
563
+ {cell.date}
564
+ </Text>
565
+ {cell.holiday ? (
566
+ <Badge
567
+ tone="destructive"
568
+ variant="outline"
569
+ className="px-1 py-0 text-[var(--font-size-2xs)]"
570
+ >
571
+
572
+ </Badge>
573
+ ) : null}
574
+ </Flex>
575
+ <Flex direction="col" gap="xs">
576
+ {cell.holiday ? <ShiftPill kind="holiday" staff={cell.holiday} /> : null}
577
+ {shown.map((s, i) => (
578
+ <ShiftPill key={`${s.kind}-${i}`} kind={s.kind} staff={s.staff} />
579
+ ))}
580
+ {overflow > 0 ? (
581
+ <Text as="span" size="2xs" tone="muted" tabular>
582
+ +{overflow} 件
583
+ </Text>
584
+ ) : null}
585
+ </Flex>
586
+ </button>
587
+ );
588
+ })}
589
+ </ResponsiveGrid>
590
+ </div>
591
+ </div>
592
+ </CardContent>
593
+ </Card>
594
+ );
595
+ }
596
+
597
+ // ── Week time-axis with a now-line (GAP: no time-grid primitive) ──────────────
598
+ function WeekTimeline() {
599
+ return (
600
+ <Card>
601
+ <CardHeader>
602
+ <CardTitle level={2}>週</CardTitle>
603
+ <CardAction>
604
+ <Text size="xs" tone="muted" tabular className="whitespace-nowrap">
605
+ 5月11日〜17日 · 06:00–22:00
606
+ </Text>
607
+ </CardAction>
608
+ </CardHeader>
609
+ <CardContent flush>
610
+ <div className="overflow-x-auto">
611
+ <div className="min-w-[760px]">
612
+ {/* Day header row */}
613
+ <ResponsiveGrid
614
+ columns={{ sm: 1, md: 1, lg: 1 }}
615
+ className="bg-secondary grid-cols-[48px_repeat(7,1fr)] border-b"
616
+ >
617
+ <div aria-hidden="true" />
618
+ {WEEK_DATES.map((d) => (
619
+ <Text
620
+ as="div"
621
+ key={d.date}
622
+ size="2xs"
623
+ weight="medium"
624
+ tabular
625
+ align="center"
626
+ className="px-2 py-1.5"
627
+ style={{
628
+ color:
629
+ d.weekday === 0
630
+ ? "var(--destructive)"
631
+ : d.weekday === 6
632
+ ? "var(--info)"
633
+ : "var(--muted-foreground)",
634
+ background:
635
+ "today" in d && d.today
636
+ ? "color-mix(in oklch, var(--primary) 6%, transparent)"
637
+ : undefined,
638
+ }}
639
+ >
640
+ {WEEKDAY_HEAD[d.weekday]} {d.date}
641
+ </Text>
642
+ ))}
643
+ </ResponsiveGrid>
644
+ {/* Time grid body */}
645
+ <ResponsiveGrid
646
+ columns={{ sm: 1, md: 1, lg: 1 }}
647
+ className="relative grid-cols-[48px_repeat(7,1fr)]"
648
+ >
649
+ {/* Hour axis labels */}
650
+ <div className="relative" style={{ height: `${(AXIS_END - AXIS_START) * 22}px` }}>
651
+ {HOURS.map((h) => (
652
+ <Text
653
+ as="div"
654
+ key={h}
655
+ size="2xs"
656
+ tone="muted"
657
+ tabular
658
+ className="absolute end-1.5 -translate-y-1/2"
659
+ style={{ top: `${hourToPct(h)}%` }}
660
+ >
661
+ {String(h).padStart(2, "0")}:00
662
+ </Text>
663
+ ))}
664
+ </div>
665
+ {/* 7 day columns */}
666
+ {WEEK_DATES.map((d, col) => (
667
+ <div
668
+ key={d.date}
669
+ className="relative border-s"
670
+ style={{ height: `${(AXIS_END - AXIS_START) * 22}px` }}
671
+ >
672
+ {/* hour gridlines */}
673
+ {HOURS.map((h) => (
674
+ <div
675
+ key={h}
676
+ aria-hidden="true"
677
+ className="border-border/60 absolute inset-x-0 border-t"
678
+ style={{ top: `${hourToPct(h)}%` }}
679
+ />
680
+ ))}
681
+ {/* shift blocks for this column */}
682
+ {WEEK_BLOCKS.filter((b) => b.col === col).map((b, i) => {
683
+ const top = hourToPct(b.startHour);
684
+ const bottom = hourToPct(b.endHour);
685
+ const meta = SHIFT_META[b.kind];
686
+ const spills = b.endHour > AXIS_END;
687
+ return (
688
+ <div
689
+ key={`${b.kind}-${i}`}
690
+ className="absolute inset-x-0.5 overflow-hidden rounded-sm border-s-2 px-1 py-0.5 leading-tight"
691
+ style={{
692
+ top: `${top}%`,
693
+ height: `${Math.max(bottom - top, 4)}%`,
694
+ borderLeftColor: `var(${meta.cssVar})`,
695
+ background: `color-mix(in oklch, var(${meta.cssVar}) 14%, transparent)`,
696
+ color: meta.muted ? "var(--muted-foreground)" : "var(--foreground)",
697
+ }}
698
+ title={`${meta.label} ${meta.time} · ${b.staff}`}
699
+ >
700
+ <Text
701
+ as="div"
702
+ size="2xs"
703
+ weight="medium"
704
+ truncate
705
+ style={{ color: "inherit" }}
706
+ >
707
+ {meta.label}
708
+ </Text>
709
+ <Text as="div" size="2xs" tone="muted" truncate>
710
+ {b.staff}
711
+ </Text>
712
+ {spills ? (
713
+ <Text as="div" size="2xs" tone="muted" tabular>
714
+ 翌日へ ↓
715
+ </Text>
716
+ ) : null}
717
+ </div>
718
+ );
719
+ })}
720
+ {/* now-line — only on "today" (col 3), 14:35 */}
721
+ {"today" in d && d.today ? (
722
+ <div
723
+ aria-label="現在時刻 14:35"
724
+ className="pointer-events-none absolute inset-x-0 z-10"
725
+ style={{ top: `${hourToPct(NOW_HOUR)}%` }}
726
+ >
727
+ <div className="h-px" style={{ background: "var(--destructive)" }} />
728
+ <div
729
+ className="absolute -start-1 -top-1 size-2 rounded-full"
730
+ style={{ background: "var(--destructive)" }}
731
+ />
732
+ </div>
733
+ ) : null}
734
+ </div>
735
+ ))}
736
+ </ResponsiveGrid>
737
+ </div>
738
+ </div>
739
+ </CardContent>
740
+ </Card>
741
+ );
742
+ }
743
+
744
+ // ── Day view — per-staff lanes (Timeline) + understaffed EmptyState ───────────
745
+ function DayLanes() {
746
+ return (
747
+ <Flex direction="col" gap="md">
748
+ <ResponsiveGrid columns={{ sm: 1, lg: 3, md: 1 }} gap="md">
749
+ {DAY_LANES.map((lane) => (
750
+ <Card key={lane.staff} className="self-start">
751
+ <CardHeader>
752
+ <CardTitle level={2} className="truncate">
753
+ {lane.staff}
754
+ </CardTitle>
755
+ <CardAction>
756
+ <Badge tone="neutral" variant="outline" className="whitespace-nowrap">
757
+ {lane.role}
758
+ </Badge>
759
+ </CardAction>
760
+ </CardHeader>
761
+ <CardContent>
762
+ <Timeline items={lane.items} />
763
+ </CardContent>
764
+ </Card>
765
+ ))}
766
+ {/* Understaffed gap — a calm attention EmptyState, not a dead grey box */}
767
+ <Card className="self-start border-dashed" style={{ borderColor: "var(--attention)" }}>
768
+ <CardHeader>
769
+ <CardTitle level={2} className="truncate whitespace-nowrap">
770
+ 夜帯 22:00–06:00
771
+ </CardTitle>
772
+ <CardAction>
773
+ <Badge tone="warning" variant="outline" className="whitespace-nowrap">
774
+ 人員不足
775
+ </Badge>
776
+ </CardAction>
777
+ </CardHeader>
778
+ <CardContent>
779
+ <EmptyState
780
+ icon={AlertTriangle}
781
+ title="募集中"
782
+ description="この時間帯はあと 1 名必要です。シフトを追加してください。"
783
+ action={
784
+ <Button variant="outline" size="sm">
785
+ <Plus aria-hidden="true" />
786
+ 募集する
787
+ </Button>
788
+ }
789
+ />
790
+ </CardContent>
791
+ </Card>
792
+ </ResponsiveGrid>
793
+ </Flex>
794
+ );
795
+ }