@viax.io/uxm 4.36.0 → 4.37.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 (133) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +74 -31
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.ts +3 -3
  5. package/dist/index.js.map +1 -1
  6. package/dist/previews/index.cjs +3 -0
  7. package/dist/previews/index.cjs.map +1 -1
  8. package/dist/previews/index.d.ts +3 -2
  9. package/dist/previews/index.d.ts.map +1 -1
  10. package/dist/previews/index.js +2 -0
  11. package/dist/previews/index.js.map +1 -1
  12. package/dist/studio/index.cjs.map +1 -1
  13. package/dist/studio/index.d.ts +2 -2
  14. package/dist/studio/index.js.map +1 -1
  15. package/dist/studio/lib/registry/app.cjs +52 -0
  16. package/dist/studio/lib/registry/app.cjs.map +1 -1
  17. package/dist/studio/lib/registry/app.d.ts.map +1 -1
  18. package/dist/studio/lib/registry/app.js +52 -0
  19. package/dist/studio/lib/registry/app.js.map +1 -1
  20. package/dist/studio/lib/registry/buttons.cjs +2 -2
  21. package/dist/studio/lib/registry/buttons.cjs.map +1 -1
  22. package/dist/studio/lib/registry/buttons.js +2 -2
  23. package/dist/studio/lib/registry/buttons.js.map +1 -1
  24. package/dist/studio/lib/registry/composite.cjs +1 -1
  25. package/dist/studio/lib/registry/composite.cjs.map +1 -1
  26. package/dist/studio/lib/registry/composite.js +1 -1
  27. package/dist/studio/lib/registry/composite.js.map +1 -1
  28. package/dist/studio/lib/registry/diagram.cjs +1 -1
  29. package/dist/studio/lib/registry/diagram.cjs.map +1 -1
  30. package/dist/studio/lib/registry/diagram.js +1 -1
  31. package/dist/studio/lib/registry/diagram.js.map +1 -1
  32. package/dist/studio/lib/registry/display.cjs +2 -2
  33. package/dist/studio/lib/registry/display.cjs.map +1 -1
  34. package/dist/studio/lib/registry/display.js +2 -2
  35. package/dist/studio/lib/registry/display.js.map +1 -1
  36. package/dist/studio/lib/registry/feedback.cjs +3 -3
  37. package/dist/studio/lib/registry/feedback.cjs.map +1 -1
  38. package/dist/studio/lib/registry/feedback.js +3 -3
  39. package/dist/studio/lib/registry/feedback.js.map +1 -1
  40. package/dist/studio/lib/registry/inputs.cjs +12 -12
  41. package/dist/studio/lib/registry/inputs.cjs.map +1 -1
  42. package/dist/studio/lib/registry/inputs.js +12 -12
  43. package/dist/studio/lib/registry/inputs.js.map +1 -1
  44. package/dist/studio/lib/types.cjs.map +1 -1
  45. package/dist/studio/persistence/generate-css.cjs +15 -0
  46. package/dist/studio/persistence/generate-css.cjs.map +1 -1
  47. package/dist/studio/persistence/generate-css.d.ts.map +1 -1
  48. package/dist/studio/persistence/generate-css.js +15 -0
  49. package/dist/studio/persistence/generate-css.js.map +1 -1
  50. package/dist/studio/shell/canvas.cjs +1 -0
  51. package/dist/studio/shell/canvas.cjs.map +1 -1
  52. package/dist/studio/shell/canvas.d.ts.map +1 -1
  53. package/dist/studio/shell/canvas.js +2 -0
  54. package/dist/studio/shell/canvas.js.map +1 -1
  55. package/dist/ui/calendar/calendar.cjs +3 -1
  56. package/dist/ui/calendar/calendar.cjs.map +1 -1
  57. package/dist/ui/calendar/calendar.d.ts +6 -2
  58. package/dist/ui/calendar/calendar.d.ts.map +1 -1
  59. package/dist/ui/calendar/calendar.js +3 -1
  60. package/dist/ui/calendar/calendar.js.map +1 -1
  61. package/dist/ui/currency-input/currency-input.cjs +3 -1
  62. package/dist/ui/currency-input/currency-input.cjs.map +1 -1
  63. package/dist/ui/currency-input/currency-input.d.ts +4 -2
  64. package/dist/ui/currency-input/currency-input.d.ts.map +1 -1
  65. package/dist/ui/currency-input/currency-input.js +3 -1
  66. package/dist/ui/currency-input/currency-input.js.map +1 -1
  67. package/dist/ui/date-input/date-input.cjs +2 -1
  68. package/dist/ui/date-input/date-input.cjs.map +1 -1
  69. package/dist/ui/date-input/date-input.d.ts +11 -1
  70. package/dist/ui/date-input/date-input.d.ts.map +1 -1
  71. package/dist/ui/date-input/date-input.js +2 -1
  72. package/dist/ui/date-input/date-input.js.map +1 -1
  73. package/dist/ui/editable-cell/editable-cell.cjs +20 -6
  74. package/dist/ui/editable-cell/editable-cell.cjs.map +1 -1
  75. package/dist/ui/editable-cell/editable-cell.d.ts +24 -1
  76. package/dist/ui/editable-cell/editable-cell.d.ts.map +1 -1
  77. package/dist/ui/editable-cell/editable-cell.js +20 -6
  78. package/dist/ui/editable-cell/editable-cell.js.map +1 -1
  79. package/dist/ui/file-upload/file-upload.cjs +36 -9
  80. package/dist/ui/file-upload/file-upload.cjs.map +1 -1
  81. package/dist/ui/file-upload/file-upload.d.ts +46 -4
  82. package/dist/ui/file-upload/file-upload.d.ts.map +1 -1
  83. package/dist/ui/file-upload/file-upload.js +36 -9
  84. package/dist/ui/file-upload/file-upload.js.map +1 -1
  85. package/dist/ui/index.cjs +12 -2
  86. package/dist/ui/index.cjs.map +1 -1
  87. package/dist/ui/index.d.ts +5 -1
  88. package/dist/ui/index.d.ts.map +1 -1
  89. package/dist/ui/index.js +7 -1
  90. package/dist/ui/index.js.map +1 -1
  91. package/dist/ui/language-switcher/index.cjs +29 -0
  92. package/dist/ui/language-switcher/index.cjs.map +1 -0
  93. package/dist/ui/language-switcher/index.d.ts +3 -0
  94. package/dist/ui/language-switcher/index.d.ts.map +1 -0
  95. package/dist/ui/language-switcher/index.js +5 -0
  96. package/dist/ui/language-switcher/index.js.map +1 -0
  97. package/dist/ui/language-switcher/language-switcher-preview.cjs +137 -0
  98. package/dist/ui/language-switcher/language-switcher-preview.cjs.map +1 -0
  99. package/dist/ui/language-switcher/language-switcher-preview.d.ts +3 -0
  100. package/dist/ui/language-switcher/language-switcher-preview.d.ts.map +1 -0
  101. package/dist/ui/language-switcher/language-switcher-preview.js +113 -0
  102. package/dist/ui/language-switcher/language-switcher-preview.js.map +1 -0
  103. package/dist/ui/language-switcher/language-switcher.cjs +127 -0
  104. package/dist/ui/language-switcher/language-switcher.cjs.map +1 -0
  105. package/dist/ui/language-switcher/language-switcher.css +95 -0
  106. package/dist/ui/language-switcher/language-switcher.d.ts +73 -0
  107. package/dist/ui/language-switcher/language-switcher.d.ts.map +1 -0
  108. package/dist/ui/language-switcher/language-switcher.js +103 -0
  109. package/dist/ui/language-switcher/language-switcher.js.map +1 -0
  110. package/dist/ui/locale/index.cjs +33 -0
  111. package/dist/ui/locale/index.cjs.map +1 -0
  112. package/dist/ui/locale/index.d.ts +3 -0
  113. package/dist/ui/locale/index.d.ts.map +1 -0
  114. package/dist/ui/locale/index.js +7 -0
  115. package/dist/ui/locale/index.js.map +1 -0
  116. package/dist/ui/locale/locale.cjs +43 -0
  117. package/dist/ui/locale/locale.cjs.map +1 -0
  118. package/dist/ui/locale/locale.d.ts +49 -0
  119. package/dist/ui/locale/locale.d.ts.map +1 -0
  120. package/dist/ui/locale/locale.js +17 -0
  121. package/dist/ui/locale/locale.js.map +1 -0
  122. package/dist/ui/page-shell/page-shell.cjs.map +1 -1
  123. package/dist/ui/page-shell/page-shell.d.ts +1 -1
  124. package/dist/ui/page-shell/page-shell.d.ts.map +1 -1
  125. package/dist/ui/page-shell/page-shell.js.map +1 -1
  126. package/dist/ui/range-slider/range-slider.cjs +7 -3
  127. package/dist/ui/range-slider/range-slider.cjs.map +1 -1
  128. package/dist/ui/range-slider/range-slider.d.ts +17 -1
  129. package/dist/ui/range-slider/range-slider.d.ts.map +1 -1
  130. package/dist/ui/range-slider/range-slider.js +7 -3
  131. package/dist/ui/range-slider/range-slider.js.map +1 -1
  132. package/dist/ui/styles.css +2 -1
  133. package/package.json +4 -5
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/studio/shell/canvas.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\n\n\nimport {\n BannerPreview,\n BulkActionBarPreview,\n AppSidebarPreview,\n AppTopBarPreview,\n AvatarPreview,\n BackLinkPreview,\n BadgePreview,\n BrandSettingsPreview,\n BreadcrumbPreview,\n ButtonGroupPreview,\n ButtonPreview,\n ButtonWithIconPreview,\n CalendarPreview,\n CardPreview,\n CheckboxPreview,\n ChipPreview,\n ClusterPreview,\n CodeEditorPreview,\n ColorInputPreview,\n ComponentRowPreview,\n ConfigComponentRowPreview,\n ConfigSegmentItemPreview,\n ContentTooltipPreview,\n CurrencyInputPreview,\n DataTablePreview,\n DateInputPreview,\n DetailSectionPreview,\n DisclosurePreview,\n DividerPreview,\n EditableCellPreview,\n EmptyStatePreview,\n ErrorPagePreview,\n ExplorerListItemPreview,\n ExplorerSectionPreview,\n FileUploadPreview,\n FilterTabsPreview,\n FormFieldPreview,\n FormPreview,\n IconButtonPreview,\n IconPreview,\n IconTilePreview,\n InlineActionPreview,\n InlineFilterPreview,\n InputPreview,\n InputWithIconPreview,\n LifecycleActionRowPreview,\n LifecycleConnectorKnobsPreview,\n LifecycleConnectorPreview,\n LifecycleDropSlotPreview,\n LifecycleEdgeLabelPreview,\n LifecycleGroupBoxPreview,\n LifecycleMinimapPreview,\n LifecycleNodeCardPreview,\n LifecyclePlusButtonPreview,\n LifecycleTerminalPreview,\n LifecycleZoomControlPreview,\n LinkPreview,\n ListItemPreview,\n ListboxPreview,\n LoaderPreview,\n LoginPagePreview,\n MenuPreview,\n MetaRowPreview,\n ModalPreview,\n MultirowFormPreview,\n NumberStepperPreview,\n NumberInputPreview,\n OptionListPreview,\n PageHeaderPreview,\n PageShellPreview,\n PasswordInputPreview,\n PhoneInputPreview,\n PillSelectPreview,\n ProgressBarPreview,\n PropertyFieldPreview,\n PropertyGridPreview,\n RadioGroupPreview,\n ResponsiveGridPreview,\n SearchDropdownPreview,\n SectionHeaderPreview,\n SegmentCardPreview,\n SegmentRowPreview,\n SelectPreview,\n SideFlexpanePreview,\n SidebarNavItemPreview,\n SidebarNavTriggerPreview,\n SliderPreview,\n StackPreview,\n StatCardPreview,\n TabsPreview,\n TabsUnderlinePreview,\n TagPreview,\n TextareaPreview,\n ThumbnailPreview,\n TimeInputPreview,\n TimelineEntryPreview,\n ToastPreview,\n TogglePreview,\n TooltipPreview,\n TypeOverviewCardPreview,\n ViewSwitcherPreview,\n} from '@/previews';\nimport { ButtonGhost, ButtonPrimary, IconButton } from '@/ui';\nimport { Icon } from '@/ui';\n\nimport { useUxm } from '../lib/context';\nimport { usePreviewShell } from '../lib/preview-shell';\nimport { getComponentDef } from '../lib/registry';\n\nimport { PreviewModal } from './preview-modal';\n\nimport type { PreviewProps } from '../lib/types';\nimport type { ReactNode } from 'react';\n\n\ntype PreviewComponent = React.ComponentType<PreviewProps>;\n\n/**\n * Text-entry surfaces (typeable inputs + textarea + contenteditable) where\n * `onInput` / React's mapped `onChange` fires per keystroke. Capturing those\n * events into the UXM event log triggers a context re-render on every\n * character, which races the controlled-input flow and silently drops\n * typed values mid-keystroke (Chrome / Safari / Firefox alike — repro'd\n * by typing into TextInput, PasswordInput, PhoneInput, etc. in the\n * canvas while the side-panel inputs work fine because they aren't\n * wrapped in the capture handlers). The fix: skip these surfaces in\n * the high-frequency capture handlers and rely on `onBlur` to log the\n * committed value once the user leaves the field.\n */\nfunction isHighFrequencyTextEntry(el: HTMLElement | null): boolean {\n if (!el) return false;\n if (el.tagName === 'TEXTAREA') return true;\n if ((el as HTMLElement).isContentEditable) return true;\n if (el.tagName === 'INPUT') {\n const type = (el as HTMLInputElement).type;\n return (\n type === 'text' ||\n type === 'tel' ||\n type === 'email' ||\n type === 'password' ||\n type === 'url' ||\n type === 'search' ||\n type === 'number'\n );\n }\n return false;\n}\n\nexport const previewMap: Record<string, PreviewComponent> = {\n 'app-sidebar': AppSidebarPreview,\n 'app-top-bar': AppTopBarPreview,\n 'button-primary': ButtonPreview,\n 'button-secondary': ButtonPreview,\n 'button-tertiary': ButtonPreview,\n 'button-ghost': ButtonPreview,\n 'button-danger': ButtonPreview,\n 'button-icon': ButtonPreview,\n 'button-with-icon': ButtonWithIconPreview,\n 'input-text': InputPreview,\n 'input-with-icon': InputWithIconPreview,\n 'color-input': ColorInputPreview,\n textarea: TextareaPreview,\n 'file-upload': FileUploadPreview,\n 'select-dropdown': SelectPreview,\n checkbox: CheckboxPreview,\n 'toggle-switch': TogglePreview,\n 'radio-group': RadioGroupPreview,\n chip: ChipPreview,\n 'code-editor': CodeEditorPreview,\n tag: TagPreview,\n card: CardPreview,\n avatar: AvatarPreview,\n 'empty-state': EmptyStatePreview,\n banner: BannerPreview,\n 'bulk-action-bar': BulkActionBarPreview,\n toast: ToastPreview,\n tooltip: TooltipPreview,\n 'form-login': FormPreview,\n 'form-contact': FormPreview,\n 'pill-select': PillSelectPreview,\n 'inline-filter': InlineFilterPreview,\n 'content-tooltip': ContentTooltipPreview,\n 'form-multirow': MultirowFormPreview,\n 'data-table': DataTablePreview,\n 'button-group': ButtonGroupPreview,\n 'stat-card': StatCardPreview,\n 'type-overview-card': TypeOverviewCardPreview,\n 'page-shell': PageShellPreview,\n 'sidebar-nav-item': SidebarNavItemPreview,\n 'sidebar-nav-trigger': SidebarNavTriggerPreview,\n 'explorer-list-item': ExplorerListItemPreview,\n 'explorer-section': ExplorerSectionPreview,\n 'inline-action': InlineActionPreview,\n 'number-stepper': NumberStepperPreview,\n 'number-input': NumberInputPreview,\n 'currency-input': CurrencyInputPreview,\n 'search-dropdown': SearchDropdownPreview,\n listbox: ListboxPreview,\n menu: MenuPreview,\n 'editable-cell': EditableCellPreview,\n 'section-header': SectionHeaderPreview,\n slider: SliderPreview,\n 'error-page': ErrorPagePreview,\n 'login-page': LoginPagePreview,\n loader: LoaderPreview,\n 'progress-bar': ProgressBarPreview,\n 'timeline-entry': TimelineEntryPreview,\n 'segment-card': SegmentCardPreview,\n 'segment-row': SegmentRowPreview,\n 'component-row': ComponentRowPreview,\n 'option-list': OptionListPreview,\n 'config-segment-item': ConfigSegmentItemPreview,\n 'config-component-row': ConfigComponentRowPreview,\n 'icon-tile': IconTilePreview,\n 'meta-row': MetaRowPreview,\n icon: IconPreview,\n 'icon-button': IconButtonPreview,\n 'detail-section': DetailSectionPreview,\n 'back-link': BackLinkPreview,\n badge: BadgePreview,\n thumbnail: ThumbnailPreview,\n 'date-input': DateInputPreview,\n 'password-input': PasswordInputPreview,\n 'phone-input': PhoneInputPreview,\n 'time-input': TimeInputPreview,\n calendar: CalendarPreview,\n disclosure: DisclosurePreview,\n 'link': LinkPreview,\n 'breadcrumb': BreadcrumbPreview,\n 'property-field': PropertyFieldPreview,\n 'property-grid': PropertyGridPreview,\n 'form-field': FormFieldPreview,\n 'list-item': ListItemPreview,\n 'side-flexpane': SideFlexpanePreview,\n 'page-header': PageHeaderPreview,\n 'divider': DividerPreview,\n 'filter-tabs': FilterTabsPreview,\n 'view-switcher': ViewSwitcherPreview,\n 'tabs': TabsPreview,\n 'tabs-underline': TabsUnderlinePreview,\n 'brand-settings': BrandSettingsPreview,\n 'lifecycle-node-card': LifecycleNodeCardPreview,\n 'lifecycle-terminal': LifecycleTerminalPreview,\n 'lifecycle-edge-label': LifecycleEdgeLabelPreview,\n 'lifecycle-plus-button': LifecyclePlusButtonPreview,\n 'lifecycle-zoom-control': LifecycleZoomControlPreview,\n 'lifecycle-minimap': LifecycleMinimapPreview,\n 'lifecycle-group-box': LifecycleGroupBoxPreview,\n 'lifecycle-drop-slot': LifecycleDropSlotPreview,\n 'lifecycle-action-row': LifecycleActionRowPreview,\n modal: ModalPreview,\n 'lifecycle-connector': LifecycleConnectorPreview,\n 'lifecycle-connector-knobs': LifecycleConnectorKnobsPreview,\n 'responsive-grid': ResponsiveGridPreview,\n stack: StackPreview,\n cluster: ClusterPreview,\n};\n\nexport function Canvas({\n embed = false,\n headerActions,\n}: {\n embed?: boolean;\n /** Optional host-provided actions rendered at the end of the top-bar\n cluster, right after the Preview & Publish button (e.g. a user avatar). */\n headerActions?: ReactNode;\n}) {\n const [previewOpen, setPreviewOpen] = useState(false);\n const { selectedId, getOverrides, resetOverrides, getCurrentVariants, pushEvent, capabilities, theme, setTheme } = useUxm();\n const shell = usePreviewShell();\n const def = getComponentDef(selectedId);\n const overrides = getOverrides(selectedId);\n const currentVariants = getCurrentVariants();\n\n const resolved = useMemo(() => {\n if (!def) return { styles: {}, variants: {} };\n const styles: Record<string, string | number | boolean> = {};\n for (const prop of def.styleProperties) {\n styles[prop.key] = overrides[prop.key] ?? prop.defaultValue;\n }\n // Variants read from ephemeral session state, NOT from overrides.\n // Variant selections decide which knobs are visible in the panel —\n // they don't persist and never get saved to components.css/json.\n const variants: Record<string, string> = {};\n for (const v of def.layoutVariants) {\n variants[v.key] = (currentVariants[v.key] as string | undefined) ?? v.defaultValue;\n }\n return { styles, variants };\n }, [def, overrides, currentVariants]);\n\n if (!def) return null;\n\n const Preview = previewMap[def.id];\n const hasOverrides = Object.keys(overrides).length > 0;\n\n return (\n <div className=\"flex h-full flex-col bg-card\">\n {/* Toolbar — uses container queries so the layout adapts to the\n canvas's actual width, not the viewport. The canvas can be\n much narrower than the viewport when the sidebar + properties\n pane are open; viewport breakpoints would respond to the\n wrong dimension. Three steps:\n ≥640px full (name + description + labelled buttons)\n 480-640 hide description, keep labels\n <480px icon-only secondary buttons; primary CTA stays\n labelled (Preview & Publish is moment-of-truth). */}\n <div className=\"@container flex items-center justify-between gap-3 border-b border-border px-6 py-4\">\n {/* min-w-0 enables `truncate` on the children — without it the\n flex item ignores the constraint and pushes the actions\n cluster off-screen. */}\n <div className=\"min-w-0 flex-1\">\n <h2 className=\"text-[15px] font-semibold text-text truncate\">{def.name}</h2>\n <p className=\"text-xs text-text-muted mt-0.5 hidden @[640px]:block truncate\">\n {def.description}\n </p>\n </div>\n {/* flex-shrink-0 keeps the actions cluster from wrapping; the\n label spans inside the secondary buttons hide first when\n space gets tight. */}\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n {hasOverrides && (\n <ButtonGhost onClick={() => resetOverrides(selectedId)} title=\"Reset overrides\">\n <Icon glyph=\"refresh\" size={14} />\n <span className=\"hidden @[480px]:inline\">Reset</span>\n </ButtonGhost>\n )}\n {/* Light/dark toggle — only in the standalone portal (the studio\n owns the theme here; ThemeSync mirrors it onto data-theme). An\n embedded host renders its own theme control. */}\n {!embed && (\n <IconButton\n onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}\n aria-label={theme === 'dark' ? 'Switch to light theme' : 'Switch to dark theme'}\n title={theme === 'dark' ? 'Switch to light theme' : 'Switch to dark theme'}\n >\n <Icon glyph={theme === 'dark' ? 'sun' : 'moon'} size={16} />\n </IconButton>\n )}\n <ButtonPrimary onClick={() => setPreviewOpen(true)}>\n <Icon glyph=\"eye\" size={14} />\n {capabilities.persist ? 'Preview & Publish' : 'Preview'}\n </ButtonPrimary>\n {headerActions}\n </div>\n </div>\n\n {/* Preview area with optional dot grid */}\n <div\n className=\"flex-1 overflow-auto p-12\"\n style={def.canvasBackground === false ? undefined : {\n backgroundImage: 'radial-gradient(circle, var(--color-canvas-dot) 1px, transparent 1px)',\n backgroundSize: '20px 20px',\n }}\n >\n <div className={def.canvasFill ? 'min-h-full' : 'min-h-full flex items-center justify-center'}>\n {Preview ? (\n // Wrap the preview in a capture-phase event listener so any\n // native event the rendered atom fires gets logged to the\n // Events tab. Capture (rather than bubble) sees the event\n // before the atom's own handler — so we log first, the atom\n // behaves second. We don't `preventDefault`, so the rendered\n // component still works normally.\n <div\n onClickCapture={(e) =>\n pushEvent({\n name: 'onClick',\n target: (e.target as HTMLElement).tagName.toLowerCase(),\n payload: JSON.stringify({ x: e.clientX, y: e.clientY, button: e.button }),\n })\n }\n onChangeCapture={(e) => {\n const t = e.target as HTMLInputElement;\n // Skip text-entry surfaces here too — React maps onChange\n // to the native `input` event for text inputs / textareas /\n // contenteditable, so this capture would fire on EVERY\n // keystroke. Combined with onInputCapture below, that's\n // two `setEventLog` calls per character, each triggering\n // a context re-render that races the controlled-input\n // flow and eats typed values mid-keystroke (focus is fine,\n // but the value never lands). The final committed value\n // is captured via onBlurCapture below instead.\n if (isHighFrequencyTextEntry(t)) return;\n pushEvent({\n name: 'onChange',\n target: t.tagName.toLowerCase(),\n payload: JSON.stringify({\n value: t.value,\n checked: t.type === 'checkbox' ? t.checked : undefined,\n }),\n });\n }}\n onInputCapture={(e) => {\n const t = e.target as HTMLInputElement;\n // Skip the input types where `input` is either redundant\n // or pathologically high-frequency:\n // - checkbox/radio fire `change` AND `input` for a single\n // toggle — the `change` is the meaningful one.\n // - range fires `input` on every drag tick (30-60Hz).\n // - text / tel / email / password / url / search / number\n // inputs and <textarea> fire `input` per keystroke —\n // same hazard as range, but worse because text fields\n // are controlled and the cascading re-render replaces\n // the inner DOM (or interferes with React's value\n // reconciliation) and drops typed characters. The blur\n // handler logs the final committed value instead.\n // Each captured event pushes to a context-managed log\n // that triggers a re-render of the UXM shell. Slow\n // renders during this loop visibly stutter range thumbs\n // AND silently eat keystrokes for text fields.\n if (t.type === 'checkbox' || t.type === 'radio' || t.type === 'range') return;\n if (isHighFrequencyTextEntry(t)) return;\n pushEvent({\n name: 'onInput',\n target: t.tagName.toLowerCase(),\n payload: JSON.stringify({ value: t.value }),\n });\n }}\n onFocusCapture={(e) =>\n pushEvent({\n name: 'onFocus',\n target: (e.target as HTMLElement).tagName.toLowerCase(),\n payload: JSON.stringify({}),\n })\n }\n onBlurCapture={(e) => {\n const t = e.target as HTMLElement;\n // For text-entry surfaces we suppressed the per-keystroke\n // onChange / onInput log entries (those caused mid-typing\n // re-render races that dropped characters). Include the\n // committed value here so designers can still see what\n // landed in the field once the user leaves it.\n const payload: Record<string, unknown> = {};\n if (isHighFrequencyTextEntry(t)) {\n payload.value = (t as HTMLInputElement | HTMLTextAreaElement).value;\n }\n pushEvent({\n name: 'onBlur',\n target: t.tagName.toLowerCase(),\n payload: JSON.stringify(payload),\n });\n }}\n onSubmitCapture={(e) => {\n pushEvent({\n name: 'onSubmit',\n target: (e.target as HTMLElement).tagName.toLowerCase(),\n payload: JSON.stringify({}),\n });\n // Forms in the preview shouldn't navigate — they're not\n // backed by anything. The atoms themselves don't always\n // remember to preventDefault, so we do it here.\n e.preventDefault();\n }}\n onKeyDownCapture={(e) => {\n // Log only \"meaningful\" keystrokes — Enter, Escape, Space,\n // and arrow keys. Logging every keystroke during typing\n // would drown the log; onInput already covers value changes.\n const meaningful = ['Enter', 'Escape', ' ', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Tab'];\n if (!meaningful.includes(e.key)) return;\n pushEvent({\n name: 'onKeyDown',\n target: (e.target as HTMLElement).tagName.toLowerCase(),\n payload: JSON.stringify({ key: e.key }),\n });\n }}\n >\n <Preview styles={resolved.styles} variants={resolved.variants} componentId={def.id} shell={shell} />\n </div>\n ) : (\n <p className=\"text-sm text-text-muted\">No preview available.</p>\n )}\n </div>\n </div>\n\n {previewOpen && <PreviewModal onClose={() => setPreviewOpen(false)} />}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/studio/shell/canvas.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\n\n\nimport {\n BannerPreview,\n BulkActionBarPreview,\n AppSidebarPreview,\n AppTopBarPreview,\n AvatarPreview,\n BackLinkPreview,\n BadgePreview,\n BrandSettingsPreview,\n BreadcrumbPreview,\n ButtonGroupPreview,\n ButtonPreview,\n ButtonWithIconPreview,\n CalendarPreview,\n CardPreview,\n CheckboxPreview,\n ChipPreview,\n ClusterPreview,\n CodeEditorPreview,\n ColorInputPreview,\n ComponentRowPreview,\n ConfigComponentRowPreview,\n ConfigSegmentItemPreview,\n ContentTooltipPreview,\n CurrencyInputPreview,\n DataTablePreview,\n DateInputPreview,\n DetailSectionPreview,\n DisclosurePreview,\n DividerPreview,\n EditableCellPreview,\n EmptyStatePreview,\n ErrorPagePreview,\n ExplorerListItemPreview,\n ExplorerSectionPreview,\n FileUploadPreview,\n FilterTabsPreview,\n FormFieldPreview,\n FormPreview,\n IconButtonPreview,\n IconPreview,\n IconTilePreview,\n InlineActionPreview,\n InlineFilterPreview,\n InputPreview,\n InputWithIconPreview,\n LanguageSwitcherPreview,\n LifecycleActionRowPreview,\n LifecycleConnectorKnobsPreview,\n LifecycleConnectorPreview,\n LifecycleDropSlotPreview,\n LifecycleEdgeLabelPreview,\n LifecycleGroupBoxPreview,\n LifecycleMinimapPreview,\n LifecycleNodeCardPreview,\n LifecyclePlusButtonPreview,\n LifecycleTerminalPreview,\n LifecycleZoomControlPreview,\n LinkPreview,\n ListItemPreview,\n ListboxPreview,\n LoaderPreview,\n LoginPagePreview,\n MenuPreview,\n MetaRowPreview,\n ModalPreview,\n MultirowFormPreview,\n NumberStepperPreview,\n NumberInputPreview,\n OptionListPreview,\n PageHeaderPreview,\n PageShellPreview,\n PasswordInputPreview,\n PhoneInputPreview,\n PillSelectPreview,\n ProgressBarPreview,\n PropertyFieldPreview,\n PropertyGridPreview,\n RadioGroupPreview,\n ResponsiveGridPreview,\n SearchDropdownPreview,\n SectionHeaderPreview,\n SegmentCardPreview,\n SegmentRowPreview,\n SelectPreview,\n SideFlexpanePreview,\n SidebarNavItemPreview,\n SidebarNavTriggerPreview,\n SliderPreview,\n StackPreview,\n StatCardPreview,\n TabsPreview,\n TabsUnderlinePreview,\n TagPreview,\n TextareaPreview,\n ThumbnailPreview,\n TimeInputPreview,\n TimelineEntryPreview,\n ToastPreview,\n TogglePreview,\n TooltipPreview,\n TypeOverviewCardPreview,\n ViewSwitcherPreview,\n} from '@/previews';\nimport { ButtonGhost, ButtonPrimary, IconButton } from '@/ui';\nimport { Icon } from '@/ui';\n\nimport { useUxm } from '../lib/context';\nimport { usePreviewShell } from '../lib/preview-shell';\nimport { getComponentDef } from '../lib/registry';\n\nimport { PreviewModal } from './preview-modal';\n\nimport type { PreviewProps } from '../lib/types';\nimport type { ReactNode } from 'react';\n\n\ntype PreviewComponent = React.ComponentType<PreviewProps>;\n\n/**\n * Text-entry surfaces (typeable inputs + textarea + contenteditable) where\n * `onInput` / React's mapped `onChange` fires per keystroke. Capturing those\n * events into the UXM event log triggers a context re-render on every\n * character, which races the controlled-input flow and silently drops\n * typed values mid-keystroke (Chrome / Safari / Firefox alike — repro'd\n * by typing into TextInput, PasswordInput, PhoneInput, etc. in the\n * canvas while the side-panel inputs work fine because they aren't\n * wrapped in the capture handlers). The fix: skip these surfaces in\n * the high-frequency capture handlers and rely on `onBlur` to log the\n * committed value once the user leaves the field.\n */\nfunction isHighFrequencyTextEntry(el: HTMLElement | null): boolean {\n if (!el) return false;\n if (el.tagName === 'TEXTAREA') return true;\n if ((el as HTMLElement).isContentEditable) return true;\n if (el.tagName === 'INPUT') {\n const type = (el as HTMLInputElement).type;\n return (\n type === 'text' ||\n type === 'tel' ||\n type === 'email' ||\n type === 'password' ||\n type === 'url' ||\n type === 'search' ||\n type === 'number'\n );\n }\n return false;\n}\n\nexport const previewMap: Record<string, PreviewComponent> = {\n 'app-sidebar': AppSidebarPreview,\n 'app-top-bar': AppTopBarPreview,\n 'button-primary': ButtonPreview,\n 'button-secondary': ButtonPreview,\n 'button-tertiary': ButtonPreview,\n 'button-ghost': ButtonPreview,\n 'button-danger': ButtonPreview,\n 'button-icon': ButtonPreview,\n 'button-with-icon': ButtonWithIconPreview,\n 'input-text': InputPreview,\n 'input-with-icon': InputWithIconPreview,\n 'color-input': ColorInputPreview,\n textarea: TextareaPreview,\n 'file-upload': FileUploadPreview,\n 'select-dropdown': SelectPreview,\n checkbox: CheckboxPreview,\n 'toggle-switch': TogglePreview,\n 'radio-group': RadioGroupPreview,\n chip: ChipPreview,\n 'code-editor': CodeEditorPreview,\n tag: TagPreview,\n card: CardPreview,\n avatar: AvatarPreview,\n 'empty-state': EmptyStatePreview,\n banner: BannerPreview,\n 'bulk-action-bar': BulkActionBarPreview,\n toast: ToastPreview,\n tooltip: TooltipPreview,\n 'form-login': FormPreview,\n 'form-contact': FormPreview,\n 'pill-select': PillSelectPreview,\n 'inline-filter': InlineFilterPreview,\n 'content-tooltip': ContentTooltipPreview,\n 'form-multirow': MultirowFormPreview,\n 'data-table': DataTablePreview,\n 'button-group': ButtonGroupPreview,\n 'stat-card': StatCardPreview,\n 'type-overview-card': TypeOverviewCardPreview,\n 'page-shell': PageShellPreview,\n 'sidebar-nav-item': SidebarNavItemPreview,\n 'sidebar-nav-trigger': SidebarNavTriggerPreview,\n 'explorer-list-item': ExplorerListItemPreview,\n 'explorer-section': ExplorerSectionPreview,\n 'inline-action': InlineActionPreview,\n 'number-stepper': NumberStepperPreview,\n 'number-input': NumberInputPreview,\n 'currency-input': CurrencyInputPreview,\n 'search-dropdown': SearchDropdownPreview,\n 'language-switcher': LanguageSwitcherPreview,\n listbox: ListboxPreview,\n menu: MenuPreview,\n 'editable-cell': EditableCellPreview,\n 'section-header': SectionHeaderPreview,\n slider: SliderPreview,\n 'error-page': ErrorPagePreview,\n 'login-page': LoginPagePreview,\n loader: LoaderPreview,\n 'progress-bar': ProgressBarPreview,\n 'timeline-entry': TimelineEntryPreview,\n 'segment-card': SegmentCardPreview,\n 'segment-row': SegmentRowPreview,\n 'component-row': ComponentRowPreview,\n 'option-list': OptionListPreview,\n 'config-segment-item': ConfigSegmentItemPreview,\n 'config-component-row': ConfigComponentRowPreview,\n 'icon-tile': IconTilePreview,\n 'meta-row': MetaRowPreview,\n icon: IconPreview,\n 'icon-button': IconButtonPreview,\n 'detail-section': DetailSectionPreview,\n 'back-link': BackLinkPreview,\n badge: BadgePreview,\n thumbnail: ThumbnailPreview,\n 'date-input': DateInputPreview,\n 'password-input': PasswordInputPreview,\n 'phone-input': PhoneInputPreview,\n 'time-input': TimeInputPreview,\n calendar: CalendarPreview,\n disclosure: DisclosurePreview,\n 'link': LinkPreview,\n 'breadcrumb': BreadcrumbPreview,\n 'property-field': PropertyFieldPreview,\n 'property-grid': PropertyGridPreview,\n 'form-field': FormFieldPreview,\n 'list-item': ListItemPreview,\n 'side-flexpane': SideFlexpanePreview,\n 'page-header': PageHeaderPreview,\n 'divider': DividerPreview,\n 'filter-tabs': FilterTabsPreview,\n 'view-switcher': ViewSwitcherPreview,\n 'tabs': TabsPreview,\n 'tabs-underline': TabsUnderlinePreview,\n 'brand-settings': BrandSettingsPreview,\n 'lifecycle-node-card': LifecycleNodeCardPreview,\n 'lifecycle-terminal': LifecycleTerminalPreview,\n 'lifecycle-edge-label': LifecycleEdgeLabelPreview,\n 'lifecycle-plus-button': LifecyclePlusButtonPreview,\n 'lifecycle-zoom-control': LifecycleZoomControlPreview,\n 'lifecycle-minimap': LifecycleMinimapPreview,\n 'lifecycle-group-box': LifecycleGroupBoxPreview,\n 'lifecycle-drop-slot': LifecycleDropSlotPreview,\n 'lifecycle-action-row': LifecycleActionRowPreview,\n modal: ModalPreview,\n 'lifecycle-connector': LifecycleConnectorPreview,\n 'lifecycle-connector-knobs': LifecycleConnectorKnobsPreview,\n 'responsive-grid': ResponsiveGridPreview,\n stack: StackPreview,\n cluster: ClusterPreview,\n};\n\nexport function Canvas({\n embed = false,\n headerActions,\n}: {\n embed?: boolean;\n /** Optional host-provided actions rendered at the end of the top-bar\n cluster, right after the Preview & Publish button (e.g. a user avatar). */\n headerActions?: ReactNode;\n}) {\n const [previewOpen, setPreviewOpen] = useState(false);\n const { selectedId, getOverrides, resetOverrides, getCurrentVariants, pushEvent, capabilities, theme, setTheme } = useUxm();\n const shell = usePreviewShell();\n const def = getComponentDef(selectedId);\n const overrides = getOverrides(selectedId);\n const currentVariants = getCurrentVariants();\n\n const resolved = useMemo(() => {\n if (!def) return { styles: {}, variants: {} };\n const styles: Record<string, string | number | boolean> = {};\n for (const prop of def.styleProperties) {\n styles[prop.key] = overrides[prop.key] ?? prop.defaultValue;\n }\n // Variants read from ephemeral session state, NOT from overrides.\n // Variant selections decide which knobs are visible in the panel —\n // they don't persist and never get saved to components.css/json.\n const variants: Record<string, string> = {};\n for (const v of def.layoutVariants) {\n variants[v.key] = (currentVariants[v.key] as string | undefined) ?? v.defaultValue;\n }\n return { styles, variants };\n }, [def, overrides, currentVariants]);\n\n if (!def) return null;\n\n const Preview = previewMap[def.id];\n const hasOverrides = Object.keys(overrides).length > 0;\n\n return (\n <div className=\"flex h-full flex-col bg-card\">\n {/* Toolbar — uses container queries so the layout adapts to the\n canvas's actual width, not the viewport. The canvas can be\n much narrower than the viewport when the sidebar + properties\n pane are open; viewport breakpoints would respond to the\n wrong dimension. Three steps:\n ≥640px full (name + description + labelled buttons)\n 480-640 hide description, keep labels\n <480px icon-only secondary buttons; primary CTA stays\n labelled (Preview & Publish is moment-of-truth). */}\n <div className=\"@container flex items-center justify-between gap-3 border-b border-border px-6 py-4\">\n {/* min-w-0 enables `truncate` on the children — without it the\n flex item ignores the constraint and pushes the actions\n cluster off-screen. */}\n <div className=\"min-w-0 flex-1\">\n <h2 className=\"text-[15px] font-semibold text-text truncate\">{def.name}</h2>\n <p className=\"text-xs text-text-muted mt-0.5 hidden @[640px]:block truncate\">\n {def.description}\n </p>\n </div>\n {/* flex-shrink-0 keeps the actions cluster from wrapping; the\n label spans inside the secondary buttons hide first when\n space gets tight. */}\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n {hasOverrides && (\n <ButtonGhost onClick={() => resetOverrides(selectedId)} title=\"Reset overrides\">\n <Icon glyph=\"refresh\" size={14} />\n <span className=\"hidden @[480px]:inline\">Reset</span>\n </ButtonGhost>\n )}\n {/* Light/dark toggle — only in the standalone portal (the studio\n owns the theme here; ThemeSync mirrors it onto data-theme). An\n embedded host renders its own theme control. */}\n {!embed && (\n <IconButton\n onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}\n aria-label={theme === 'dark' ? 'Switch to light theme' : 'Switch to dark theme'}\n title={theme === 'dark' ? 'Switch to light theme' : 'Switch to dark theme'}\n >\n <Icon glyph={theme === 'dark' ? 'sun' : 'moon'} size={16} />\n </IconButton>\n )}\n <ButtonPrimary onClick={() => setPreviewOpen(true)}>\n <Icon glyph=\"eye\" size={14} />\n {capabilities.persist ? 'Preview & Publish' : 'Preview'}\n </ButtonPrimary>\n {headerActions}\n </div>\n </div>\n\n {/* Preview area with optional dot grid */}\n <div\n className=\"flex-1 overflow-auto p-12\"\n style={def.canvasBackground === false ? undefined : {\n backgroundImage: 'radial-gradient(circle, var(--color-canvas-dot) 1px, transparent 1px)',\n backgroundSize: '20px 20px',\n }}\n >\n <div className={def.canvasFill ? 'min-h-full' : 'min-h-full flex items-center justify-center'}>\n {Preview ? (\n // Wrap the preview in a capture-phase event listener so any\n // native event the rendered atom fires gets logged to the\n // Events tab. Capture (rather than bubble) sees the event\n // before the atom's own handler — so we log first, the atom\n // behaves second. We don't `preventDefault`, so the rendered\n // component still works normally.\n <div\n onClickCapture={(e) =>\n pushEvent({\n name: 'onClick',\n target: (e.target as HTMLElement).tagName.toLowerCase(),\n payload: JSON.stringify({ x: e.clientX, y: e.clientY, button: e.button }),\n })\n }\n onChangeCapture={(e) => {\n const t = e.target as HTMLInputElement;\n // Skip text-entry surfaces here too — React maps onChange\n // to the native `input` event for text inputs / textareas /\n // contenteditable, so this capture would fire on EVERY\n // keystroke. Combined with onInputCapture below, that's\n // two `setEventLog` calls per character, each triggering\n // a context re-render that races the controlled-input\n // flow and eats typed values mid-keystroke (focus is fine,\n // but the value never lands). The final committed value\n // is captured via onBlurCapture below instead.\n if (isHighFrequencyTextEntry(t)) return;\n pushEvent({\n name: 'onChange',\n target: t.tagName.toLowerCase(),\n payload: JSON.stringify({\n value: t.value,\n checked: t.type === 'checkbox' ? t.checked : undefined,\n }),\n });\n }}\n onInputCapture={(e) => {\n const t = e.target as HTMLInputElement;\n // Skip the input types where `input` is either redundant\n // or pathologically high-frequency:\n // - checkbox/radio fire `change` AND `input` for a single\n // toggle — the `change` is the meaningful one.\n // - range fires `input` on every drag tick (30-60Hz).\n // - text / tel / email / password / url / search / number\n // inputs and <textarea> fire `input` per keystroke —\n // same hazard as range, but worse because text fields\n // are controlled and the cascading re-render replaces\n // the inner DOM (or interferes with React's value\n // reconciliation) and drops typed characters. The blur\n // handler logs the final committed value instead.\n // Each captured event pushes to a context-managed log\n // that triggers a re-render of the UXM shell. Slow\n // renders during this loop visibly stutter range thumbs\n // AND silently eat keystrokes for text fields.\n if (t.type === 'checkbox' || t.type === 'radio' || t.type === 'range') return;\n if (isHighFrequencyTextEntry(t)) return;\n pushEvent({\n name: 'onInput',\n target: t.tagName.toLowerCase(),\n payload: JSON.stringify({ value: t.value }),\n });\n }}\n onFocusCapture={(e) =>\n pushEvent({\n name: 'onFocus',\n target: (e.target as HTMLElement).tagName.toLowerCase(),\n payload: JSON.stringify({}),\n })\n }\n onBlurCapture={(e) => {\n const t = e.target as HTMLElement;\n // For text-entry surfaces we suppressed the per-keystroke\n // onChange / onInput log entries (those caused mid-typing\n // re-render races that dropped characters). Include the\n // committed value here so designers can still see what\n // landed in the field once the user leaves it.\n const payload: Record<string, unknown> = {};\n if (isHighFrequencyTextEntry(t)) {\n payload.value = (t as HTMLInputElement | HTMLTextAreaElement).value;\n }\n pushEvent({\n name: 'onBlur',\n target: t.tagName.toLowerCase(),\n payload: JSON.stringify(payload),\n });\n }}\n onSubmitCapture={(e) => {\n pushEvent({\n name: 'onSubmit',\n target: (e.target as HTMLElement).tagName.toLowerCase(),\n payload: JSON.stringify({}),\n });\n // Forms in the preview shouldn't navigate — they're not\n // backed by anything. The atoms themselves don't always\n // remember to preventDefault, so we do it here.\n e.preventDefault();\n }}\n onKeyDownCapture={(e) => {\n // Log only \"meaningful\" keystrokes — Enter, Escape, Space,\n // and arrow keys. Logging every keystroke during typing\n // would drown the log; onInput already covers value changes.\n const meaningful = ['Enter', 'Escape', ' ', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Tab'];\n if (!meaningful.includes(e.key)) return;\n pushEvent({\n name: 'onKeyDown',\n target: (e.target as HTMLElement).tagName.toLowerCase(),\n payload: JSON.stringify({ key: e.key }),\n });\n }}\n >\n <Preview styles={resolved.styles} variants={resolved.variants} componentId={def.id} shell={shell} />\n </div>\n ) : (\n <p className=\"text-sm text-text-muted\">No preview available.</p>\n )}\n </div>\n </div>\n\n {previewOpen && <PreviewModal onClose={() => setPreviewOpen(false)} />}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -26,6 +26,7 @@ var import_react = require("react");
26
26
  var import_helpers = require("../../helpers/index.cjs");
27
27
  var import_calendar_grid = require("../../lib/calendar-grid.cjs");
28
28
  var import_icon = require("../icon/index.cjs");
29
+ var import_locale = require("../locale/index.cjs");
29
30
  const MS_PER_DAY = 864e5;
30
31
  const YEARS_PER_BLOCK = 12;
31
32
  function startOfDay(d) {
@@ -87,7 +88,7 @@ function Calendar({
87
88
  today,
88
89
  isDisabled,
89
90
  weekStartsOn = 0,
90
- locale = "en-US",
91
+ locale: localeProp,
91
92
  onChange,
92
93
  onMonthChange,
93
94
  shadow = true,
@@ -97,6 +98,7 @@ function Calendar({
97
98
  className,
98
99
  ...rest
99
100
  }) {
101
+ const locale = (0, import_locale.useUxmLocale)(localeProp);
100
102
  const realToday = (0, import_react.useMemo)(() => today ?? /* @__PURE__ */ new Date(), [today]);
101
103
  const [internalMonth, setInternalMonth] = (0, import_react.useState)(() => {
102
104
  const seed = value?.start ?? value?.end ?? realToday;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/calendar/calendar.tsx"],"sourcesContent":["import { useCallback, useMemo, useRef, useState } from 'react';\n\nimport { cn } from '@/helpers';\n\nimport { computeMonthGrid, type WeekStart } from '../../lib/calendar-grid';\nimport { Icon } from '../icon';\n\nimport type { HTMLAttributes, KeyboardEvent } from 'react';\n\nexport interface CalendarValue {\n start: Date | null;\n end: Date | null;\n}\n\ntype CalendarView = 'day' | 'month' | 'year';\n\nexport interface CalendarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {\n /** Currently-displayed month. Controlled. If omitted, atom owns it (initialized to the selected value's month, else today's). */\n month?: Date;\n /** Selection value. Controlled when passed. `{ start, end: null }` for a single date; `{ start, end }` for a range. */\n value?: CalendarValue;\n /** Override for \"today\". Defaults to real current date. */\n today?: Date;\n /** Predicate for disabled days. Disabled cells ignore clicks. */\n isDisabled?: (date: Date) => boolean;\n /** 0 = Sunday (US), 1 = Monday. */\n weekStartsOn?: WeekStart;\n /** Locale for month label and weekday names. */\n locale?: string;\n /** Fires after every click. `end` is null after the first click of a new range, set after the second. */\n onChange?: (value: CalendarValue) => void;\n /** Month navigation callback (prev/next buttons, outside-month-cell click, or drilling via month/year views). */\n onMonthChange?: (next: Date) => void;\n /** Drop a soft elevation shadow (the floating-surface look). Default true — pass `false` for a flat, inline / embedded calendar. */\n shadow?: boolean;\n /** Accessible name for the previous-month button. Default `\"Previous\"`. */\n previousMonthLabel?: string;\n /** Accessible name for the next-month button. Default `\"Next\"`. */\n nextMonthLabel?: string;\n /** Accessible name for the month/year drill-up button. Default `\"Drill up\"`. */\n drillUpLabel?: string;\n}\n\nconst MS_PER_DAY = 86_400_000;\nconst YEARS_PER_BLOCK = 12;\n\nfunction startOfDay(d: Date): Date {\n return new Date(d.getFullYear(), d.getMonth(), d.getDate());\n}\nfunction sameDay(a: Date | null | undefined, b: Date | null | undefined): boolean {\n if (!a || !b) return false;\n return startOfDay(a).getTime() === startOfDay(b).getTime();\n}\nfunction isStrictlyBetween(date: Date, start: Date | null | undefined, end: Date | null | undefined): boolean {\n if (!start || !end) return false;\n const t = startOfDay(date).getTime();\n const lo = Math.min(startOfDay(start).getTime(), startOfDay(end).getTime());\n const hi = Math.max(startOfDay(start).getTime(), startOfDay(end).getTime());\n return t > lo && t < hi;\n}\n\nfunction getWeekdayLabels(locale: string, weekStartsOn: WeekStart): string[] {\n const fmt = new Intl.DateTimeFormat(locale, { weekday: 'short' });\n // 2024-01-07 is a known Sunday — stable anchor for the formatter.\n const anchor = new Date(2024, 0, 7);\n const labels: string[] = [];\n for (let i = 0; i < 7; i++) {\n labels.push(fmt.format(new Date(anchor.getTime() + i * MS_PER_DAY)));\n }\n return weekStartsOn === 1 ? [...labels.slice(1), labels[0]] : labels;\n}\n\n/**\n * Year-block start for the year view. 12-year blocks aligned to multiples of\n * `YEARS_PER_BLOCK` (e.g. 2024 → 2016–2027). Block alignment is the iOS pattern;\n * picking any other anchor (decades, etc.) is equally valid — this just keeps\n * navigation predictable.\n */\nfunction yearBlockStart(year: number): number {\n return Math.floor(year / YEARS_PER_BLOCK) * YEARS_PER_BLOCK;\n}\n\n/** True if any day of `year-month` overlaps the inclusive selection range. */\nfunction monthOverlapsSelection(year: number, monthIdx: number, value: CalendarValue): boolean {\n if (!value.start) return false;\n const monthFirstMs = new Date(year, monthIdx, 1).getTime();\n const monthLastMs = new Date(year, monthIdx + 1, 0).getTime();\n const sMs = startOfDay(value.start).getTime();\n const eMs = value.end ? startOfDay(value.end).getTime() : sMs;\n const lo = Math.min(sMs, eMs);\n const hi = Math.max(sMs, eMs);\n return monthFirstMs <= hi && monthLastMs >= lo;\n}\n\n/** True if `year` falls within the inclusive selection range's year span. */\nfunction yearOverlapsSelection(year: number, value: CalendarValue): boolean {\n if (!value.start) return false;\n const sY = value.start.getFullYear();\n const eY = value.end ? value.end.getFullYear() : sY;\n const lo = Math.min(sY, eY);\n const hi = Math.max(sY, eY);\n return year >= lo && year <= hi;\n}\n\nconst EMPTY_VALUE: CalendarValue = { start: null, end: null };\n\n/**\n * Default roving-tabindex target for the day grid: the selected day if\n * it's in the visible grid and enabled, else today if visible and\n * enabled, else the first enabled cell, else 0. Keeps the grid reachable\n * via Tab even when the \"obvious\" cell happens to be disabled.\n */\nfunction defaultDayFocusIndex(\n cells: { date: Date; outside: boolean }[],\n start: Date | null,\n today: Date,\n isCellDisabled: (date: Date) => boolean,\n): number {\n const startIndex = start ? cells.findIndex((c) => sameDay(c.date, start)) : -1;\n if (startIndex >= 0 && !isCellDisabled(cells[startIndex].date)) return startIndex;\n const todayIndex = cells.findIndex((c) => sameDay(c.date, today));\n if (todayIndex >= 0 && !isCellDisabled(cells[todayIndex].date)) return todayIndex;\n const firstEnabled = cells.findIndex((c) => !isCellDisabled(c.date));\n return firstEnabled >= 0 ? firstEnabled : 0;\n}\n\nexport function Calendar({\n month,\n value,\n today,\n isDisabled,\n weekStartsOn = 0,\n locale = 'en-US',\n onChange,\n onMonthChange,\n shadow = true,\n previousMonthLabel = 'Previous',\n nextMonthLabel = 'Next',\n drillUpLabel = 'Drill up',\n className,\n ...rest\n}: CalendarProps) {\n const realToday = useMemo(() => today ?? new Date(), [today]);\n\n // Month state — controlled when `month` prop is provided. When uncontrolled,\n // seed from the current selection so opening on an existing date lands on\n // that date's month, not today's. The popover consumers mount the calendar\n // fresh per open, so this initial `value` is the selected date; nav then\n // owns `internalMonth`. Falls back to today's month when nothing's selected.\n const [internalMonth, setInternalMonth] = useState<Date>(() => {\n // `end` covers the range-input edge case where only the closing half is\n // seeded (`{ start: null, end }`); today is the last resort.\n const seed = value?.start ?? value?.end ?? realToday;\n return new Date(seed.getFullYear(), seed.getMonth(), 1);\n });\n const currentMonth = month ?? internalMonth;\n\n // View state — pure UI state, never controlled. Day view by default. Header\n // click drills up (day → month → year). Cell click drills back down.\n const [view, setView] = useState<CalendarView>('day');\n\n // Unified selection model — atom infers single vs range from click sequence.\n const isControlled = value !== undefined;\n const [internalValue, setInternalValue] = useState<CalendarValue>(EMPTY_VALUE);\n const currentValue = isControlled ? value : internalValue;\n const currentStart = currentValue.start;\n const currentEnd = currentValue.end;\n\n // Hover preview — only meaningful in the in-flight window in day view.\n const [hoveredDate, setHoveredDate] = useState<Date | null>(null);\n const inFlight = !!currentStart && !currentEnd;\n const previewEnd = inFlight && hoveredDate ? hoveredDate : currentEnd;\n const isRangeView = !!currentStart && !!previewEnd && !sameDay(currentStart, previewEnd);\n\n const dayCells = useMemo(\n () => computeMonthGrid(currentMonth, weekStartsOn),\n [currentMonth, weekStartsOn],\n );\n\n const isCellDisabled = useCallback((date: Date) => isDisabled?.(date) ?? false, [isDisabled]);\n\n // Roving tabindex across the day grid (ARIA grid pattern): only one cell\n // is a Tab stop at a time; arrow keys move it. `dayRefs` lets the arrow\n // handler call `.focus()` on the target cell after moving the index.\n const dayRefs = useRef<Array<HTMLButtonElement | null>>([]);\n const [focusedDayIndex, setFocusedDayIndex] = useState<number>(() =>\n defaultDayFocusIndex(dayCells, currentStart, realToday, isCellDisabled),\n );\n\n const weekdays = useMemo(\n () => getWeekdayLabels(locale, weekStartsOn),\n [locale, weekStartsOn],\n );\n\n // Header label — depends on the active view.\n const titleLabel = useMemo(() => {\n if (view === 'day') {\n return new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }).format(currentMonth);\n }\n if (view === 'month') {\n return String(currentMonth.getFullYear());\n }\n const start = yearBlockStart(currentMonth.getFullYear());\n return `${start}–${start + YEARS_PER_BLOCK - 1}`;\n }, [view, currentMonth, locale]);\n\n // Month-view labels — short month names from the locale formatter.\n const monthShortNames = useMemo(() => {\n const fmt = new Intl.DateTimeFormat(locale, { month: 'short' });\n return Array.from({ length: 12 }, (_, i) => fmt.format(new Date(2024, i, 1)));\n }, [locale]);\n\n const setMonth = useCallback(\n (next: Date) => {\n if (month === undefined) setInternalMonth(next);\n onMonthChange?.(next);\n // Re-target the day grid's roving tab stop for the month we're\n // navigating to — the previous index otherwise points at an\n // unrelated cell (or none) in the new grid.\n const nextCells = computeMonthGrid(next, weekStartsOn);\n setFocusedDayIndex(defaultDayFocusIndex(nextCells, currentStart, realToday, isCellDisabled));\n },\n [month, onMonthChange, weekStartsOn, currentStart, realToday, isCellDisabled],\n );\n\n // Prev/next nav stepping is view-aware: day → ±1 month, month → ±1 year,\n // year → ±12 years (one block).\n const handleNav = useCallback(\n (delta: number) => {\n const m = currentMonth.getMonth();\n const y = currentMonth.getFullYear();\n let next: Date;\n if (view === 'day') {\n next = new Date(y, m + delta, 1);\n } else if (view === 'month') {\n next = new Date(y + delta, m, 1);\n } else {\n next = new Date(y + delta * YEARS_PER_BLOCK, m, 1);\n }\n setMonth(next);\n },\n [view, currentMonth, setMonth],\n );\n\n // Header click drills up. Year view has nowhere further to go.\n const handleTitleClick = useCallback(() => {\n if (view === 'day') setView('month');\n else if (view === 'month') setView('year');\n }, [view]);\n\n const commit = useCallback(\n (next: CalendarValue) => {\n if (!isControlled) setInternalValue(next);\n onChange?.(next);\n },\n [isControlled, onChange],\n );\n\n // Day-cell click — the unified single/range click logic.\n const handleDayClick = useCallback(\n (date: Date, outside: boolean, disabled: boolean) => {\n if (disabled) return;\n if (outside) {\n setMonth(new Date(date.getFullYear(), date.getMonth(), 1));\n }\n const startingNew = !currentStart || !!currentEnd;\n if (startingNew) {\n commit({ start: date, end: null });\n } else {\n const s = currentStart!;\n let finalStart = s;\n let finalEnd = date;\n if (startOfDay(date).getTime() < startOfDay(s).getTime()) {\n finalStart = date;\n finalEnd = s;\n }\n commit({ start: finalStart, end: finalEnd });\n }\n setHoveredDate(null);\n },\n [currentStart, currentEnd, commit, setMonth],\n );\n\n // Month-view click drills back to day view for the clicked month.\n const handleMonthClick = useCallback(\n (monthIdx: number) => {\n setMonth(new Date(currentMonth.getFullYear(), monthIdx, 1));\n setView('day');\n },\n [currentMonth, setMonth],\n );\n\n // Year-view click drills to month view for the clicked year, preserving\n // the previously-active month-of-year so the user doesn't lose context.\n const handleYearClick = useCallback(\n (year: number) => {\n setMonth(new Date(year, currentMonth.getMonth(), 1));\n setView('month');\n },\n [currentMonth, setMonth],\n );\n\n const handleMouseEnter = useCallback(\n (date: Date, disabled: boolean) => {\n if (disabled) return;\n if (inFlight) setHoveredDate(date);\n },\n [inFlight],\n );\n\n const handleMouseLeave = useCallback((date: Date) => {\n setHoveredDate((prev) => (sameDay(prev, date) ? null : prev));\n }, []);\n\n // ARIA grid keyboard navigation for the day view: ArrowLeft/Right move a\n // day, ArrowUp/Down move a week, Home/End jump to the start/end of the\n // current week row. Stays within the visible 6×7 grid (doesn't cross\n // into the previous/next month) — disabled cells are skipped so arrow\n // navigation never lands on (and gets stuck at) an inert cell.\n const handleDayKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>, i: number) => {\n const rowStart = i - (i % 7);\n let step = 0;\n let bound: number | null = null;\n switch (e.key) {\n case 'ArrowLeft':\n step = -1;\n break;\n case 'ArrowRight':\n step = 1;\n break;\n case 'ArrowUp':\n step = -7;\n break;\n case 'ArrowDown':\n step = 7;\n break;\n case 'Home':\n step = -1;\n bound = rowStart;\n break;\n case 'End':\n step = 1;\n bound = rowStart + 6;\n break;\n default:\n return;\n }\n e.preventDefault();\n let next = bound ?? i + step;\n while (\n next >= 0 &&\n next < dayCells.length &&\n isCellDisabled(dayCells[next].date)\n ) {\n next += step;\n }\n if (next < 0 || next >= dayCells.length) return;\n setFocusedDayIndex(next);\n dayRefs.current[next]?.focus();\n },\n [dayCells, isCellDisabled],\n );\n\n return (\n <div className={cn('uxm-calendar', `uxm-calendar--view-${view}`, shadow && 'uxm-calendar--shadow', className)} {...rest}>\n <div className=\"uxm-calendar__header\">\n <button\n type=\"button\"\n className=\"uxm-calendar__nav\"\n onClick={() => handleNav(-1)}\n aria-label={previousMonthLabel}\n >\n <Icon glyph=\"chevron-left\" size={16} />\n </button>\n <button\n type=\"button\"\n className=\"uxm-calendar__title\"\n onClick={handleTitleClick}\n disabled={view === 'year'}\n aria-label={view === 'year' ? undefined : drillUpLabel}\n >\n {titleLabel}\n </button>\n <button\n type=\"button\"\n className=\"uxm-calendar__nav\"\n onClick={() => handleNav(1)}\n aria-label={nextMonthLabel}\n >\n <Icon glyph=\"chevron-right\" size={16} />\n </button>\n </div>\n\n {view === 'day' && (\n <>\n <div className=\"uxm-calendar__weekdays\" aria-hidden=\"true\">\n {weekdays.map((w) => (\n <div key={w} className=\"uxm-calendar__weekday\">{w}</div>\n ))}\n </div>\n <div className=\"uxm-calendar__grid\" role=\"grid\">\n {Array.from({ length: dayCells.length / 7 }, (_, week) => (\n // `display: contents` (see calendar.scss) keeps these cells as\n // direct participants in the parent's CSS grid while giving\n // each week its own `role=\"row\"` — the ARIA grid pattern\n // requires gridcells to be grouped into rows.\n <div key={week} role=\"row\" className=\"uxm-calendar__grid-row\">\n {dayCells.slice(week * 7, week * 7 + 7).map(({ date, outside }, col) => {\n const i = week * 7 + col;\n const isToday = sameDay(date, realToday);\n const isStart = sameDay(date, currentStart);\n const isEnd = !!previewEnd && sameDay(date, previewEnd);\n const isSelected = !isRangeView && isStart;\n const isRangeStart = isRangeView && isStart;\n const isRangeEnd = isRangeView && isEnd;\n const isInRange = isStrictlyBetween(date, currentStart, previewEnd);\n const disabled = isDisabled?.(date) ?? false;\n return (\n <button\n key={i}\n ref={(el) => {\n dayRefs.current[i] = el;\n }}\n type=\"button\"\n role=\"gridcell\"\n disabled={disabled}\n tabIndex={i === focusedDayIndex ? 0 : -1}\n onClick={() => {\n handleDayClick(date, outside, disabled);\n if (!disabled && !outside) setFocusedDayIndex(i);\n }}\n onKeyDown={(e) => handleDayKeyDown(e, i)}\n onMouseEnter={() => handleMouseEnter(date, disabled)}\n onMouseLeave={() => handleMouseLeave(date)}\n className={cn(\n 'uxm-calendar__day',\n outside && 'uxm-calendar__day--outside',\n isToday && 'uxm-calendar__day--today',\n isSelected && 'uxm-calendar__day--selected',\n isRangeStart && 'uxm-calendar__day--range-start',\n isRangeEnd && 'uxm-calendar__day--range-end',\n isInRange && 'uxm-calendar__day--in-range',\n disabled && 'uxm-calendar__day--disabled',\n )}\n aria-selected={isSelected || isRangeStart || isRangeEnd || undefined}\n aria-current={isToday ? 'date' : undefined}\n >\n {date.getDate()}\n </button>\n );\n })}\n </div>\n ))}\n </div>\n </>\n )}\n\n {view === 'month' && (\n <div className=\"uxm-calendar__months\" role=\"grid\">\n {monthShortNames.map((name, i) => {\n const year = currentMonth.getFullYear();\n const isCurrent = realToday.getFullYear() === year && realToday.getMonth() === i;\n const isSelected = monthOverlapsSelection(year, i, currentValue);\n return (\n <button\n key={i}\n type=\"button\"\n role=\"gridcell\"\n onClick={() => handleMonthClick(i)}\n className={cn(\n 'uxm-calendar__month',\n isCurrent && 'uxm-calendar__month--today',\n isSelected && 'uxm-calendar__month--selected',\n )}\n aria-selected={isSelected || undefined}\n aria-current={isCurrent ? 'true' : undefined}\n >\n {name}\n </button>\n );\n })}\n </div>\n )}\n\n {view === 'year' && (\n <div className=\"uxm-calendar__years\" role=\"grid\">\n {Array.from({ length: YEARS_PER_BLOCK }, (_, i) => {\n const year = yearBlockStart(currentMonth.getFullYear()) + i;\n const isCurrent = realToday.getFullYear() === year;\n const isSelected = yearOverlapsSelection(year, currentValue);\n return (\n <button\n key={year}\n type=\"button\"\n role=\"gridcell\"\n onClick={() => handleYearClick(year)}\n className={cn(\n 'uxm-calendar__year',\n isCurrent && 'uxm-calendar__year--today',\n isSelected && 'uxm-calendar__year--selected',\n )}\n aria-selected={isSelected || undefined}\n aria-current={isCurrent ? 'true' : undefined}\n >\n {year}\n </button>\n );\n })}\n </div>\n )}\n\n {/* \"Today\" — jumps the view back to the current month in day view so\n the user can find today after navigating away. Deliberately does\n NOT select today (returning to a date ≠ committing it); the day\n cell is one click away. */}\n <div className=\"uxm-calendar__footer\">\n <button\n type=\"button\"\n className=\"uxm-calendar__today-button\"\n disabled={isDisabled?.(realToday)}\n onClick={() => {\n setView('day');\n setMonth(new Date(realToday.getFullYear(), realToday.getMonth(), 1));\n }}\n >\n Today\n </button>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/ui/calendar/calendar.tsx"],"sourcesContent":["import { useCallback, useMemo, useRef, useState } from 'react';\n\nimport { cn } from '@/helpers';\n\nimport { computeMonthGrid, type WeekStart } from '../../lib/calendar-grid';\nimport { Icon } from '../icon';\nimport { useUxmLocale } from '../locale';\n\nimport type { HTMLAttributes, KeyboardEvent } from 'react';\n\nexport interface CalendarValue {\n start: Date | null;\n end: Date | null;\n}\n\ntype CalendarView = 'day' | 'month' | 'year';\n\nexport interface CalendarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {\n /** Currently-displayed month. Controlled. If omitted, atom owns it (initialized to the selected value's month, else today's). */\n month?: Date;\n /** Selection value. Controlled when passed. `{ start, end: null }` for a single date; `{ start, end }` for a range. */\n value?: CalendarValue;\n /** Override for \"today\". Defaults to real current date. */\n today?: Date;\n /** Predicate for disabled days. Disabled cells ignore clicks. */\n isDisabled?: (date: Date) => boolean;\n /** 0 = Sunday (US), 1 = Monday. */\n weekStartsOn?: WeekStart;\n /**\n * BCP-47 locale for the month label and weekday names. Defaults to the\n * nearest `UxmLocaleProvider`, then to `\"en-US\"`. Set it here only to pin\n * one calendar to a locale that differs from the rest of the app.\n */\n locale?: string;\n /** Fires after every click. `end` is null after the first click of a new range, set after the second. */\n onChange?: (value: CalendarValue) => void;\n /** Month navigation callback (prev/next buttons, outside-month-cell click, or drilling via month/year views). */\n onMonthChange?: (next: Date) => void;\n /** Drop a soft elevation shadow (the floating-surface look). Default true — pass `false` for a flat, inline / embedded calendar. */\n shadow?: boolean;\n /** Accessible name for the previous-month button. Default `\"Previous\"`. */\n previousMonthLabel?: string;\n /** Accessible name for the next-month button. Default `\"Next\"`. */\n nextMonthLabel?: string;\n /** Accessible name for the month/year drill-up button. Default `\"Drill up\"`. */\n drillUpLabel?: string;\n}\n\nconst MS_PER_DAY = 86_400_000;\nconst YEARS_PER_BLOCK = 12;\n\nfunction startOfDay(d: Date): Date {\n return new Date(d.getFullYear(), d.getMonth(), d.getDate());\n}\nfunction sameDay(a: Date | null | undefined, b: Date | null | undefined): boolean {\n if (!a || !b) return false;\n return startOfDay(a).getTime() === startOfDay(b).getTime();\n}\nfunction isStrictlyBetween(date: Date, start: Date | null | undefined, end: Date | null | undefined): boolean {\n if (!start || !end) return false;\n const t = startOfDay(date).getTime();\n const lo = Math.min(startOfDay(start).getTime(), startOfDay(end).getTime());\n const hi = Math.max(startOfDay(start).getTime(), startOfDay(end).getTime());\n return t > lo && t < hi;\n}\n\nfunction getWeekdayLabels(locale: string, weekStartsOn: WeekStart): string[] {\n const fmt = new Intl.DateTimeFormat(locale, { weekday: 'short' });\n // 2024-01-07 is a known Sunday — stable anchor for the formatter.\n const anchor = new Date(2024, 0, 7);\n const labels: string[] = [];\n for (let i = 0; i < 7; i++) {\n labels.push(fmt.format(new Date(anchor.getTime() + i * MS_PER_DAY)));\n }\n return weekStartsOn === 1 ? [...labels.slice(1), labels[0]] : labels;\n}\n\n/**\n * Year-block start for the year view. 12-year blocks aligned to multiples of\n * `YEARS_PER_BLOCK` (e.g. 2024 → 2016–2027). Block alignment is the iOS pattern;\n * picking any other anchor (decades, etc.) is equally valid — this just keeps\n * navigation predictable.\n */\nfunction yearBlockStart(year: number): number {\n return Math.floor(year / YEARS_PER_BLOCK) * YEARS_PER_BLOCK;\n}\n\n/** True if any day of `year-month` overlaps the inclusive selection range. */\nfunction monthOverlapsSelection(year: number, monthIdx: number, value: CalendarValue): boolean {\n if (!value.start) return false;\n const monthFirstMs = new Date(year, monthIdx, 1).getTime();\n const monthLastMs = new Date(year, monthIdx + 1, 0).getTime();\n const sMs = startOfDay(value.start).getTime();\n const eMs = value.end ? startOfDay(value.end).getTime() : sMs;\n const lo = Math.min(sMs, eMs);\n const hi = Math.max(sMs, eMs);\n return monthFirstMs <= hi && monthLastMs >= lo;\n}\n\n/** True if `year` falls within the inclusive selection range's year span. */\nfunction yearOverlapsSelection(year: number, value: CalendarValue): boolean {\n if (!value.start) return false;\n const sY = value.start.getFullYear();\n const eY = value.end ? value.end.getFullYear() : sY;\n const lo = Math.min(sY, eY);\n const hi = Math.max(sY, eY);\n return year >= lo && year <= hi;\n}\n\nconst EMPTY_VALUE: CalendarValue = { start: null, end: null };\n\n/**\n * Default roving-tabindex target for the day grid: the selected day if\n * it's in the visible grid and enabled, else today if visible and\n * enabled, else the first enabled cell, else 0. Keeps the grid reachable\n * via Tab even when the \"obvious\" cell happens to be disabled.\n */\nfunction defaultDayFocusIndex(\n cells: { date: Date; outside: boolean }[],\n start: Date | null,\n today: Date,\n isCellDisabled: (date: Date) => boolean,\n): number {\n const startIndex = start ? cells.findIndex((c) => sameDay(c.date, start)) : -1;\n if (startIndex >= 0 && !isCellDisabled(cells[startIndex].date)) return startIndex;\n const todayIndex = cells.findIndex((c) => sameDay(c.date, today));\n if (todayIndex >= 0 && !isCellDisabled(cells[todayIndex].date)) return todayIndex;\n const firstEnabled = cells.findIndex((c) => !isCellDisabled(c.date));\n return firstEnabled >= 0 ? firstEnabled : 0;\n}\n\nexport function Calendar({\n month,\n value,\n today,\n isDisabled,\n weekStartsOn = 0,\n locale: localeProp,\n onChange,\n onMonthChange,\n shadow = true,\n previousMonthLabel = 'Previous',\n nextMonthLabel = 'Next',\n drillUpLabel = 'Drill up',\n className,\n ...rest\n}: CalendarProps) {\n const locale = useUxmLocale(localeProp);\n const realToday = useMemo(() => today ?? new Date(), [today]);\n\n // Month state — controlled when `month` prop is provided. When uncontrolled,\n // seed from the current selection so opening on an existing date lands on\n // that date's month, not today's. The popover consumers mount the calendar\n // fresh per open, so this initial `value` is the selected date; nav then\n // owns `internalMonth`. Falls back to today's month when nothing's selected.\n const [internalMonth, setInternalMonth] = useState<Date>(() => {\n // `end` covers the range-input edge case where only the closing half is\n // seeded (`{ start: null, end }`); today is the last resort.\n const seed = value?.start ?? value?.end ?? realToday;\n return new Date(seed.getFullYear(), seed.getMonth(), 1);\n });\n const currentMonth = month ?? internalMonth;\n\n // View state — pure UI state, never controlled. Day view by default. Header\n // click drills up (day → month → year). Cell click drills back down.\n const [view, setView] = useState<CalendarView>('day');\n\n // Unified selection model — atom infers single vs range from click sequence.\n const isControlled = value !== undefined;\n const [internalValue, setInternalValue] = useState<CalendarValue>(EMPTY_VALUE);\n const currentValue = isControlled ? value : internalValue;\n const currentStart = currentValue.start;\n const currentEnd = currentValue.end;\n\n // Hover preview — only meaningful in the in-flight window in day view.\n const [hoveredDate, setHoveredDate] = useState<Date | null>(null);\n const inFlight = !!currentStart && !currentEnd;\n const previewEnd = inFlight && hoveredDate ? hoveredDate : currentEnd;\n const isRangeView = !!currentStart && !!previewEnd && !sameDay(currentStart, previewEnd);\n\n const dayCells = useMemo(\n () => computeMonthGrid(currentMonth, weekStartsOn),\n [currentMonth, weekStartsOn],\n );\n\n const isCellDisabled = useCallback((date: Date) => isDisabled?.(date) ?? false, [isDisabled]);\n\n // Roving tabindex across the day grid (ARIA grid pattern): only one cell\n // is a Tab stop at a time; arrow keys move it. `dayRefs` lets the arrow\n // handler call `.focus()` on the target cell after moving the index.\n const dayRefs = useRef<Array<HTMLButtonElement | null>>([]);\n const [focusedDayIndex, setFocusedDayIndex] = useState<number>(() =>\n defaultDayFocusIndex(dayCells, currentStart, realToday, isCellDisabled),\n );\n\n const weekdays = useMemo(\n () => getWeekdayLabels(locale, weekStartsOn),\n [locale, weekStartsOn],\n );\n\n // Header label — depends on the active view.\n const titleLabel = useMemo(() => {\n if (view === 'day') {\n return new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }).format(currentMonth);\n }\n if (view === 'month') {\n return String(currentMonth.getFullYear());\n }\n const start = yearBlockStart(currentMonth.getFullYear());\n return `${start}–${start + YEARS_PER_BLOCK - 1}`;\n }, [view, currentMonth, locale]);\n\n // Month-view labels — short month names from the locale formatter.\n const monthShortNames = useMemo(() => {\n const fmt = new Intl.DateTimeFormat(locale, { month: 'short' });\n return Array.from({ length: 12 }, (_, i) => fmt.format(new Date(2024, i, 1)));\n }, [locale]);\n\n const setMonth = useCallback(\n (next: Date) => {\n if (month === undefined) setInternalMonth(next);\n onMonthChange?.(next);\n // Re-target the day grid's roving tab stop for the month we're\n // navigating to — the previous index otherwise points at an\n // unrelated cell (or none) in the new grid.\n const nextCells = computeMonthGrid(next, weekStartsOn);\n setFocusedDayIndex(defaultDayFocusIndex(nextCells, currentStart, realToday, isCellDisabled));\n },\n [month, onMonthChange, weekStartsOn, currentStart, realToday, isCellDisabled],\n );\n\n // Prev/next nav stepping is view-aware: day → ±1 month, month → ±1 year,\n // year → ±12 years (one block).\n const handleNav = useCallback(\n (delta: number) => {\n const m = currentMonth.getMonth();\n const y = currentMonth.getFullYear();\n let next: Date;\n if (view === 'day') {\n next = new Date(y, m + delta, 1);\n } else if (view === 'month') {\n next = new Date(y + delta, m, 1);\n } else {\n next = new Date(y + delta * YEARS_PER_BLOCK, m, 1);\n }\n setMonth(next);\n },\n [view, currentMonth, setMonth],\n );\n\n // Header click drills up. Year view has nowhere further to go.\n const handleTitleClick = useCallback(() => {\n if (view === 'day') setView('month');\n else if (view === 'month') setView('year');\n }, [view]);\n\n const commit = useCallback(\n (next: CalendarValue) => {\n if (!isControlled) setInternalValue(next);\n onChange?.(next);\n },\n [isControlled, onChange],\n );\n\n // Day-cell click — the unified single/range click logic.\n const handleDayClick = useCallback(\n (date: Date, outside: boolean, disabled: boolean) => {\n if (disabled) return;\n if (outside) {\n setMonth(new Date(date.getFullYear(), date.getMonth(), 1));\n }\n const startingNew = !currentStart || !!currentEnd;\n if (startingNew) {\n commit({ start: date, end: null });\n } else {\n const s = currentStart!;\n let finalStart = s;\n let finalEnd = date;\n if (startOfDay(date).getTime() < startOfDay(s).getTime()) {\n finalStart = date;\n finalEnd = s;\n }\n commit({ start: finalStart, end: finalEnd });\n }\n setHoveredDate(null);\n },\n [currentStart, currentEnd, commit, setMonth],\n );\n\n // Month-view click drills back to day view for the clicked month.\n const handleMonthClick = useCallback(\n (monthIdx: number) => {\n setMonth(new Date(currentMonth.getFullYear(), monthIdx, 1));\n setView('day');\n },\n [currentMonth, setMonth],\n );\n\n // Year-view click drills to month view for the clicked year, preserving\n // the previously-active month-of-year so the user doesn't lose context.\n const handleYearClick = useCallback(\n (year: number) => {\n setMonth(new Date(year, currentMonth.getMonth(), 1));\n setView('month');\n },\n [currentMonth, setMonth],\n );\n\n const handleMouseEnter = useCallback(\n (date: Date, disabled: boolean) => {\n if (disabled) return;\n if (inFlight) setHoveredDate(date);\n },\n [inFlight],\n );\n\n const handleMouseLeave = useCallback((date: Date) => {\n setHoveredDate((prev) => (sameDay(prev, date) ? null : prev));\n }, []);\n\n // ARIA grid keyboard navigation for the day view: ArrowLeft/Right move a\n // day, ArrowUp/Down move a week, Home/End jump to the start/end of the\n // current week row. Stays within the visible 6×7 grid (doesn't cross\n // into the previous/next month) — disabled cells are skipped so arrow\n // navigation never lands on (and gets stuck at) an inert cell.\n const handleDayKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>, i: number) => {\n const rowStart = i - (i % 7);\n let step = 0;\n let bound: number | null = null;\n switch (e.key) {\n case 'ArrowLeft':\n step = -1;\n break;\n case 'ArrowRight':\n step = 1;\n break;\n case 'ArrowUp':\n step = -7;\n break;\n case 'ArrowDown':\n step = 7;\n break;\n case 'Home':\n step = -1;\n bound = rowStart;\n break;\n case 'End':\n step = 1;\n bound = rowStart + 6;\n break;\n default:\n return;\n }\n e.preventDefault();\n let next = bound ?? i + step;\n while (\n next >= 0 &&\n next < dayCells.length &&\n isCellDisabled(dayCells[next].date)\n ) {\n next += step;\n }\n if (next < 0 || next >= dayCells.length) return;\n setFocusedDayIndex(next);\n dayRefs.current[next]?.focus();\n },\n [dayCells, isCellDisabled],\n );\n\n return (\n <div className={cn('uxm-calendar', `uxm-calendar--view-${view}`, shadow && 'uxm-calendar--shadow', className)} {...rest}>\n <div className=\"uxm-calendar__header\">\n <button\n type=\"button\"\n className=\"uxm-calendar__nav\"\n onClick={() => handleNav(-1)}\n aria-label={previousMonthLabel}\n >\n <Icon glyph=\"chevron-left\" size={16} />\n </button>\n <button\n type=\"button\"\n className=\"uxm-calendar__title\"\n onClick={handleTitleClick}\n disabled={view === 'year'}\n aria-label={view === 'year' ? undefined : drillUpLabel}\n >\n {titleLabel}\n </button>\n <button\n type=\"button\"\n className=\"uxm-calendar__nav\"\n onClick={() => handleNav(1)}\n aria-label={nextMonthLabel}\n >\n <Icon glyph=\"chevron-right\" size={16} />\n </button>\n </div>\n\n {view === 'day' && (\n <>\n <div className=\"uxm-calendar__weekdays\" aria-hidden=\"true\">\n {weekdays.map((w) => (\n <div key={w} className=\"uxm-calendar__weekday\">{w}</div>\n ))}\n </div>\n <div className=\"uxm-calendar__grid\" role=\"grid\">\n {Array.from({ length: dayCells.length / 7 }, (_, week) => (\n // `display: contents` (see calendar.scss) keeps these cells as\n // direct participants in the parent's CSS grid while giving\n // each week its own `role=\"row\"` — the ARIA grid pattern\n // requires gridcells to be grouped into rows.\n <div key={week} role=\"row\" className=\"uxm-calendar__grid-row\">\n {dayCells.slice(week * 7, week * 7 + 7).map(({ date, outside }, col) => {\n const i = week * 7 + col;\n const isToday = sameDay(date, realToday);\n const isStart = sameDay(date, currentStart);\n const isEnd = !!previewEnd && sameDay(date, previewEnd);\n const isSelected = !isRangeView && isStart;\n const isRangeStart = isRangeView && isStart;\n const isRangeEnd = isRangeView && isEnd;\n const isInRange = isStrictlyBetween(date, currentStart, previewEnd);\n const disabled = isDisabled?.(date) ?? false;\n return (\n <button\n key={i}\n ref={(el) => {\n dayRefs.current[i] = el;\n }}\n type=\"button\"\n role=\"gridcell\"\n disabled={disabled}\n tabIndex={i === focusedDayIndex ? 0 : -1}\n onClick={() => {\n handleDayClick(date, outside, disabled);\n if (!disabled && !outside) setFocusedDayIndex(i);\n }}\n onKeyDown={(e) => handleDayKeyDown(e, i)}\n onMouseEnter={() => handleMouseEnter(date, disabled)}\n onMouseLeave={() => handleMouseLeave(date)}\n className={cn(\n 'uxm-calendar__day',\n outside && 'uxm-calendar__day--outside',\n isToday && 'uxm-calendar__day--today',\n isSelected && 'uxm-calendar__day--selected',\n isRangeStart && 'uxm-calendar__day--range-start',\n isRangeEnd && 'uxm-calendar__day--range-end',\n isInRange && 'uxm-calendar__day--in-range',\n disabled && 'uxm-calendar__day--disabled',\n )}\n aria-selected={isSelected || isRangeStart || isRangeEnd || undefined}\n aria-current={isToday ? 'date' : undefined}\n >\n {date.getDate()}\n </button>\n );\n })}\n </div>\n ))}\n </div>\n </>\n )}\n\n {view === 'month' && (\n <div className=\"uxm-calendar__months\" role=\"grid\">\n {monthShortNames.map((name, i) => {\n const year = currentMonth.getFullYear();\n const isCurrent = realToday.getFullYear() === year && realToday.getMonth() === i;\n const isSelected = monthOverlapsSelection(year, i, currentValue);\n return (\n <button\n key={i}\n type=\"button\"\n role=\"gridcell\"\n onClick={() => handleMonthClick(i)}\n className={cn(\n 'uxm-calendar__month',\n isCurrent && 'uxm-calendar__month--today',\n isSelected && 'uxm-calendar__month--selected',\n )}\n aria-selected={isSelected || undefined}\n aria-current={isCurrent ? 'true' : undefined}\n >\n {name}\n </button>\n );\n })}\n </div>\n )}\n\n {view === 'year' && (\n <div className=\"uxm-calendar__years\" role=\"grid\">\n {Array.from({ length: YEARS_PER_BLOCK }, (_, i) => {\n const year = yearBlockStart(currentMonth.getFullYear()) + i;\n const isCurrent = realToday.getFullYear() === year;\n const isSelected = yearOverlapsSelection(year, currentValue);\n return (\n <button\n key={year}\n type=\"button\"\n role=\"gridcell\"\n onClick={() => handleYearClick(year)}\n className={cn(\n 'uxm-calendar__year',\n isCurrent && 'uxm-calendar__year--today',\n isSelected && 'uxm-calendar__year--selected',\n )}\n aria-selected={isSelected || undefined}\n aria-current={isCurrent ? 'true' : undefined}\n >\n {year}\n </button>\n );\n })}\n </div>\n )}\n\n {/* \"Today\" — jumps the view back to the current month in day view so\n the user can find today after navigating away. Deliberately does\n NOT select today (returning to a date ≠ committing it); the day\n cell is one click away. */}\n <div className=\"uxm-calendar__footer\">\n <button\n type=\"button\"\n className=\"uxm-calendar__today-button\"\n disabled={isDisabled?.(realToday)}\n onClick={() => {\n setView('day');\n setMonth(new Date(realToday.getFullYear(), realToday.getMonth(), 1));\n }}\n >\n Today\n </button>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAoXM;AApXN,mBAAuD;AAEvD,qBAAmB;AAEnB,2BAAiD;AACjD,kBAAqB;AACrB,oBAA6B;AA0C7B,MAAM,aAAa;AACnB,MAAM,kBAAkB;AAExB,SAAS,WAAW,GAAe;AACjC,SAAO,IAAI,KAAK,EAAE,YAAY,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ,CAAC;AAC5D;AACA,SAAS,QAAQ,GAA4B,GAAqC;AAChF,MAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AACrB,SAAO,WAAW,CAAC,EAAE,QAAQ,MAAM,WAAW,CAAC,EAAE,QAAQ;AAC3D;AACA,SAAS,kBAAkB,MAAY,OAAgC,KAAuC;AAC5G,MAAI,CAAC,SAAS,CAAC,IAAK,QAAO;AAC3B,QAAM,IAAI,WAAW,IAAI,EAAE,QAAQ;AACnC,QAAM,KAAK,KAAK,IAAI,WAAW,KAAK,EAAE,QAAQ,GAAG,WAAW,GAAG,EAAE,QAAQ,CAAC;AAC1E,QAAM,KAAK,KAAK,IAAI,WAAW,KAAK,EAAE,QAAQ,GAAG,WAAW,GAAG,EAAE,QAAQ,CAAC;AAC1E,SAAO,IAAI,MAAM,IAAI;AACvB;AAEA,SAAS,iBAAiB,QAAgB,cAAmC;AAC3E,QAAM,MAAM,IAAI,KAAK,eAAe,QAAQ,EAAE,SAAS,QAAQ,CAAC;AAEhE,QAAM,SAAS,IAAI,KAAK,MAAM,GAAG,CAAC;AAClC,QAAM,SAAmB,CAAC;AAC1B,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,WAAO,KAAK,IAAI,OAAO,IAAI,KAAK,OAAO,QAAQ,IAAI,IAAI,UAAU,CAAC,CAAC;AAAA,EACrE;AACA,SAAO,iBAAiB,IAAI,CAAC,GAAG,OAAO,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI;AAChE;AAQA,SAAS,eAAe,MAAsB;AAC5C,SAAO,KAAK,MAAM,OAAO,eAAe,IAAI;AAC9C;AAGA,SAAS,uBAAuB,MAAc,UAAkB,OAA+B;AAC7F,MAAI,CAAC,MAAM,MAAO,QAAO;AACzB,QAAM,eAAe,IAAI,KAAK,MAAM,UAAU,CAAC,EAAE,QAAQ;AACzD,QAAM,cAAc,IAAI,KAAK,MAAM,WAAW,GAAG,CAAC,EAAE,QAAQ;AAC5D,QAAM,MAAM,WAAW,MAAM,KAAK,EAAE,QAAQ;AAC5C,QAAM,MAAM,MAAM,MAAM,WAAW,MAAM,GAAG,EAAE,QAAQ,IAAI;AAC1D,QAAM,KAAK,KAAK,IAAI,KAAK,GAAG;AAC5B,QAAM,KAAK,KAAK,IAAI,KAAK,GAAG;AAC5B,SAAO,gBAAgB,MAAM,eAAe;AAC9C;AAGA,SAAS,sBAAsB,MAAc,OAA+B;AAC1E,MAAI,CAAC,MAAM,MAAO,QAAO;AACzB,QAAM,KAAK,MAAM,MAAM,YAAY;AACnC,QAAM,KAAK,MAAM,MAAM,MAAM,IAAI,YAAY,IAAI;AACjD,QAAM,KAAK,KAAK,IAAI,IAAI,EAAE;AAC1B,QAAM,KAAK,KAAK,IAAI,IAAI,EAAE;AAC1B,SAAO,QAAQ,MAAM,QAAQ;AAC/B;AAEA,MAAM,cAA6B,EAAE,OAAO,MAAM,KAAK,KAAK;AAQ5D,SAAS,qBACP,OACA,OACA,OACA,gBACQ;AACR,QAAM,aAAa,QAAQ,MAAM,UAAU,CAAC,MAAM,QAAQ,EAAE,MAAM,KAAK,CAAC,IAAI;AAC5E,MAAI,cAAc,KAAK,CAAC,eAAe,MAAM,UAAU,EAAE,IAAI,EAAG,QAAO;AACvE,QAAM,aAAa,MAAM,UAAU,CAAC,MAAM,QAAQ,EAAE,MAAM,KAAK,CAAC;AAChE,MAAI,cAAc,KAAK,CAAC,eAAe,MAAM,UAAU,EAAE,IAAI,EAAG,QAAO;AACvE,QAAM,eAAe,MAAM,UAAU,CAAC,MAAM,CAAC,eAAe,EAAE,IAAI,CAAC;AACnE,SAAO,gBAAgB,IAAI,eAAe;AAC5C;AAEO,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf;AAAA,EACA,GAAG;AACL,GAAkB;AAChB,QAAM,aAAS,4BAAa,UAAU;AACtC,QAAM,gBAAY,sBAAQ,MAAM,SAAS,oBAAI,KAAK,GAAG,CAAC,KAAK,CAAC;AAO5D,QAAM,CAAC,eAAe,gBAAgB,QAAI,uBAAe,MAAM;AAG7D,UAAM,OAAO,OAAO,SAAS,OAAO,OAAO;AAC3C,WAAO,IAAI,KAAK,KAAK,YAAY,GAAG,KAAK,SAAS,GAAG,CAAC;AAAA,EACxD,CAAC;AACD,QAAM,eAAe,SAAS;AAI9B,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAuB,KAAK;AAGpD,QAAM,eAAe,UAAU;AAC/B,QAAM,CAAC,eAAe,gBAAgB,QAAI,uBAAwB,WAAW;AAC7E,QAAM,eAAe,eAAe,QAAQ;AAC5C,QAAM,eAAe,aAAa;AAClC,QAAM,aAAa,aAAa;AAGhC,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAsB,IAAI;AAChE,QAAM,WAAW,CAAC,CAAC,gBAAgB,CAAC;AACpC,QAAM,aAAa,YAAY,cAAc,cAAc;AAC3D,QAAM,cAAc,CAAC,CAAC,gBAAgB,CAAC,CAAC,cAAc,CAAC,QAAQ,cAAc,UAAU;AAEvF,QAAM,eAAW;AAAA,IACf,UAAM,uCAAiB,cAAc,YAAY;AAAA,IACjD,CAAC,cAAc,YAAY;AAAA,EAC7B;AAEA,QAAM,qBAAiB,0BAAY,CAAC,SAAe,aAAa,IAAI,KAAK,OAAO,CAAC,UAAU,CAAC;AAK5F,QAAM,cAAU,qBAAwC,CAAC,CAAC;AAC1D,QAAM,CAAC,iBAAiB,kBAAkB,QAAI;AAAA,IAAiB,MAC7D,qBAAqB,UAAU,cAAc,WAAW,cAAc;AAAA,EACxE;AAEA,QAAM,eAAW;AAAA,IACf,MAAM,iBAAiB,QAAQ,YAAY;AAAA,IAC3C,CAAC,QAAQ,YAAY;AAAA,EACvB;AAGA,QAAM,iBAAa,sBAAQ,MAAM;AAC/B,QAAI,SAAS,OAAO;AAClB,aAAO,IAAI,KAAK,eAAe,QAAQ,EAAE,OAAO,QAAQ,MAAM,UAAU,CAAC,EAAE,OAAO,YAAY;AAAA,IAChG;AACA,QAAI,SAAS,SAAS;AACpB,aAAO,OAAO,aAAa,YAAY,CAAC;AAAA,IAC1C;AACA,UAAM,QAAQ,eAAe,aAAa,YAAY,CAAC;AACvD,WAAO,GAAG,KAAK,SAAI,QAAQ,kBAAkB,CAAC;AAAA,EAChD,GAAG,CAAC,MAAM,cAAc,MAAM,CAAC;AAG/B,QAAM,sBAAkB,sBAAQ,MAAM;AACpC,UAAM,MAAM,IAAI,KAAK,eAAe,QAAQ,EAAE,OAAO,QAAQ,CAAC;AAC9D,WAAO,MAAM,KAAK,EAAE,QAAQ,GAAG,GAAG,CAAC,GAAG,MAAM,IAAI,OAAO,IAAI,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC;AAAA,EAC9E,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,eAAW;AAAA,IACf,CAAC,SAAe;AACd,UAAI,UAAU,OAAW,kBAAiB,IAAI;AAC9C,sBAAgB,IAAI;AAIpB,YAAM,gBAAY,uCAAiB,MAAM,YAAY;AACrD,yBAAmB,qBAAqB,WAAW,cAAc,WAAW,cAAc,CAAC;AAAA,IAC7F;AAAA,IACA,CAAC,OAAO,eAAe,cAAc,cAAc,WAAW,cAAc;AAAA,EAC9E;AAIA,QAAM,gBAAY;AAAA,IAChB,CAAC,UAAkB;AACjB,YAAM,IAAI,aAAa,SAAS;AAChC,YAAM,IAAI,aAAa,YAAY;AACnC,UAAI;AACJ,UAAI,SAAS,OAAO;AAClB,eAAO,IAAI,KAAK,GAAG,IAAI,OAAO,CAAC;AAAA,MACjC,WAAW,SAAS,SAAS;AAC3B,eAAO,IAAI,KAAK,IAAI,OAAO,GAAG,CAAC;AAAA,MACjC,OAAO;AACL,eAAO,IAAI,KAAK,IAAI,QAAQ,iBAAiB,GAAG,CAAC;AAAA,MACnD;AACA,eAAS,IAAI;AAAA,IACf;AAAA,IACA,CAAC,MAAM,cAAc,QAAQ;AAAA,EAC/B;AAGA,QAAM,uBAAmB,0BAAY,MAAM;AACzC,QAAI,SAAS,MAAO,SAAQ,OAAO;AAAA,aAC1B,SAAS,QAAS,SAAQ,MAAM;AAAA,EAC3C,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,aAAS;AAAA,IACb,CAAC,SAAwB;AACvB,UAAI,CAAC,aAAc,kBAAiB,IAAI;AACxC,iBAAW,IAAI;AAAA,IACjB;AAAA,IACA,CAAC,cAAc,QAAQ;AAAA,EACzB;AAGA,QAAM,qBAAiB;AAAA,IACrB,CAAC,MAAY,SAAkB,aAAsB;AACnD,UAAI,SAAU;AACd,UAAI,SAAS;AACX,iBAAS,IAAI,KAAK,KAAK,YAAY,GAAG,KAAK,SAAS,GAAG,CAAC,CAAC;AAAA,MAC3D;AACA,YAAM,cAAc,CAAC,gBAAgB,CAAC,CAAC;AACvC,UAAI,aAAa;AACf,eAAO,EAAE,OAAO,MAAM,KAAK,KAAK,CAAC;AAAA,MACnC,OAAO;AACL,cAAM,IAAI;AACV,YAAI,aAAa;AACjB,YAAI,WAAW;AACf,YAAI,WAAW,IAAI,EAAE,QAAQ,IAAI,WAAW,CAAC,EAAE,QAAQ,GAAG;AACxD,uBAAa;AACb,qBAAW;AAAA,QACb;AACA,eAAO,EAAE,OAAO,YAAY,KAAK,SAAS,CAAC;AAAA,MAC7C;AACA,qBAAe,IAAI;AAAA,IACrB;AAAA,IACA,CAAC,cAAc,YAAY,QAAQ,QAAQ;AAAA,EAC7C;AAGA,QAAM,uBAAmB;AAAA,IACvB,CAAC,aAAqB;AACpB,eAAS,IAAI,KAAK,aAAa,YAAY,GAAG,UAAU,CAAC,CAAC;AAC1D,cAAQ,KAAK;AAAA,IACf;AAAA,IACA,CAAC,cAAc,QAAQ;AAAA,EACzB;AAIA,QAAM,sBAAkB;AAAA,IACtB,CAAC,SAAiB;AAChB,eAAS,IAAI,KAAK,MAAM,aAAa,SAAS,GAAG,CAAC,CAAC;AACnD,cAAQ,OAAO;AAAA,IACjB;AAAA,IACA,CAAC,cAAc,QAAQ;AAAA,EACzB;AAEA,QAAM,uBAAmB;AAAA,IACvB,CAAC,MAAY,aAAsB;AACjC,UAAI,SAAU;AACd,UAAI,SAAU,gBAAe,IAAI;AAAA,IACnC;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,uBAAmB,0BAAY,CAAC,SAAe;AACnD,mBAAe,CAAC,SAAU,QAAQ,MAAM,IAAI,IAAI,OAAO,IAAK;AAAA,EAC9D,GAAG,CAAC,CAAC;AAOL,QAAM,uBAAmB;AAAA,IACvB,CAAC,GAAqC,MAAc;AAClD,YAAM,WAAW,IAAK,IAAI;AAC1B,UAAI,OAAO;AACX,UAAI,QAAuB;AAC3B,cAAQ,EAAE,KAAK;AAAA,QACb,KAAK;AACH,iBAAO;AACP;AAAA,QACF,KAAK;AACH,iBAAO;AACP;AAAA,QACF,KAAK;AACH,iBAAO;AACP;AAAA,QACF,KAAK;AACH,iBAAO;AACP;AAAA,QACF,KAAK;AACH,iBAAO;AACP,kBAAQ;AACR;AAAA,QACF,KAAK;AACH,iBAAO;AACP,kBAAQ,WAAW;AACnB;AAAA,QACF;AACE;AAAA,MACJ;AACA,QAAE,eAAe;AACjB,UAAI,OAAO,SAAS,IAAI;AACxB,aACE,QAAQ,KACR,OAAO,SAAS,UAChB,eAAe,SAAS,IAAI,EAAE,IAAI,GAClC;AACA,gBAAQ;AAAA,MACV;AACA,UAAI,OAAO,KAAK,QAAQ,SAAS,OAAQ;AACzC,yBAAmB,IAAI;AACvB,cAAQ,QAAQ,IAAI,GAAG,MAAM;AAAA,IAC/B;AAAA,IACA,CAAC,UAAU,cAAc;AAAA,EAC3B;AAEA,SACE,6CAAC,SAAI,eAAW,mBAAG,gBAAgB,sBAAsB,IAAI,IAAI,UAAU,wBAAwB,SAAS,GAAI,GAAG,MACjH;AAAA,iDAAC,SAAI,WAAU,wBACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,MAAM,UAAU,EAAE;AAAA,UAC3B,cAAY;AAAA,UAEZ,sDAAC,oBAAK,OAAM,gBAAe,MAAM,IAAI;AAAA;AAAA,MACvC;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS;AAAA,UACT,UAAU,SAAS;AAAA,UACnB,cAAY,SAAS,SAAS,SAAY;AAAA,UAEzC;AAAA;AAAA,MACH;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,MAAM,UAAU,CAAC;AAAA,UAC1B,cAAY;AAAA,UAEZ,sDAAC,oBAAK,OAAM,iBAAgB,MAAM,IAAI;AAAA;AAAA,MACxC;AAAA,OACF;AAAA,IAEC,SAAS,SACR,4EACE;AAAA,kDAAC,SAAI,WAAU,0BAAyB,eAAY,QACjD,mBAAS,IAAI,CAAC,MACb,4CAAC,SAAY,WAAU,yBAAyB,eAAtC,CAAwC,CACnD,GACH;AAAA,MACA,4CAAC,SAAI,WAAU,sBAAqB,MAAK,QACtC,gBAAM,KAAK,EAAE,QAAQ,SAAS,SAAS,EAAE,GAAG,CAAC,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA,QAK/C,4CAAC,SAAe,MAAK,OAAM,WAAU,0BAClC,mBAAS,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,QAAQ,GAAG,QAAQ;AACtE,gBAAM,IAAI,OAAO,IAAI;AACrB,gBAAM,UAAU,QAAQ,MAAM,SAAS;AACvC,gBAAM,UAAU,QAAQ,MAAM,YAAY;AAC1C,gBAAM,QAAQ,CAAC,CAAC,cAAc,QAAQ,MAAM,UAAU;AACtD,gBAAM,aAAa,CAAC,eAAe;AACnC,gBAAM,eAAe,eAAe;AACpC,gBAAM,aAAa,eAAe;AAClC,gBAAM,YAAY,kBAAkB,MAAM,cAAc,UAAU;AAClE,gBAAM,WAAW,aAAa,IAAI,KAAK;AACvC,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,KAAK,CAAC,OAAO;AACX,wBAAQ,QAAQ,CAAC,IAAI;AAAA,cACvB;AAAA,cACA,MAAK;AAAA,cACL,MAAK;AAAA,cACL;AAAA,cACA,UAAU,MAAM,kBAAkB,IAAI;AAAA,cACtC,SAAS,MAAM;AACb,+BAAe,MAAM,SAAS,QAAQ;AACtC,oBAAI,CAAC,YAAY,CAAC,QAAS,oBAAmB,CAAC;AAAA,cACjD;AAAA,cACA,WAAW,CAAC,MAAM,iBAAiB,GAAG,CAAC;AAAA,cACvC,cAAc,MAAM,iBAAiB,MAAM,QAAQ;AAAA,cACnD,cAAc,MAAM,iBAAiB,IAAI;AAAA,cACzC,eAAW;AAAA,gBACT;AAAA,gBACA,WAAW;AAAA,gBACX,WAAW;AAAA,gBACX,cAAc;AAAA,gBACd,gBAAgB;AAAA,gBAChB,cAAc;AAAA,gBACd,aAAa;AAAA,gBACb,YAAY;AAAA,cACd;AAAA,cACA,iBAAe,cAAc,gBAAgB,cAAc;AAAA,cAC3D,gBAAc,UAAU,SAAS;AAAA,cAEhC,eAAK,QAAQ;AAAA;AAAA,YA5BT;AAAA,UA6BP;AAAA,QAEJ,CAAC,KA5CO,IA6CV;AAAA,OACD,GACH;AAAA,OACF;AAAA,IAGD,SAAS,WACR,4CAAC,SAAI,WAAU,wBAAuB,MAAK,QACxC,0BAAgB,IAAI,CAAC,MAAM,MAAM;AAChC,YAAM,OAAO,aAAa,YAAY;AACtC,YAAM,YAAY,UAAU,YAAY,MAAM,QAAQ,UAAU,SAAS,MAAM;AAC/E,YAAM,aAAa,uBAAuB,MAAM,GAAG,YAAY;AAC/D,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,MAAK;AAAA,UACL,MAAK;AAAA,UACL,SAAS,MAAM,iBAAiB,CAAC;AAAA,UACjC,eAAW;AAAA,YACT;AAAA,YACA,aAAa;AAAA,YACb,cAAc;AAAA,UAChB;AAAA,UACA,iBAAe,cAAc;AAAA,UAC7B,gBAAc,YAAY,SAAS;AAAA,UAElC;AAAA;AAAA,QAZI;AAAA,MAaP;AAAA,IAEJ,CAAC,GACH;AAAA,IAGD,SAAS,UACR,4CAAC,SAAI,WAAU,uBAAsB,MAAK,QACvC,gBAAM,KAAK,EAAE,QAAQ,gBAAgB,GAAG,CAAC,GAAG,MAAM;AACjD,YAAM,OAAO,eAAe,aAAa,YAAY,CAAC,IAAI;AAC1D,YAAM,YAAY,UAAU,YAAY,MAAM;AAC9C,YAAM,aAAa,sBAAsB,MAAM,YAAY;AAC3D,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,MAAK;AAAA,UACL,MAAK;AAAA,UACL,SAAS,MAAM,gBAAgB,IAAI;AAAA,UACnC,eAAW;AAAA,YACT;AAAA,YACA,aAAa;AAAA,YACb,cAAc;AAAA,UAChB;AAAA,UACA,iBAAe,cAAc;AAAA,UAC7B,gBAAc,YAAY,SAAS;AAAA,UAElC;AAAA;AAAA,QAZI;AAAA,MAaP;AAAA,IAEJ,CAAC,GACH;AAAA,IAOF,4CAAC,SAAI,WAAU,wBACb;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QACV,UAAU,aAAa,SAAS;AAAA,QAChC,SAAS,MAAM;AACb,kBAAQ,KAAK;AACb,mBAAS,IAAI,KAAK,UAAU,YAAY,GAAG,UAAU,SAAS,GAAG,CAAC,CAAC;AAAA,QACrE;AAAA,QACD;AAAA;AAAA,IAED,GACF;AAAA,KACF;AAEJ;","names":[]}
@@ -15,7 +15,11 @@ export interface CalendarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onC
15
15
  isDisabled?: (date: Date) => boolean;
16
16
  /** 0 = Sunday (US), 1 = Monday. */
17
17
  weekStartsOn?: WeekStart;
18
- /** Locale for month label and weekday names. */
18
+ /**
19
+ * BCP-47 locale for the month label and weekday names. Defaults to the
20
+ * nearest `UxmLocaleProvider`, then to `"en-US"`. Set it here only to pin
21
+ * one calendar to a locale that differs from the rest of the app.
22
+ */
19
23
  locale?: string;
20
24
  /** Fires after every click. `end` is null after the first click of a new range, set after the second. */
21
25
  onChange?: (value: CalendarValue) => void;
@@ -30,5 +34,5 @@ export interface CalendarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onC
30
34
  /** Accessible name for the month/year drill-up button. Default `"Drill up"`. */
31
35
  drillUpLabel?: string;
32
36
  }
33
- export declare function Calendar({ month, value, today, isDisabled, weekStartsOn, locale, onChange, onMonthChange, shadow, previousMonthLabel, nextMonthLabel, drillUpLabel, className, ...rest }: CalendarProps): import("react/jsx-runtime").JSX.Element;
37
+ export declare function Calendar({ month, value, today, isDisabled, weekStartsOn, locale: localeProp, onChange, onMonthChange, shadow, previousMonthLabel, nextMonthLabel, drillUpLabel, className, ...rest }: CalendarProps): import("react/jsx-runtime").JSX.Element;
34
38
  //# sourceMappingURL=calendar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../../src/ui/calendar/calendar.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAG3E,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,OAAO,CAAC;AAE3D,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,GAAG,EAAE,IAAI,GAAG,IAAI,CAAC;CAClB;AAID,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC;IACrF,iIAAiI;IACjI,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,uHAAuH;IACvH,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,iEAAiE;IACjE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;IACrC,mCAAmC;IACnC,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yGAAyG;IACzG,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,iHAAiH;IACjH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACrC,oIAAoI;IACpI,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAqFD,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,KAAK,EACL,KAAK,EACL,UAAU,EACV,YAAgB,EAChB,MAAgB,EAChB,QAAQ,EACR,aAAa,EACb,MAAa,EACb,kBAA+B,EAC/B,cAAuB,EACvB,YAAyB,EACzB,SAAS,EACT,GAAG,IAAI,EACR,EAAE,aAAa,2CAsYf"}
1
+ {"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../../src/ui/calendar/calendar.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAI3E,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,OAAO,CAAC;AAE3D,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,GAAG,EAAE,IAAI,GAAG,IAAI,CAAC;CAClB;AAID,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC;IACrF,iIAAiI;IACjI,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,uHAAuH;IACvH,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,iEAAiE;IACjE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;IACrC,mCAAmC;IACnC,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yGAAyG;IACzG,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,iHAAiH;IACjH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACrC,oIAAoI;IACpI,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAqFD,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,KAAK,EACL,KAAK,EACL,UAAU,EACV,YAAgB,EAChB,MAAM,EAAE,UAAU,EAClB,QAAQ,EACR,aAAa,EACb,MAAa,EACb,kBAA+B,EAC/B,cAAuB,EACvB,YAAyB,EACzB,SAAS,EACT,GAAG,IAAI,EACR,EAAE,aAAa,2CAuYf"}
@@ -3,6 +3,7 @@ import { useCallback, useMemo, useRef, useState } from "react";
3
3
  import { cn } from "../../helpers/index.js";
4
4
  import { computeMonthGrid } from "../../lib/calendar-grid.js";
5
5
  import { Icon } from "../icon/index.js";
6
+ import { useUxmLocale } from "../locale/index.js";
6
7
  const MS_PER_DAY = 864e5;
7
8
  const YEARS_PER_BLOCK = 12;
8
9
  function startOfDay(d) {
@@ -64,7 +65,7 @@ function Calendar({
64
65
  today,
65
66
  isDisabled,
66
67
  weekStartsOn = 0,
67
- locale = "en-US",
68
+ locale: localeProp,
68
69
  onChange,
69
70
  onMonthChange,
70
71
  shadow = true,
@@ -74,6 +75,7 @@ function Calendar({
74
75
  className,
75
76
  ...rest
76
77
  }) {
78
+ const locale = useUxmLocale(localeProp);
77
79
  const realToday = useMemo(() => today ?? /* @__PURE__ */ new Date(), [today]);
78
80
  const [internalMonth, setInternalMonth] = useState(() => {
79
81
  const seed = value?.start ?? value?.end ?? realToday;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/calendar/calendar.tsx"],"sourcesContent":["import { useCallback, useMemo, useRef, useState } from 'react';\n\nimport { cn } from '@/helpers';\n\nimport { computeMonthGrid, type WeekStart } from '../../lib/calendar-grid';\nimport { Icon } from '../icon';\n\nimport type { HTMLAttributes, KeyboardEvent } from 'react';\n\nexport interface CalendarValue {\n start: Date | null;\n end: Date | null;\n}\n\ntype CalendarView = 'day' | 'month' | 'year';\n\nexport interface CalendarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {\n /** Currently-displayed month. Controlled. If omitted, atom owns it (initialized to the selected value's month, else today's). */\n month?: Date;\n /** Selection value. Controlled when passed. `{ start, end: null }` for a single date; `{ start, end }` for a range. */\n value?: CalendarValue;\n /** Override for \"today\". Defaults to real current date. */\n today?: Date;\n /** Predicate for disabled days. Disabled cells ignore clicks. */\n isDisabled?: (date: Date) => boolean;\n /** 0 = Sunday (US), 1 = Monday. */\n weekStartsOn?: WeekStart;\n /** Locale for month label and weekday names. */\n locale?: string;\n /** Fires after every click. `end` is null after the first click of a new range, set after the second. */\n onChange?: (value: CalendarValue) => void;\n /** Month navigation callback (prev/next buttons, outside-month-cell click, or drilling via month/year views). */\n onMonthChange?: (next: Date) => void;\n /** Drop a soft elevation shadow (the floating-surface look). Default true — pass `false` for a flat, inline / embedded calendar. */\n shadow?: boolean;\n /** Accessible name for the previous-month button. Default `\"Previous\"`. */\n previousMonthLabel?: string;\n /** Accessible name for the next-month button. Default `\"Next\"`. */\n nextMonthLabel?: string;\n /** Accessible name for the month/year drill-up button. Default `\"Drill up\"`. */\n drillUpLabel?: string;\n}\n\nconst MS_PER_DAY = 86_400_000;\nconst YEARS_PER_BLOCK = 12;\n\nfunction startOfDay(d: Date): Date {\n return new Date(d.getFullYear(), d.getMonth(), d.getDate());\n}\nfunction sameDay(a: Date | null | undefined, b: Date | null | undefined): boolean {\n if (!a || !b) return false;\n return startOfDay(a).getTime() === startOfDay(b).getTime();\n}\nfunction isStrictlyBetween(date: Date, start: Date | null | undefined, end: Date | null | undefined): boolean {\n if (!start || !end) return false;\n const t = startOfDay(date).getTime();\n const lo = Math.min(startOfDay(start).getTime(), startOfDay(end).getTime());\n const hi = Math.max(startOfDay(start).getTime(), startOfDay(end).getTime());\n return t > lo && t < hi;\n}\n\nfunction getWeekdayLabels(locale: string, weekStartsOn: WeekStart): string[] {\n const fmt = new Intl.DateTimeFormat(locale, { weekday: 'short' });\n // 2024-01-07 is a known Sunday — stable anchor for the formatter.\n const anchor = new Date(2024, 0, 7);\n const labels: string[] = [];\n for (let i = 0; i < 7; i++) {\n labels.push(fmt.format(new Date(anchor.getTime() + i * MS_PER_DAY)));\n }\n return weekStartsOn === 1 ? [...labels.slice(1), labels[0]] : labels;\n}\n\n/**\n * Year-block start for the year view. 12-year blocks aligned to multiples of\n * `YEARS_PER_BLOCK` (e.g. 2024 → 2016–2027). Block alignment is the iOS pattern;\n * picking any other anchor (decades, etc.) is equally valid — this just keeps\n * navigation predictable.\n */\nfunction yearBlockStart(year: number): number {\n return Math.floor(year / YEARS_PER_BLOCK) * YEARS_PER_BLOCK;\n}\n\n/** True if any day of `year-month` overlaps the inclusive selection range. */\nfunction monthOverlapsSelection(year: number, monthIdx: number, value: CalendarValue): boolean {\n if (!value.start) return false;\n const monthFirstMs = new Date(year, monthIdx, 1).getTime();\n const monthLastMs = new Date(year, monthIdx + 1, 0).getTime();\n const sMs = startOfDay(value.start).getTime();\n const eMs = value.end ? startOfDay(value.end).getTime() : sMs;\n const lo = Math.min(sMs, eMs);\n const hi = Math.max(sMs, eMs);\n return monthFirstMs <= hi && monthLastMs >= lo;\n}\n\n/** True if `year` falls within the inclusive selection range's year span. */\nfunction yearOverlapsSelection(year: number, value: CalendarValue): boolean {\n if (!value.start) return false;\n const sY = value.start.getFullYear();\n const eY = value.end ? value.end.getFullYear() : sY;\n const lo = Math.min(sY, eY);\n const hi = Math.max(sY, eY);\n return year >= lo && year <= hi;\n}\n\nconst EMPTY_VALUE: CalendarValue = { start: null, end: null };\n\n/**\n * Default roving-tabindex target for the day grid: the selected day if\n * it's in the visible grid and enabled, else today if visible and\n * enabled, else the first enabled cell, else 0. Keeps the grid reachable\n * via Tab even when the \"obvious\" cell happens to be disabled.\n */\nfunction defaultDayFocusIndex(\n cells: { date: Date; outside: boolean }[],\n start: Date | null,\n today: Date,\n isCellDisabled: (date: Date) => boolean,\n): number {\n const startIndex = start ? cells.findIndex((c) => sameDay(c.date, start)) : -1;\n if (startIndex >= 0 && !isCellDisabled(cells[startIndex].date)) return startIndex;\n const todayIndex = cells.findIndex((c) => sameDay(c.date, today));\n if (todayIndex >= 0 && !isCellDisabled(cells[todayIndex].date)) return todayIndex;\n const firstEnabled = cells.findIndex((c) => !isCellDisabled(c.date));\n return firstEnabled >= 0 ? firstEnabled : 0;\n}\n\nexport function Calendar({\n month,\n value,\n today,\n isDisabled,\n weekStartsOn = 0,\n locale = 'en-US',\n onChange,\n onMonthChange,\n shadow = true,\n previousMonthLabel = 'Previous',\n nextMonthLabel = 'Next',\n drillUpLabel = 'Drill up',\n className,\n ...rest\n}: CalendarProps) {\n const realToday = useMemo(() => today ?? new Date(), [today]);\n\n // Month state — controlled when `month` prop is provided. When uncontrolled,\n // seed from the current selection so opening on an existing date lands on\n // that date's month, not today's. The popover consumers mount the calendar\n // fresh per open, so this initial `value` is the selected date; nav then\n // owns `internalMonth`. Falls back to today's month when nothing's selected.\n const [internalMonth, setInternalMonth] = useState<Date>(() => {\n // `end` covers the range-input edge case where only the closing half is\n // seeded (`{ start: null, end }`); today is the last resort.\n const seed = value?.start ?? value?.end ?? realToday;\n return new Date(seed.getFullYear(), seed.getMonth(), 1);\n });\n const currentMonth = month ?? internalMonth;\n\n // View state — pure UI state, never controlled. Day view by default. Header\n // click drills up (day → month → year). Cell click drills back down.\n const [view, setView] = useState<CalendarView>('day');\n\n // Unified selection model — atom infers single vs range from click sequence.\n const isControlled = value !== undefined;\n const [internalValue, setInternalValue] = useState<CalendarValue>(EMPTY_VALUE);\n const currentValue = isControlled ? value : internalValue;\n const currentStart = currentValue.start;\n const currentEnd = currentValue.end;\n\n // Hover preview — only meaningful in the in-flight window in day view.\n const [hoveredDate, setHoveredDate] = useState<Date | null>(null);\n const inFlight = !!currentStart && !currentEnd;\n const previewEnd = inFlight && hoveredDate ? hoveredDate : currentEnd;\n const isRangeView = !!currentStart && !!previewEnd && !sameDay(currentStart, previewEnd);\n\n const dayCells = useMemo(\n () => computeMonthGrid(currentMonth, weekStartsOn),\n [currentMonth, weekStartsOn],\n );\n\n const isCellDisabled = useCallback((date: Date) => isDisabled?.(date) ?? false, [isDisabled]);\n\n // Roving tabindex across the day grid (ARIA grid pattern): only one cell\n // is a Tab stop at a time; arrow keys move it. `dayRefs` lets the arrow\n // handler call `.focus()` on the target cell after moving the index.\n const dayRefs = useRef<Array<HTMLButtonElement | null>>([]);\n const [focusedDayIndex, setFocusedDayIndex] = useState<number>(() =>\n defaultDayFocusIndex(dayCells, currentStart, realToday, isCellDisabled),\n );\n\n const weekdays = useMemo(\n () => getWeekdayLabels(locale, weekStartsOn),\n [locale, weekStartsOn],\n );\n\n // Header label — depends on the active view.\n const titleLabel = useMemo(() => {\n if (view === 'day') {\n return new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }).format(currentMonth);\n }\n if (view === 'month') {\n return String(currentMonth.getFullYear());\n }\n const start = yearBlockStart(currentMonth.getFullYear());\n return `${start}–${start + YEARS_PER_BLOCK - 1}`;\n }, [view, currentMonth, locale]);\n\n // Month-view labels — short month names from the locale formatter.\n const monthShortNames = useMemo(() => {\n const fmt = new Intl.DateTimeFormat(locale, { month: 'short' });\n return Array.from({ length: 12 }, (_, i) => fmt.format(new Date(2024, i, 1)));\n }, [locale]);\n\n const setMonth = useCallback(\n (next: Date) => {\n if (month === undefined) setInternalMonth(next);\n onMonthChange?.(next);\n // Re-target the day grid's roving tab stop for the month we're\n // navigating to — the previous index otherwise points at an\n // unrelated cell (or none) in the new grid.\n const nextCells = computeMonthGrid(next, weekStartsOn);\n setFocusedDayIndex(defaultDayFocusIndex(nextCells, currentStart, realToday, isCellDisabled));\n },\n [month, onMonthChange, weekStartsOn, currentStart, realToday, isCellDisabled],\n );\n\n // Prev/next nav stepping is view-aware: day → ±1 month, month → ±1 year,\n // year → ±12 years (one block).\n const handleNav = useCallback(\n (delta: number) => {\n const m = currentMonth.getMonth();\n const y = currentMonth.getFullYear();\n let next: Date;\n if (view === 'day') {\n next = new Date(y, m + delta, 1);\n } else if (view === 'month') {\n next = new Date(y + delta, m, 1);\n } else {\n next = new Date(y + delta * YEARS_PER_BLOCK, m, 1);\n }\n setMonth(next);\n },\n [view, currentMonth, setMonth],\n );\n\n // Header click drills up. Year view has nowhere further to go.\n const handleTitleClick = useCallback(() => {\n if (view === 'day') setView('month');\n else if (view === 'month') setView('year');\n }, [view]);\n\n const commit = useCallback(\n (next: CalendarValue) => {\n if (!isControlled) setInternalValue(next);\n onChange?.(next);\n },\n [isControlled, onChange],\n );\n\n // Day-cell click — the unified single/range click logic.\n const handleDayClick = useCallback(\n (date: Date, outside: boolean, disabled: boolean) => {\n if (disabled) return;\n if (outside) {\n setMonth(new Date(date.getFullYear(), date.getMonth(), 1));\n }\n const startingNew = !currentStart || !!currentEnd;\n if (startingNew) {\n commit({ start: date, end: null });\n } else {\n const s = currentStart!;\n let finalStart = s;\n let finalEnd = date;\n if (startOfDay(date).getTime() < startOfDay(s).getTime()) {\n finalStart = date;\n finalEnd = s;\n }\n commit({ start: finalStart, end: finalEnd });\n }\n setHoveredDate(null);\n },\n [currentStart, currentEnd, commit, setMonth],\n );\n\n // Month-view click drills back to day view for the clicked month.\n const handleMonthClick = useCallback(\n (monthIdx: number) => {\n setMonth(new Date(currentMonth.getFullYear(), monthIdx, 1));\n setView('day');\n },\n [currentMonth, setMonth],\n );\n\n // Year-view click drills to month view for the clicked year, preserving\n // the previously-active month-of-year so the user doesn't lose context.\n const handleYearClick = useCallback(\n (year: number) => {\n setMonth(new Date(year, currentMonth.getMonth(), 1));\n setView('month');\n },\n [currentMonth, setMonth],\n );\n\n const handleMouseEnter = useCallback(\n (date: Date, disabled: boolean) => {\n if (disabled) return;\n if (inFlight) setHoveredDate(date);\n },\n [inFlight],\n );\n\n const handleMouseLeave = useCallback((date: Date) => {\n setHoveredDate((prev) => (sameDay(prev, date) ? null : prev));\n }, []);\n\n // ARIA grid keyboard navigation for the day view: ArrowLeft/Right move a\n // day, ArrowUp/Down move a week, Home/End jump to the start/end of the\n // current week row. Stays within the visible 6×7 grid (doesn't cross\n // into the previous/next month) — disabled cells are skipped so arrow\n // navigation never lands on (and gets stuck at) an inert cell.\n const handleDayKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>, i: number) => {\n const rowStart = i - (i % 7);\n let step = 0;\n let bound: number | null = null;\n switch (e.key) {\n case 'ArrowLeft':\n step = -1;\n break;\n case 'ArrowRight':\n step = 1;\n break;\n case 'ArrowUp':\n step = -7;\n break;\n case 'ArrowDown':\n step = 7;\n break;\n case 'Home':\n step = -1;\n bound = rowStart;\n break;\n case 'End':\n step = 1;\n bound = rowStart + 6;\n break;\n default:\n return;\n }\n e.preventDefault();\n let next = bound ?? i + step;\n while (\n next >= 0 &&\n next < dayCells.length &&\n isCellDisabled(dayCells[next].date)\n ) {\n next += step;\n }\n if (next < 0 || next >= dayCells.length) return;\n setFocusedDayIndex(next);\n dayRefs.current[next]?.focus();\n },\n [dayCells, isCellDisabled],\n );\n\n return (\n <div className={cn('uxm-calendar', `uxm-calendar--view-${view}`, shadow && 'uxm-calendar--shadow', className)} {...rest}>\n <div className=\"uxm-calendar__header\">\n <button\n type=\"button\"\n className=\"uxm-calendar__nav\"\n onClick={() => handleNav(-1)}\n aria-label={previousMonthLabel}\n >\n <Icon glyph=\"chevron-left\" size={16} />\n </button>\n <button\n type=\"button\"\n className=\"uxm-calendar__title\"\n onClick={handleTitleClick}\n disabled={view === 'year'}\n aria-label={view === 'year' ? undefined : drillUpLabel}\n >\n {titleLabel}\n </button>\n <button\n type=\"button\"\n className=\"uxm-calendar__nav\"\n onClick={() => handleNav(1)}\n aria-label={nextMonthLabel}\n >\n <Icon glyph=\"chevron-right\" size={16} />\n </button>\n </div>\n\n {view === 'day' && (\n <>\n <div className=\"uxm-calendar__weekdays\" aria-hidden=\"true\">\n {weekdays.map((w) => (\n <div key={w} className=\"uxm-calendar__weekday\">{w}</div>\n ))}\n </div>\n <div className=\"uxm-calendar__grid\" role=\"grid\">\n {Array.from({ length: dayCells.length / 7 }, (_, week) => (\n // `display: contents` (see calendar.scss) keeps these cells as\n // direct participants in the parent's CSS grid while giving\n // each week its own `role=\"row\"` — the ARIA grid pattern\n // requires gridcells to be grouped into rows.\n <div key={week} role=\"row\" className=\"uxm-calendar__grid-row\">\n {dayCells.slice(week * 7, week * 7 + 7).map(({ date, outside }, col) => {\n const i = week * 7 + col;\n const isToday = sameDay(date, realToday);\n const isStart = sameDay(date, currentStart);\n const isEnd = !!previewEnd && sameDay(date, previewEnd);\n const isSelected = !isRangeView && isStart;\n const isRangeStart = isRangeView && isStart;\n const isRangeEnd = isRangeView && isEnd;\n const isInRange = isStrictlyBetween(date, currentStart, previewEnd);\n const disabled = isDisabled?.(date) ?? false;\n return (\n <button\n key={i}\n ref={(el) => {\n dayRefs.current[i] = el;\n }}\n type=\"button\"\n role=\"gridcell\"\n disabled={disabled}\n tabIndex={i === focusedDayIndex ? 0 : -1}\n onClick={() => {\n handleDayClick(date, outside, disabled);\n if (!disabled && !outside) setFocusedDayIndex(i);\n }}\n onKeyDown={(e) => handleDayKeyDown(e, i)}\n onMouseEnter={() => handleMouseEnter(date, disabled)}\n onMouseLeave={() => handleMouseLeave(date)}\n className={cn(\n 'uxm-calendar__day',\n outside && 'uxm-calendar__day--outside',\n isToday && 'uxm-calendar__day--today',\n isSelected && 'uxm-calendar__day--selected',\n isRangeStart && 'uxm-calendar__day--range-start',\n isRangeEnd && 'uxm-calendar__day--range-end',\n isInRange && 'uxm-calendar__day--in-range',\n disabled && 'uxm-calendar__day--disabled',\n )}\n aria-selected={isSelected || isRangeStart || isRangeEnd || undefined}\n aria-current={isToday ? 'date' : undefined}\n >\n {date.getDate()}\n </button>\n );\n })}\n </div>\n ))}\n </div>\n </>\n )}\n\n {view === 'month' && (\n <div className=\"uxm-calendar__months\" role=\"grid\">\n {monthShortNames.map((name, i) => {\n const year = currentMonth.getFullYear();\n const isCurrent = realToday.getFullYear() === year && realToday.getMonth() === i;\n const isSelected = monthOverlapsSelection(year, i, currentValue);\n return (\n <button\n key={i}\n type=\"button\"\n role=\"gridcell\"\n onClick={() => handleMonthClick(i)}\n className={cn(\n 'uxm-calendar__month',\n isCurrent && 'uxm-calendar__month--today',\n isSelected && 'uxm-calendar__month--selected',\n )}\n aria-selected={isSelected || undefined}\n aria-current={isCurrent ? 'true' : undefined}\n >\n {name}\n </button>\n );\n })}\n </div>\n )}\n\n {view === 'year' && (\n <div className=\"uxm-calendar__years\" role=\"grid\">\n {Array.from({ length: YEARS_PER_BLOCK }, (_, i) => {\n const year = yearBlockStart(currentMonth.getFullYear()) + i;\n const isCurrent = realToday.getFullYear() === year;\n const isSelected = yearOverlapsSelection(year, currentValue);\n return (\n <button\n key={year}\n type=\"button\"\n role=\"gridcell\"\n onClick={() => handleYearClick(year)}\n className={cn(\n 'uxm-calendar__year',\n isCurrent && 'uxm-calendar__year--today',\n isSelected && 'uxm-calendar__year--selected',\n )}\n aria-selected={isSelected || undefined}\n aria-current={isCurrent ? 'true' : undefined}\n >\n {year}\n </button>\n );\n })}\n </div>\n )}\n\n {/* \"Today\" — jumps the view back to the current month in day view so\n the user can find today after navigating away. Deliberately does\n NOT select today (returning to a date ≠ committing it); the day\n cell is one click away. */}\n <div className=\"uxm-calendar__footer\">\n <button\n type=\"button\"\n className=\"uxm-calendar__today-button\"\n disabled={isDisabled?.(realToday)}\n onClick={() => {\n setView('day');\n setMonth(new Date(realToday.getFullYear(), realToday.getMonth(), 1));\n }}\n >\n Today\n </button>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/ui/calendar/calendar.tsx"],"sourcesContent":["import { useCallback, useMemo, useRef, useState } from 'react';\n\nimport { cn } from '@/helpers';\n\nimport { computeMonthGrid, type WeekStart } from '../../lib/calendar-grid';\nimport { Icon } from '../icon';\nimport { useUxmLocale } from '../locale';\n\nimport type { HTMLAttributes, KeyboardEvent } from 'react';\n\nexport interface CalendarValue {\n start: Date | null;\n end: Date | null;\n}\n\ntype CalendarView = 'day' | 'month' | 'year';\n\nexport interface CalendarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {\n /** Currently-displayed month. Controlled. If omitted, atom owns it (initialized to the selected value's month, else today's). */\n month?: Date;\n /** Selection value. Controlled when passed. `{ start, end: null }` for a single date; `{ start, end }` for a range. */\n value?: CalendarValue;\n /** Override for \"today\". Defaults to real current date. */\n today?: Date;\n /** Predicate for disabled days. Disabled cells ignore clicks. */\n isDisabled?: (date: Date) => boolean;\n /** 0 = Sunday (US), 1 = Monday. */\n weekStartsOn?: WeekStart;\n /**\n * BCP-47 locale for the month label and weekday names. Defaults to the\n * nearest `UxmLocaleProvider`, then to `\"en-US\"`. Set it here only to pin\n * one calendar to a locale that differs from the rest of the app.\n */\n locale?: string;\n /** Fires after every click. `end` is null after the first click of a new range, set after the second. */\n onChange?: (value: CalendarValue) => void;\n /** Month navigation callback (prev/next buttons, outside-month-cell click, or drilling via month/year views). */\n onMonthChange?: (next: Date) => void;\n /** Drop a soft elevation shadow (the floating-surface look). Default true — pass `false` for a flat, inline / embedded calendar. */\n shadow?: boolean;\n /** Accessible name for the previous-month button. Default `\"Previous\"`. */\n previousMonthLabel?: string;\n /** Accessible name for the next-month button. Default `\"Next\"`. */\n nextMonthLabel?: string;\n /** Accessible name for the month/year drill-up button. Default `\"Drill up\"`. */\n drillUpLabel?: string;\n}\n\nconst MS_PER_DAY = 86_400_000;\nconst YEARS_PER_BLOCK = 12;\n\nfunction startOfDay(d: Date): Date {\n return new Date(d.getFullYear(), d.getMonth(), d.getDate());\n}\nfunction sameDay(a: Date | null | undefined, b: Date | null | undefined): boolean {\n if (!a || !b) return false;\n return startOfDay(a).getTime() === startOfDay(b).getTime();\n}\nfunction isStrictlyBetween(date: Date, start: Date | null | undefined, end: Date | null | undefined): boolean {\n if (!start || !end) return false;\n const t = startOfDay(date).getTime();\n const lo = Math.min(startOfDay(start).getTime(), startOfDay(end).getTime());\n const hi = Math.max(startOfDay(start).getTime(), startOfDay(end).getTime());\n return t > lo && t < hi;\n}\n\nfunction getWeekdayLabels(locale: string, weekStartsOn: WeekStart): string[] {\n const fmt = new Intl.DateTimeFormat(locale, { weekday: 'short' });\n // 2024-01-07 is a known Sunday — stable anchor for the formatter.\n const anchor = new Date(2024, 0, 7);\n const labels: string[] = [];\n for (let i = 0; i < 7; i++) {\n labels.push(fmt.format(new Date(anchor.getTime() + i * MS_PER_DAY)));\n }\n return weekStartsOn === 1 ? [...labels.slice(1), labels[0]] : labels;\n}\n\n/**\n * Year-block start for the year view. 12-year blocks aligned to multiples of\n * `YEARS_PER_BLOCK` (e.g. 2024 → 2016–2027). Block alignment is the iOS pattern;\n * picking any other anchor (decades, etc.) is equally valid — this just keeps\n * navigation predictable.\n */\nfunction yearBlockStart(year: number): number {\n return Math.floor(year / YEARS_PER_BLOCK) * YEARS_PER_BLOCK;\n}\n\n/** True if any day of `year-month` overlaps the inclusive selection range. */\nfunction monthOverlapsSelection(year: number, monthIdx: number, value: CalendarValue): boolean {\n if (!value.start) return false;\n const monthFirstMs = new Date(year, monthIdx, 1).getTime();\n const monthLastMs = new Date(year, monthIdx + 1, 0).getTime();\n const sMs = startOfDay(value.start).getTime();\n const eMs = value.end ? startOfDay(value.end).getTime() : sMs;\n const lo = Math.min(sMs, eMs);\n const hi = Math.max(sMs, eMs);\n return monthFirstMs <= hi && monthLastMs >= lo;\n}\n\n/** True if `year` falls within the inclusive selection range's year span. */\nfunction yearOverlapsSelection(year: number, value: CalendarValue): boolean {\n if (!value.start) return false;\n const sY = value.start.getFullYear();\n const eY = value.end ? value.end.getFullYear() : sY;\n const lo = Math.min(sY, eY);\n const hi = Math.max(sY, eY);\n return year >= lo && year <= hi;\n}\n\nconst EMPTY_VALUE: CalendarValue = { start: null, end: null };\n\n/**\n * Default roving-tabindex target for the day grid: the selected day if\n * it's in the visible grid and enabled, else today if visible and\n * enabled, else the first enabled cell, else 0. Keeps the grid reachable\n * via Tab even when the \"obvious\" cell happens to be disabled.\n */\nfunction defaultDayFocusIndex(\n cells: { date: Date; outside: boolean }[],\n start: Date | null,\n today: Date,\n isCellDisabled: (date: Date) => boolean,\n): number {\n const startIndex = start ? cells.findIndex((c) => sameDay(c.date, start)) : -1;\n if (startIndex >= 0 && !isCellDisabled(cells[startIndex].date)) return startIndex;\n const todayIndex = cells.findIndex((c) => sameDay(c.date, today));\n if (todayIndex >= 0 && !isCellDisabled(cells[todayIndex].date)) return todayIndex;\n const firstEnabled = cells.findIndex((c) => !isCellDisabled(c.date));\n return firstEnabled >= 0 ? firstEnabled : 0;\n}\n\nexport function Calendar({\n month,\n value,\n today,\n isDisabled,\n weekStartsOn = 0,\n locale: localeProp,\n onChange,\n onMonthChange,\n shadow = true,\n previousMonthLabel = 'Previous',\n nextMonthLabel = 'Next',\n drillUpLabel = 'Drill up',\n className,\n ...rest\n}: CalendarProps) {\n const locale = useUxmLocale(localeProp);\n const realToday = useMemo(() => today ?? new Date(), [today]);\n\n // Month state — controlled when `month` prop is provided. When uncontrolled,\n // seed from the current selection so opening on an existing date lands on\n // that date's month, not today's. The popover consumers mount the calendar\n // fresh per open, so this initial `value` is the selected date; nav then\n // owns `internalMonth`. Falls back to today's month when nothing's selected.\n const [internalMonth, setInternalMonth] = useState<Date>(() => {\n // `end` covers the range-input edge case where only the closing half is\n // seeded (`{ start: null, end }`); today is the last resort.\n const seed = value?.start ?? value?.end ?? realToday;\n return new Date(seed.getFullYear(), seed.getMonth(), 1);\n });\n const currentMonth = month ?? internalMonth;\n\n // View state — pure UI state, never controlled. Day view by default. Header\n // click drills up (day → month → year). Cell click drills back down.\n const [view, setView] = useState<CalendarView>('day');\n\n // Unified selection model — atom infers single vs range from click sequence.\n const isControlled = value !== undefined;\n const [internalValue, setInternalValue] = useState<CalendarValue>(EMPTY_VALUE);\n const currentValue = isControlled ? value : internalValue;\n const currentStart = currentValue.start;\n const currentEnd = currentValue.end;\n\n // Hover preview — only meaningful in the in-flight window in day view.\n const [hoveredDate, setHoveredDate] = useState<Date | null>(null);\n const inFlight = !!currentStart && !currentEnd;\n const previewEnd = inFlight && hoveredDate ? hoveredDate : currentEnd;\n const isRangeView = !!currentStart && !!previewEnd && !sameDay(currentStart, previewEnd);\n\n const dayCells = useMemo(\n () => computeMonthGrid(currentMonth, weekStartsOn),\n [currentMonth, weekStartsOn],\n );\n\n const isCellDisabled = useCallback((date: Date) => isDisabled?.(date) ?? false, [isDisabled]);\n\n // Roving tabindex across the day grid (ARIA grid pattern): only one cell\n // is a Tab stop at a time; arrow keys move it. `dayRefs` lets the arrow\n // handler call `.focus()` on the target cell after moving the index.\n const dayRefs = useRef<Array<HTMLButtonElement | null>>([]);\n const [focusedDayIndex, setFocusedDayIndex] = useState<number>(() =>\n defaultDayFocusIndex(dayCells, currentStart, realToday, isCellDisabled),\n );\n\n const weekdays = useMemo(\n () => getWeekdayLabels(locale, weekStartsOn),\n [locale, weekStartsOn],\n );\n\n // Header label — depends on the active view.\n const titleLabel = useMemo(() => {\n if (view === 'day') {\n return new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }).format(currentMonth);\n }\n if (view === 'month') {\n return String(currentMonth.getFullYear());\n }\n const start = yearBlockStart(currentMonth.getFullYear());\n return `${start}–${start + YEARS_PER_BLOCK - 1}`;\n }, [view, currentMonth, locale]);\n\n // Month-view labels — short month names from the locale formatter.\n const monthShortNames = useMemo(() => {\n const fmt = new Intl.DateTimeFormat(locale, { month: 'short' });\n return Array.from({ length: 12 }, (_, i) => fmt.format(new Date(2024, i, 1)));\n }, [locale]);\n\n const setMonth = useCallback(\n (next: Date) => {\n if (month === undefined) setInternalMonth(next);\n onMonthChange?.(next);\n // Re-target the day grid's roving tab stop for the month we're\n // navigating to — the previous index otherwise points at an\n // unrelated cell (or none) in the new grid.\n const nextCells = computeMonthGrid(next, weekStartsOn);\n setFocusedDayIndex(defaultDayFocusIndex(nextCells, currentStart, realToday, isCellDisabled));\n },\n [month, onMonthChange, weekStartsOn, currentStart, realToday, isCellDisabled],\n );\n\n // Prev/next nav stepping is view-aware: day → ±1 month, month → ±1 year,\n // year → ±12 years (one block).\n const handleNav = useCallback(\n (delta: number) => {\n const m = currentMonth.getMonth();\n const y = currentMonth.getFullYear();\n let next: Date;\n if (view === 'day') {\n next = new Date(y, m + delta, 1);\n } else if (view === 'month') {\n next = new Date(y + delta, m, 1);\n } else {\n next = new Date(y + delta * YEARS_PER_BLOCK, m, 1);\n }\n setMonth(next);\n },\n [view, currentMonth, setMonth],\n );\n\n // Header click drills up. Year view has nowhere further to go.\n const handleTitleClick = useCallback(() => {\n if (view === 'day') setView('month');\n else if (view === 'month') setView('year');\n }, [view]);\n\n const commit = useCallback(\n (next: CalendarValue) => {\n if (!isControlled) setInternalValue(next);\n onChange?.(next);\n },\n [isControlled, onChange],\n );\n\n // Day-cell click — the unified single/range click logic.\n const handleDayClick = useCallback(\n (date: Date, outside: boolean, disabled: boolean) => {\n if (disabled) return;\n if (outside) {\n setMonth(new Date(date.getFullYear(), date.getMonth(), 1));\n }\n const startingNew = !currentStart || !!currentEnd;\n if (startingNew) {\n commit({ start: date, end: null });\n } else {\n const s = currentStart!;\n let finalStart = s;\n let finalEnd = date;\n if (startOfDay(date).getTime() < startOfDay(s).getTime()) {\n finalStart = date;\n finalEnd = s;\n }\n commit({ start: finalStart, end: finalEnd });\n }\n setHoveredDate(null);\n },\n [currentStart, currentEnd, commit, setMonth],\n );\n\n // Month-view click drills back to day view for the clicked month.\n const handleMonthClick = useCallback(\n (monthIdx: number) => {\n setMonth(new Date(currentMonth.getFullYear(), monthIdx, 1));\n setView('day');\n },\n [currentMonth, setMonth],\n );\n\n // Year-view click drills to month view for the clicked year, preserving\n // the previously-active month-of-year so the user doesn't lose context.\n const handleYearClick = useCallback(\n (year: number) => {\n setMonth(new Date(year, currentMonth.getMonth(), 1));\n setView('month');\n },\n [currentMonth, setMonth],\n );\n\n const handleMouseEnter = useCallback(\n (date: Date, disabled: boolean) => {\n if (disabled) return;\n if (inFlight) setHoveredDate(date);\n },\n [inFlight],\n );\n\n const handleMouseLeave = useCallback((date: Date) => {\n setHoveredDate((prev) => (sameDay(prev, date) ? null : prev));\n }, []);\n\n // ARIA grid keyboard navigation for the day view: ArrowLeft/Right move a\n // day, ArrowUp/Down move a week, Home/End jump to the start/end of the\n // current week row. Stays within the visible 6×7 grid (doesn't cross\n // into the previous/next month) — disabled cells are skipped so arrow\n // navigation never lands on (and gets stuck at) an inert cell.\n const handleDayKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>, i: number) => {\n const rowStart = i - (i % 7);\n let step = 0;\n let bound: number | null = null;\n switch (e.key) {\n case 'ArrowLeft':\n step = -1;\n break;\n case 'ArrowRight':\n step = 1;\n break;\n case 'ArrowUp':\n step = -7;\n break;\n case 'ArrowDown':\n step = 7;\n break;\n case 'Home':\n step = -1;\n bound = rowStart;\n break;\n case 'End':\n step = 1;\n bound = rowStart + 6;\n break;\n default:\n return;\n }\n e.preventDefault();\n let next = bound ?? i + step;\n while (\n next >= 0 &&\n next < dayCells.length &&\n isCellDisabled(dayCells[next].date)\n ) {\n next += step;\n }\n if (next < 0 || next >= dayCells.length) return;\n setFocusedDayIndex(next);\n dayRefs.current[next]?.focus();\n },\n [dayCells, isCellDisabled],\n );\n\n return (\n <div className={cn('uxm-calendar', `uxm-calendar--view-${view}`, shadow && 'uxm-calendar--shadow', className)} {...rest}>\n <div className=\"uxm-calendar__header\">\n <button\n type=\"button\"\n className=\"uxm-calendar__nav\"\n onClick={() => handleNav(-1)}\n aria-label={previousMonthLabel}\n >\n <Icon glyph=\"chevron-left\" size={16} />\n </button>\n <button\n type=\"button\"\n className=\"uxm-calendar__title\"\n onClick={handleTitleClick}\n disabled={view === 'year'}\n aria-label={view === 'year' ? undefined : drillUpLabel}\n >\n {titleLabel}\n </button>\n <button\n type=\"button\"\n className=\"uxm-calendar__nav\"\n onClick={() => handleNav(1)}\n aria-label={nextMonthLabel}\n >\n <Icon glyph=\"chevron-right\" size={16} />\n </button>\n </div>\n\n {view === 'day' && (\n <>\n <div className=\"uxm-calendar__weekdays\" aria-hidden=\"true\">\n {weekdays.map((w) => (\n <div key={w} className=\"uxm-calendar__weekday\">{w}</div>\n ))}\n </div>\n <div className=\"uxm-calendar__grid\" role=\"grid\">\n {Array.from({ length: dayCells.length / 7 }, (_, week) => (\n // `display: contents` (see calendar.scss) keeps these cells as\n // direct participants in the parent's CSS grid while giving\n // each week its own `role=\"row\"` — the ARIA grid pattern\n // requires gridcells to be grouped into rows.\n <div key={week} role=\"row\" className=\"uxm-calendar__grid-row\">\n {dayCells.slice(week * 7, week * 7 + 7).map(({ date, outside }, col) => {\n const i = week * 7 + col;\n const isToday = sameDay(date, realToday);\n const isStart = sameDay(date, currentStart);\n const isEnd = !!previewEnd && sameDay(date, previewEnd);\n const isSelected = !isRangeView && isStart;\n const isRangeStart = isRangeView && isStart;\n const isRangeEnd = isRangeView && isEnd;\n const isInRange = isStrictlyBetween(date, currentStart, previewEnd);\n const disabled = isDisabled?.(date) ?? false;\n return (\n <button\n key={i}\n ref={(el) => {\n dayRefs.current[i] = el;\n }}\n type=\"button\"\n role=\"gridcell\"\n disabled={disabled}\n tabIndex={i === focusedDayIndex ? 0 : -1}\n onClick={() => {\n handleDayClick(date, outside, disabled);\n if (!disabled && !outside) setFocusedDayIndex(i);\n }}\n onKeyDown={(e) => handleDayKeyDown(e, i)}\n onMouseEnter={() => handleMouseEnter(date, disabled)}\n onMouseLeave={() => handleMouseLeave(date)}\n className={cn(\n 'uxm-calendar__day',\n outside && 'uxm-calendar__day--outside',\n isToday && 'uxm-calendar__day--today',\n isSelected && 'uxm-calendar__day--selected',\n isRangeStart && 'uxm-calendar__day--range-start',\n isRangeEnd && 'uxm-calendar__day--range-end',\n isInRange && 'uxm-calendar__day--in-range',\n disabled && 'uxm-calendar__day--disabled',\n )}\n aria-selected={isSelected || isRangeStart || isRangeEnd || undefined}\n aria-current={isToday ? 'date' : undefined}\n >\n {date.getDate()}\n </button>\n );\n })}\n </div>\n ))}\n </div>\n </>\n )}\n\n {view === 'month' && (\n <div className=\"uxm-calendar__months\" role=\"grid\">\n {monthShortNames.map((name, i) => {\n const year = currentMonth.getFullYear();\n const isCurrent = realToday.getFullYear() === year && realToday.getMonth() === i;\n const isSelected = monthOverlapsSelection(year, i, currentValue);\n return (\n <button\n key={i}\n type=\"button\"\n role=\"gridcell\"\n onClick={() => handleMonthClick(i)}\n className={cn(\n 'uxm-calendar__month',\n isCurrent && 'uxm-calendar__month--today',\n isSelected && 'uxm-calendar__month--selected',\n )}\n aria-selected={isSelected || undefined}\n aria-current={isCurrent ? 'true' : undefined}\n >\n {name}\n </button>\n );\n })}\n </div>\n )}\n\n {view === 'year' && (\n <div className=\"uxm-calendar__years\" role=\"grid\">\n {Array.from({ length: YEARS_PER_BLOCK }, (_, i) => {\n const year = yearBlockStart(currentMonth.getFullYear()) + i;\n const isCurrent = realToday.getFullYear() === year;\n const isSelected = yearOverlapsSelection(year, currentValue);\n return (\n <button\n key={year}\n type=\"button\"\n role=\"gridcell\"\n onClick={() => handleYearClick(year)}\n className={cn(\n 'uxm-calendar__year',\n isCurrent && 'uxm-calendar__year--today',\n isSelected && 'uxm-calendar__year--selected',\n )}\n aria-selected={isSelected || undefined}\n aria-current={isCurrent ? 'true' : undefined}\n >\n {year}\n </button>\n );\n })}\n </div>\n )}\n\n {/* \"Today\" — jumps the view back to the current month in day view so\n the user can find today after navigating away. Deliberately does\n NOT select today (returning to a date ≠ committing it); the day\n cell is one click away. */}\n <div className=\"uxm-calendar__footer\">\n <button\n type=\"button\"\n className=\"uxm-calendar__today-button\"\n disabled={isDisabled?.(realToday)}\n onClick={() => {\n setView('day');\n setMonth(new Date(realToday.getFullYear(), realToday.getMonth(), 1));\n }}\n >\n Today\n </button>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -29,6 +29,7 @@ var import_field_error = require("../field-error/index.cjs");
29
29
  var import_icon = require("../icon/index.cjs");
30
30
  var import_icon_button = require("../icon-button/index.cjs");
31
31
  var import_listbox = require("../listbox/index.cjs");
32
+ var import_locale = require("../locale/index.cjs");
32
33
  function maskAmount(raw, allowNegative, decimals) {
33
34
  if (raw === "") return "";
34
35
  let negative = false;
@@ -89,7 +90,7 @@ function CurrencyInput({
89
90
  defaultValue,
90
91
  onChange,
91
92
  currencies = import_currencies.CURATED_CURRENCIES,
92
- locale = "en-US",
93
+ locale: localeProp,
93
94
  min,
94
95
  max,
95
96
  allowNegative = false,
@@ -105,6 +106,7 @@ function CurrencyInput({
105
106
  error,
106
107
  ...rest
107
108
  }) {
109
+ const locale = (0, import_locale.useUxmLocale)(localeProp);
108
110
  const [internal, setInternal] = (0, import_react.useState)(
109
111
  () => defaultValue ?? { currency: currencies[0]?.code ?? "USD", amount: "" }
110
112
  );