@acronis-platform/ui-react 0.57.1 → 0.58.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 (129) hide show
  1. package/dist/components/ui/area-chart/area-chart.js +149 -0
  2. package/dist/components/ui/area-chart/area-chart.js.map +1 -0
  3. package/dist/components/ui/bar-chart/bar-chart.js +152 -77
  4. package/dist/components/ui/bar-chart/bar-chart.js.map +1 -1
  5. package/dist/components/ui/button-icon-input/button-icon-input.js +36 -0
  6. package/dist/components/ui/button-icon-input/button-icon-input.js.map +1 -0
  7. package/dist/components/ui/button-menu/button-menu.js +1 -1
  8. package/dist/components/ui/button-menu/button-menu.js.map +1 -1
  9. package/dist/components/ui/card-filter/card-filter.js +40 -27
  10. package/dist/components/ui/card-filter/card-filter.js.map +1 -1
  11. package/dist/components/ui/chart-state/chart-state.js +42 -0
  12. package/dist/components/ui/chart-state/chart-state.js.map +1 -0
  13. package/dist/components/ui/chip/chip.js +13 -13
  14. package/dist/components/ui/chip/chip.js.map +1 -1
  15. package/dist/components/ui/composed-chart/composed-chart.js +107 -0
  16. package/dist/components/ui/composed-chart/composed-chart.js.map +1 -0
  17. package/dist/components/ui/confidence-cone/confidence-cone.js +195 -0
  18. package/dist/components/ui/confidence-cone/confidence-cone.js.map +1 -0
  19. package/dist/components/ui/date-range-picker/date-range-picker.js +21 -21
  20. package/dist/components/ui/date-range-picker/date-range-picker.js.map +1 -1
  21. package/dist/components/ui/dialog/dialog.js +250 -78
  22. package/dist/components/ui/dialog/dialog.js.map +1 -1
  23. package/dist/components/ui/dialog-footer-default/dialog-footer-default.js +26 -0
  24. package/dist/components/ui/dialog-footer-default/dialog-footer-default.js.map +1 -0
  25. package/dist/components/ui/dropdown-menu/dropdown-menu.js +21 -21
  26. package/dist/components/ui/dropdown-menu/dropdown-menu.js.map +1 -1
  27. package/dist/components/ui/filter-search/filter-search.js +91 -90
  28. package/dist/components/ui/filter-search/filter-search.js.map +1 -1
  29. package/dist/components/ui/funnel-chart/funnel-chart.js +87 -0
  30. package/dist/components/ui/funnel-chart/funnel-chart.js.map +1 -0
  31. package/dist/components/ui/histogram/histogram.js +108 -0
  32. package/dist/components/ui/histogram/histogram.js.map +1 -0
  33. package/dist/components/ui/input-otp/input-otp.js +104 -0
  34. package/dist/components/ui/input-otp/input-otp.js.map +1 -0
  35. package/dist/components/ui/input-password/input-password.js +120 -0
  36. package/dist/components/ui/input-password/input-password.js.map +1 -0
  37. package/dist/components/ui/input-text/input-text.js +9 -9
  38. package/dist/components/ui/input-text/input-text.js.map +1 -1
  39. package/dist/components/ui/line-chart/line-chart.js +206 -0
  40. package/dist/components/ui/line-chart/line-chart.js.map +1 -0
  41. package/dist/components/ui/link/link.js +5 -5
  42. package/dist/components/ui/link/link.js.map +1 -1
  43. package/dist/components/ui/loading/loading.js +79 -0
  44. package/dist/components/ui/loading/loading.js.map +1 -0
  45. package/dist/components/ui/meter/meter.js +93 -0
  46. package/dist/components/ui/meter/meter.js.map +1 -0
  47. package/dist/components/ui/metric/metric.js +185 -0
  48. package/dist/components/ui/metric/metric.js.map +1 -0
  49. package/dist/components/ui/pie-chart/pie-chart.js +113 -0
  50. package/dist/components/ui/pie-chart/pie-chart.js.map +1 -0
  51. package/dist/components/ui/popover/popover.js +57 -27
  52. package/dist/components/ui/popover/popover.js.map +1 -1
  53. package/dist/components/ui/radar-chart/radar-chart.js +74 -0
  54. package/dist/components/ui/radar-chart/radar-chart.js.map +1 -0
  55. package/dist/components/ui/radial-bar-chart/radial-bar-chart.js +69 -0
  56. package/dist/components/ui/radial-bar-chart/radial-bar-chart.js.map +1 -0
  57. package/dist/components/ui/scatter-chart/scatter-chart.js +102 -0
  58. package/dist/components/ui/scatter-chart/scatter-chart.js.map +1 -0
  59. package/dist/components/ui/search/search.js +16 -16
  60. package/dist/components/ui/search/search.js.map +1 -1
  61. package/dist/components/ui/sheet/sheet-details.js +16 -16
  62. package/dist/components/ui/sheet/sheet-details.js.map +1 -1
  63. package/dist/components/ui/spinner/spinner.js.map +1 -1
  64. package/dist/components/ui/timeline/timeline.js +175 -0
  65. package/dist/components/ui/timeline/timeline.js.map +1 -0
  66. package/dist/components/ui/treemap/treemap.js +83 -0
  67. package/dist/components/ui/treemap/treemap.js.map +1 -0
  68. package/dist/components/ui/trend-indicator/trend-indicator.js +97 -0
  69. package/dist/components/ui/trend-indicator/trend-indicator.js.map +1 -0
  70. package/dist/index.js +423 -384
  71. package/dist/index.js.map +1 -1
  72. package/dist/react.js +423 -384
  73. package/dist/react.js.map +1 -1
  74. package/dist/src/components/ui/area-chart/area-chart.d.ts +49 -0
  75. package/dist/src/components/ui/area-chart/index.d.ts +1 -0
  76. package/dist/src/components/ui/avatar/avatar.d.ts +1 -1
  77. package/dist/src/components/ui/bar-chart/bar-chart.d.ts +42 -2
  78. package/dist/src/components/ui/bar-chart/index.d.ts +1 -1
  79. package/dist/src/components/ui/button-icon-input/button-icon-input.d.ts +21 -0
  80. package/dist/src/components/ui/button-icon-input/index.d.ts +1 -0
  81. package/dist/src/components/ui/card-filter/card-filter.d.ts +5 -0
  82. package/dist/src/components/ui/chart-state/chart-state.d.ts +11 -0
  83. package/dist/src/components/ui/chart-state/index.d.ts +1 -0
  84. package/dist/src/components/ui/composed-chart/composed-chart.d.ts +48 -0
  85. package/dist/src/components/ui/composed-chart/index.d.ts +1 -0
  86. package/dist/src/components/ui/confidence-cone/confidence-cone.d.ts +79 -0
  87. package/dist/src/components/ui/confidence-cone/index.d.ts +1 -0
  88. package/dist/src/components/ui/dialog/dialog.d.ts +105 -9
  89. package/dist/src/components/ui/dialog/dialog.docs.d.ts +82 -5
  90. package/dist/src/components/ui/dialog/index.d.ts +1 -1
  91. package/dist/src/components/ui/dialog-footer-default/dialog-footer-default.d.ts +11 -0
  92. package/dist/src/components/ui/dialog-footer-default/index.d.ts +1 -0
  93. package/dist/src/components/ui/filter-search/filter-search.d.ts +1 -1
  94. package/dist/src/components/ui/funnel-chart/funnel-chart.d.ts +39 -0
  95. package/dist/src/components/ui/funnel-chart/index.d.ts +1 -0
  96. package/dist/src/components/ui/histogram/histogram.d.ts +55 -0
  97. package/dist/src/components/ui/histogram/index.d.ts +1 -0
  98. package/dist/src/components/ui/input-otp/index.d.ts +1 -0
  99. package/dist/src/components/ui/input-otp/input-otp.d.ts +23 -0
  100. package/dist/src/components/ui/input-password/index.d.ts +1 -0
  101. package/dist/src/components/ui/input-password/input-password.d.ts +21 -0
  102. package/dist/src/components/ui/line-chart/index.d.ts +1 -0
  103. package/dist/src/components/ui/line-chart/line-chart.d.ts +83 -0
  104. package/dist/src/components/ui/loading/index.d.ts +1 -0
  105. package/dist/src/components/ui/loading/loading.d.ts +13 -0
  106. package/dist/src/components/ui/meter/index.d.ts +1 -0
  107. package/dist/src/components/ui/meter/meter.d.ts +25 -0
  108. package/dist/src/components/ui/metric/index.d.ts +1 -0
  109. package/dist/src/components/ui/metric/metric.d.ts +37 -0
  110. package/dist/src/components/ui/pie-chart/index.d.ts +1 -0
  111. package/dist/src/components/ui/pie-chart/pie-chart.d.ts +54 -0
  112. package/dist/src/components/ui/popover/index.d.ts +1 -1
  113. package/dist/src/components/ui/popover/popover.d.ts +13 -1
  114. package/dist/src/components/ui/radar-chart/index.d.ts +1 -0
  115. package/dist/src/components/ui/radar-chart/radar-chart.d.ts +40 -0
  116. package/dist/src/components/ui/radial-bar-chart/index.d.ts +1 -0
  117. package/dist/src/components/ui/radial-bar-chart/radial-bar-chart.d.ts +45 -0
  118. package/dist/src/components/ui/scatter-chart/index.d.ts +1 -0
  119. package/dist/src/components/ui/scatter-chart/scatter-chart.d.ts +50 -0
  120. package/dist/src/components/ui/stack/stack.d.ts +1 -1
  121. package/dist/src/components/ui/timeline/index.d.ts +1 -0
  122. package/dist/src/components/ui/timeline/timeline.d.ts +41 -0
  123. package/dist/src/components/ui/treemap/index.d.ts +1 -0
  124. package/dist/src/components/ui/treemap/treemap.d.ts +44 -0
  125. package/dist/src/components/ui/trend-indicator/index.d.ts +1 -0
  126. package/dist/src/components/ui/trend-indicator/trend-indicator.d.ts +32 -0
  127. package/dist/src/index.d.ts +22 -2
  128. package/dist/ui-react.css +1 -1
  129. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"filter-search.js","sources":["../../../../src/components/ui/filter-search/filter-search.tsx"],"sourcesContent":["import * as React from 'react';\nimport { ChevronDownIcon } from '@acronis-platform/icons-react/stroke-mono';\n\nimport { cn } from '@/lib/utils';\nimport { deepEqual } from '@/lib/deep-equal';\nimport { Button } from '../button';\nimport { Chip } from '../chip';\nimport { Popover, PopoverContent, PopoverTrigger } from '../popover';\n\n// A composable toolbar for data tables: arranges a search field, optional\n// filters button, optional tenant/scope switcher, and trailing action buttons\n// in a single horizontal row. Maps to the Figma \"FilterSearch\" component. The\n// root container uses 16px inter-item gap (Figma `gap/gap-16`) and centers\n// children vertically. `FilterSearchActions` is a right-aligned flex area that\n// pushes its content to the trailing edge.\n//\n// No dedicated token tier — spacing is the Figma `gap/gap-16` semantic token\n// (16px), and each child (Search, ButtonMenu, Select) brings its own\n// `--ui-*` tier.\n//\n// `FilterSearchFilters` and `FilterSearchAppliedFilters` are design-pending\n// additions (no Figma node yet, unlike the FilterSearch node itself): the\n// filter popover (trigger + form + Reset/Cancel/Apply) and the applied-filter\n// chip row + top-level \"Reset filters\" affordance. Compose `FilterSearchFilters`\n// as a child of `<FilterSearch>` in place of a plain filter trigger, and render\n// `<FilterSearchAppliedFilters>` as a sibling row below it. Reconcile against\n// the real design with `/figma-component FilterSearchFilters <url> --update`\n// once a mockup lands.\n\nexport type FilterSearchProps = React.ComponentPropsWithoutRef<'div'>;\n\nconst FilterSearch = React.forwardRef<HTMLDivElement, FilterSearchProps>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex items-center gap-4', className)}\n {...props}\n />\n )\n);\nFilterSearch.displayName = 'FilterSearch';\n\nexport type FilterSearchActionsProps = React.ComponentPropsWithoutRef<'div'>;\n\nconst FilterSearchActions = React.forwardRef<\n HTMLDivElement,\n FilterSearchActionsProps\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex h-8 min-w-px flex-1 items-center justify-end gap-4', className)}\n {...props}\n />\n));\nFilterSearchActions.displayName = 'FilterSearchActions';\n\ninterface FilterSearchFiltersContextValue {\n /** The current draft filters being edited inside the popover. */\n filters: Record<string, unknown>;\n /** Set a single draft filter value by key. */\n setFilter: (key: string, value: unknown) => void;\n}\n\nconst FilterSearchFiltersContext =\n React.createContext<FilterSearchFiltersContextValue | null>(null);\n\n/**\n * Bind a filter field child (rendered inside `FilterSearchFilters`'s popover) to\n * the component's draft filters. Returns the current draft map and a per-key\n * setter. Field children are plain JSX (no render prop) and wire their own\n * `onChange` to `setFilter`; Apply commits the draft, Cancel/dismiss reverts it.\n */\nexport function useFilterSearchFilters(): FilterSearchFiltersContextValue {\n const context = React.useContext(FilterSearchFiltersContext);\n if (context === null) {\n throw new Error(\n 'useFilterSearchFilters must be used within a <FilterSearchFilters> popover.'\n );\n }\n return context;\n}\n\nexport interface FilterSearchFiltersProps {\n /** The applied filter values, keyed by an arbitrary consumer-chosen id. */\n value: Record<string, unknown>;\n /** Called with the committed filters when Apply is pressed. */\n onValueChange: (next: Record<string, unknown>) => void;\n /** Called with the newly applied filters when Apply is pressed (fires alongside `onValueChange`). */\n onApply?: (filters: Record<string, unknown>) => void;\n /** Label for the filter trigger button. */\n label?: React.ReactNode;\n /**\n * Filter field children rendered inside the popover form. Plain children —\n * wire each field to the draft via `useFilterSearchFilters`. Group fields with\n * a `Separator` as a layout convention if desired.\n */\n children?: React.ReactNode;\n /** Additional classes merged onto the trigger button. */\n className?: string;\n}\n\nconst FilterSearchFilters = React.forwardRef<\n HTMLButtonElement,\n FilterSearchFiltersProps\n>(({ value, onValueChange, onApply, label = 'Filters', children, className }, ref) => {\n const [open, setOpen] = React.useState(false);\n const [draft, setDraft] = React.useState<Record<string, unknown>>(value);\n\n const handleOpenChange = (nextOpen: boolean) => {\n // Snapshot the applied filters on open; revert the draft on any dismiss\n // (outside press / Escape) so an un-applied edit never leaks out.\n setDraft(value);\n setOpen(nextOpen);\n };\n\n const setFilter = React.useCallback((key: string, filterValue: unknown) => {\n setDraft((previous) => {\n if (filterValue === undefined) {\n if (!(key in previous)) return previous;\n const next = { ...previous };\n delete next[key];\n return next;\n }\n return { ...previous, [key]: filterValue };\n });\n }, []);\n\n const filterContext = React.useMemo<FilterSearchFiltersContextValue>(\n () => ({ filters: draft, setFilter }),\n [draft, setFilter]\n );\n\n const handleReset = () => setDraft({});\n\n const handleCancel = () => {\n setDraft(value);\n setOpen(false);\n };\n\n const handleApply = () => {\n onValueChange(draft);\n onApply?.(draft);\n setOpen(false);\n };\n\n const resetDisabled = Object.keys(draft).length === 0;\n const applyDisabled = deepEqual(draft, value);\n\n return (\n <Popover open={open} onOpenChange={handleOpenChange}>\n <PopoverTrigger\n ref={ref}\n render={\n <Button\n variant=\"secondary\"\n className={cn(\n 'group data-[popup-open]:border-[var(--ui-button-secondary-container-border-color-active)] data-[popup-open]:bg-[var(--ui-button-secondary-container-color-active)] data-[popup-open]:text-[var(--ui-button-secondary-label-color-active)]',\n className\n )}\n />\n }\n >\n {label}\n <ChevronDownIcon\n size={16}\n className=\"transition-transform group-data-[popup-open]:rotate-180\"\n />\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-96 p-0\">\n <form\n onSubmit={(event) => {\n event.preventDefault();\n handleApply();\n }}\n >\n <div className=\"flex max-h-80 flex-col gap-4 overflow-y-auto p-4\">\n <FilterSearchFiltersContext.Provider value={filterContext}>\n {children}\n </FilterSearchFiltersContext.Provider>\n </div>\n <div className=\"flex items-center gap-2 border-t border-border p-4\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n disabled={resetDisabled}\n onClick={handleReset}\n >\n Reset filters\n </Button>\n <div className=\"ms-auto flex items-center gap-2\">\n <Button type=\"button\" variant=\"secondary\" onClick={handleCancel}>\n Cancel\n </Button>\n <Button type=\"submit\" variant=\"default\" disabled={applyDisabled}>\n Apply\n </Button>\n </div>\n </div>\n </form>\n </PopoverContent>\n </Popover>\n );\n});\nFilterSearchFilters.displayName = 'FilterSearchFilters';\n\nfunction defaultFilterChipLabel(key: string, value: unknown): string {\n const text = Array.isArray(value)\n ? value.map((entry) => String(entry)).join(', ')\n : typeof value === 'object' && value !== null\n ? JSON.stringify(value)\n : String(value);\n return `${key}: ${text}`;\n}\n\nexport interface FilterSearchAppliedFiltersProps\n extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {\n /** The currently applied filter values, keyed by an arbitrary consumer-chosen id. */\n filters: Record<string, unknown>;\n /** Called with the remaining filters when a chip is removed or \"Reset filters\" clears all. */\n onValueChange: (next: Record<string, unknown>) => void;\n /** Format an applied-filter chip's label. Defaults to `\"<key>: <value>\"`. */\n getFilterChipLabel?: (key: string, value: unknown) => React.ReactNode;\n}\n\n/**\n * Renders one removable chip per applied filter plus a \"Reset filters\" action\n * that clears all filters immediately (no popover involved). Renders nothing\n * when `filters` is empty.\n */\nconst FilterSearchAppliedFilters = React.forwardRef<\n HTMLDivElement,\n FilterSearchAppliedFiltersProps\n>(({ className, filters, onValueChange, getFilterChipLabel, ...props }, ref) => {\n const keys = Object.keys(filters);\n if (keys.length === 0) return null;\n\n const handleRemove = (key: string) => {\n const next = { ...filters };\n delete next[key];\n onValueChange(next);\n };\n\n return (\n <div\n ref={ref}\n className={cn('flex flex-wrap items-center gap-3', className)}\n {...props}\n >\n {keys.map((key) => (\n <Chip\n key={key}\n variant=\"removable\"\n onRemove={() => handleRemove(key)}\n removeLabel={`Remove ${key} filter`}\n >\n {getFilterChipLabel\n ? getFilterChipLabel(key, filters[key])\n : defaultFilterChipLabel(key, filters[key])}\n </Chip>\n ))}\n <Button\n type=\"button\"\n variant=\"ghost\"\n onClick={() => onValueChange({})}\n >\n Reset filters\n </Button>\n </div>\n );\n});\nFilterSearchAppliedFilters.displayName = 'FilterSearchAppliedFilters';\n\nexport {\n FilterSearch,\n FilterSearchActions,\n FilterSearchFilters,\n FilterSearchAppliedFilters,\n};\n"],"names":["FilterSearch","React","className","props","ref","jsx","cn","FilterSearchActions","FilterSearchFiltersContext","useFilterSearchFilters","context","FilterSearchFilters","value","onValueChange","onApply","label","children","open","setOpen","draft","setDraft","handleOpenChange","nextOpen","setFilter","key","filterValue","previous","next","filterContext","handleReset","handleCancel","handleApply","resetDisabled","applyDisabled","deepEqual","jsxs","Popover","PopoverTrigger","Button","ChevronDownIcon","PopoverContent","event","defaultFilterChipLabel","text","entry","FilterSearchAppliedFilters","filters","getFilterChipLabel","keys","handleRemove","Chip"],"mappings":"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"}
1
+ {"version":3,"file":"filter-search.js","sources":["../../../../src/components/ui/filter-search/filter-search.tsx"],"sourcesContent":["import * as React from 'react';\nimport { ChevronDownIcon } from '@acronis-platform/icons-react/stroke-mono';\n\nimport { cn } from '@/lib/utils';\nimport { deepEqual } from '@/lib/deep-equal';\nimport { Button } from '../button';\nimport { Chip } from '../chip';\nimport { Popover, PopoverContent, PopoverTrigger } from '../popover';\n\n// A composable toolbar for data tables: arranges a search field, optional\n// filters button, optional tenant/scope switcher, and trailing action buttons\n// in a single horizontal row. Maps to the Figma \"FilterSearch\" component. The\n// root container uses 16px inter-item gap (Figma `gap/gap-16`) and centers\n// children vertically. `FilterSearchActions` is a right-aligned flex area that\n// pushes its content to the trailing edge.\n//\n// No dedicated token tier — spacing is the Figma `gap/gap-16` semantic token\n// (16px), and each child (Search, ButtonMenu, Select) brings its own\n// `--ui-*` tier.\n//\n// `FilterSearchFilters` and `FilterSearchAppliedFilters` are design-pending\n// additions (no Figma node yet, unlike the FilterSearch node itself): the\n// filter popover (trigger + form + Reset/Cancel/Apply) and the applied-filter\n// chip row + top-level \"Reset filters\" affordance. Compose `FilterSearchFilters`\n// as a child of `<FilterSearch>` in place of a plain filter trigger, and render\n// `<FilterSearchAppliedFilters>` as a sibling row below it. Reconcile against\n// the real design with `/figma-component FilterSearchFilters <url> --update`\n// once a mockup lands.\n\nexport type FilterSearchProps = React.ComponentPropsWithoutRef<'div'>;\n\nconst FilterSearch = React.forwardRef<HTMLDivElement, FilterSearchProps>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex items-center gap-4', className)}\n {...props}\n />\n )\n);\nFilterSearch.displayName = 'FilterSearch';\n\nexport type FilterSearchActionsProps = React.ComponentPropsWithoutRef<'div'>;\n\nconst FilterSearchActions = React.forwardRef<\n HTMLDivElement,\n FilterSearchActionsProps\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex h-8 min-w-px flex-1 items-center justify-end gap-4', className)}\n {...props}\n />\n));\nFilterSearchActions.displayName = 'FilterSearchActions';\n\ninterface FilterSearchFiltersContextValue {\n /** The current draft filters being edited inside the popover. */\n filters: Record<string, unknown>;\n /** Set a single draft filter value by key. */\n setFilter: (key: string, value: unknown) => void;\n}\n\nconst FilterSearchFiltersContext =\n React.createContext<FilterSearchFiltersContextValue | null>(null);\n\n/**\n * Bind a filter field child (rendered inside `FilterSearchFilters`'s popover) to\n * the component's draft filters. Returns the current draft map and a per-key\n * setter. Field children are plain JSX (no render prop) and wire their own\n * `onChange` to `setFilter`; Apply commits the draft, Cancel/dismiss reverts it.\n */\nexport function useFilterSearchFilters(): FilterSearchFiltersContextValue {\n const context = React.useContext(FilterSearchFiltersContext);\n if (context === null) {\n throw new Error(\n 'useFilterSearchFilters must be used within a <FilterSearchFilters> popover.'\n );\n }\n return context;\n}\n\nexport interface FilterSearchFiltersProps\n extends Omit<React.ComponentPropsWithoutRef<'button'>, 'value' | 'onValueChange'> {\n /** The applied filter values, keyed by an arbitrary consumer-chosen id. */\n value: Record<string, unknown>;\n /** Called with the committed filters when Apply is pressed. */\n onValueChange: (next: Record<string, unknown>) => void;\n /** Called with the newly applied filters when Apply is pressed (fires alongside `onValueChange`). */\n onApply?: (filters: Record<string, unknown>) => void;\n /** Label for the filter trigger button. */\n label?: React.ReactNode;\n /**\n * Filter field children rendered inside the popover form. Plain children —\n * wire each field to the draft via `useFilterSearchFilters`. Group fields with\n * a `Separator` as a layout convention if desired.\n */\n children?: React.ReactNode;\n /** Additional classes merged onto the trigger button. */\n className?: string;\n}\n\nconst FilterSearchFilters = React.forwardRef<\n HTMLButtonElement,\n FilterSearchFiltersProps\n>(({ value, onValueChange, onApply, label = 'Filters', children, className, ...rest }, ref) => {\n const [open, setOpen] = React.useState(false);\n const [draft, setDraft] = React.useState<Record<string, unknown>>(value);\n\n const handleOpenChange = (nextOpen: boolean) => {\n // Snapshot the applied filters on open; revert the draft on any dismiss\n // (outside press / Escape) so an un-applied edit never leaks out.\n setDraft(value);\n setOpen(nextOpen);\n };\n\n const setFilter = React.useCallback((key: string, filterValue: unknown) => {\n setDraft((previous) => {\n if (filterValue === undefined) {\n if (!(key in previous)) return previous;\n const next = { ...previous };\n delete next[key];\n return next;\n }\n return { ...previous, [key]: filterValue };\n });\n }, []);\n\n const filterContext = React.useMemo<FilterSearchFiltersContextValue>(\n () => ({ filters: draft, setFilter }),\n [draft, setFilter]\n );\n\n const handleReset = () => setDraft({});\n\n const handleCancel = () => {\n setDraft(value);\n setOpen(false);\n };\n\n const handleApply = () => {\n onValueChange(draft);\n onApply?.(draft);\n setOpen(false);\n };\n\n const resetDisabled = Object.keys(draft).length === 0;\n const applyDisabled = deepEqual(draft, value);\n\n return (\n <Popover open={open} onOpenChange={handleOpenChange}>\n <PopoverTrigger\n ref={ref}\n render={\n <Button\n variant=\"secondary\"\n className={cn(\n 'group data-[popup-open]:border-[var(--ui-button-secondary-container-border-color-active)] data-[popup-open]:bg-[var(--ui-button-secondary-container-color-active)] data-[popup-open]:text-[var(--ui-button-secondary-label-color-active)]',\n className\n )}\n {...rest}\n />\n }\n >\n {label}\n <ChevronDownIcon\n size={16}\n className=\"transition-transform group-data-[popup-open]:rotate-180\"\n />\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-96 p-0\">\n <form\n onSubmit={(event) => {\n event.preventDefault();\n handleApply();\n }}\n >\n <div className=\"flex max-h-80 flex-col gap-4 overflow-y-auto p-4\">\n <FilterSearchFiltersContext.Provider value={filterContext}>\n {children}\n </FilterSearchFiltersContext.Provider>\n </div>\n <div className=\"flex items-center gap-2 border-t border-border p-4\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n disabled={resetDisabled}\n onClick={handleReset}\n >\n Reset filters\n </Button>\n <div className=\"ms-auto flex items-center gap-2\">\n <Button type=\"button\" variant=\"secondary\" onClick={handleCancel}>\n Cancel\n </Button>\n <Button type=\"submit\" variant=\"default\" disabled={applyDisabled}>\n Apply\n </Button>\n </div>\n </div>\n </form>\n </PopoverContent>\n </Popover>\n );\n});\nFilterSearchFilters.displayName = 'FilterSearchFilters';\n\nfunction defaultFilterChipLabel(key: string, value: unknown): string {\n const text = Array.isArray(value)\n ? value.map((entry) => String(entry)).join(', ')\n : typeof value === 'object' && value !== null\n ? JSON.stringify(value)\n : String(value);\n return `${key}: ${text}`;\n}\n\nexport interface FilterSearchAppliedFiltersProps\n extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {\n /** The currently applied filter values, keyed by an arbitrary consumer-chosen id. */\n filters: Record<string, unknown>;\n /** Called with the remaining filters when a chip is removed or \"Reset filters\" clears all. */\n onValueChange: (next: Record<string, unknown>) => void;\n /** Format an applied-filter chip's label. Defaults to `\"<key>: <value>\"`. */\n getFilterChipLabel?: (key: string, value: unknown) => React.ReactNode;\n}\n\n/**\n * Renders one removable chip per applied filter plus a \"Reset filters\" action\n * that clears all filters immediately (no popover involved). Renders nothing\n * when `filters` is empty.\n */\nconst FilterSearchAppliedFilters = React.forwardRef<\n HTMLDivElement,\n FilterSearchAppliedFiltersProps\n>(({ className, filters, onValueChange, getFilterChipLabel, ...props }, ref) => {\n const keys = Object.keys(filters);\n if (keys.length === 0) return null;\n\n const handleRemove = (key: string) => {\n const next = { ...filters };\n delete next[key];\n onValueChange(next);\n };\n\n return (\n <div\n ref={ref}\n className={cn('flex flex-wrap items-center gap-3', className)}\n {...props}\n >\n {keys.map((key) => (\n <Chip\n key={key}\n variant=\"removable\"\n onRemove={() => handleRemove(key)}\n removeLabel={`Remove ${key} filter`}\n >\n {getFilterChipLabel\n ? getFilterChipLabel(key, filters[key])\n : defaultFilterChipLabel(key, filters[key])}\n </Chip>\n ))}\n <Button\n type=\"button\"\n variant=\"ghost\"\n onClick={() => onValueChange({})}\n >\n Reset filters\n </Button>\n </div>\n );\n});\nFilterSearchAppliedFilters.displayName = 'FilterSearchAppliedFilters';\n\nexport {\n FilterSearch,\n FilterSearchActions,\n FilterSearchFilters,\n FilterSearchAppliedFilters,\n};\n"],"names":["FilterSearch","React","className","props","ref","jsx","cn","FilterSearchActions","FilterSearchFiltersContext","useFilterSearchFilters","context","FilterSearchFilters","value","onValueChange","onApply","label","children","rest","open","setOpen","draft","setDraft","handleOpenChange","nextOpen","setFilter","key","filterValue","previous","next","filterContext","handleReset","handleCancel","handleApply","resetDisabled","applyDisabled","deepEqual","jsxs","Popover","PopoverTrigger","Button","ChevronDownIcon","PopoverContent","event","defaultFilterChipLabel","text","entry","FilterSearchAppliedFilters","filters","getFilterChipLabel","keys","handleRemove","Chip"],"mappings":";;;;;;;;AA+BA,MAAMA,IAAeC,EAAM;AAAA,EACzB,CAAC,EAAE,WAAAC,GAAW,GAAGC,EAAA,GAASC,MACxB,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAD;AAAA,MACA,WAAWE,EAAG,2BAA2BJ,CAAS;AAAA,MACjD,GAAGC;AAAA,IAAA;AAAA,EAAA;AAGV;AACAH,EAAa,cAAc;AAI3B,MAAMO,IAAsBN,EAAM,WAGhC,CAAC,EAAE,WAAAC,GAAW,GAAGC,EAAA,GAASC,MAC1B,gBAAAC;AAAA,EAAC;AAAA,EAAA;AAAA,IACC,KAAAD;AAAA,IACA,WAAWE,EAAG,2DAA2DJ,CAAS;AAAA,IACjF,GAAGC;AAAA,EAAA;AACN,CACD;AACDI,EAAoB,cAAc;AASlC,MAAMC,IACJP,EAAM,cAAsD,IAAI;AAQ3D,SAASQ,IAA0D;AACxE,QAAMC,IAAUT,EAAM,WAAWO,CAA0B;AAC3D,MAAIE,MAAY;AACd,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAGJ,SAAOA;AACT;AAsBA,MAAMC,IAAsBV,EAAM,WAGhC,CAAC,EAAE,OAAAW,GAAO,eAAAC,GAAe,SAAAC,GAAS,OAAAC,IAAQ,WAAW,UAAAC,GAAU,WAAAd,GAAW,GAAGe,EAAA,GAAQb,MAAQ;AAC7F,QAAM,CAACc,GAAMC,CAAO,IAAIlB,EAAM,SAAS,EAAK,GACtC,CAACmB,GAAOC,CAAQ,IAAIpB,EAAM,SAAkCW,CAAK,GAEjEU,IAAmB,CAACC,MAAsB;AAG9C,IAAAF,EAAST,CAAK,GACdO,EAAQI,CAAQ;AAAA,EAClB,GAEMC,IAAYvB,EAAM,YAAY,CAACwB,GAAaC,MAAyB;AACzE,IAAAL,EAAS,CAACM,MAAa;AACrB,UAAID,MAAgB,QAAW;AAC7B,YAAI,EAAED,KAAOE,GAAW,QAAOA;AAC/B,cAAMC,IAAO,EAAE,GAAGD,EAAA;AAClB,sBAAOC,EAAKH,CAAG,GACRG;AAAA,MACT;AACA,aAAO,EAAE,GAAGD,GAAU,CAACF,CAAG,GAAGC,EAAA;AAAA,IAC/B,CAAC;AAAA,EACH,GAAG,CAAA,CAAE,GAECG,IAAgB5B,EAAM;AAAA,IAC1B,OAAO,EAAE,SAASmB,GAAO,WAAAI;IACzB,CAACJ,GAAOI,CAAS;AAAA,EAAA,GAGbM,IAAc,MAAMT,EAAS,EAAE,GAE/BU,IAAe,MAAM;AACzB,IAAAV,EAAST,CAAK,GACdO,EAAQ,EAAK;AAAA,EACf,GAEMa,IAAc,MAAM;AACxB,IAAAnB,EAAcO,CAAK,GACnBN,KAAA,QAAAA,EAAUM,IACVD,EAAQ,EAAK;AAAA,EACf,GAEMc,IAAgB,OAAO,KAAKb,CAAK,EAAE,WAAW,GAC9Cc,IAAgBC,EAAUf,GAAOR,CAAK;AAE5C,SACE,gBAAAwB,EAACC,GAAA,EAAQ,MAAAnB,GAAY,cAAcI,GACjC,UAAA;AAAA,IAAA,gBAAAc;AAAA,MAACE;AAAA,MAAA;AAAA,QACC,KAAAlC;AAAA,QACA,QACE,gBAAAC;AAAA,UAACkC;AAAA,UAAA;AAAA,YACC,SAAQ;AAAA,YACR,WAAWjC;AAAA,cACT;AAAA,cACAJ;AAAA,YAAA;AAAA,YAED,GAAGe;AAAA,UAAA;AAAA,QAAA;AAAA,QAIP,UAAA;AAAA,UAAAF;AAAA,UACD,gBAAAV;AAAA,YAACmC;AAAA,YAAA;AAAA,cACC,MAAM;AAAA,cACN,WAAU;AAAA,YAAA;AAAA,UAAA;AAAA,QACZ;AAAA,MAAA;AAAA,IAAA;AAAA,IAEF,gBAAAnC,EAACoC,GAAA,EAAe,OAAM,SAAQ,WAAU,YACtC,UAAA,gBAAAL;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,UAAU,CAACM,MAAU;AACnB,UAAAA,EAAM,eAAA,GACNV,EAAA;AAAA,QACF;AAAA,QAEA,UAAA;AAAA,UAAA,gBAAA3B,EAAC,OAAA,EAAI,WAAU,oDACb,UAAA,gBAAAA,EAACG,EAA2B,UAA3B,EAAoC,OAAOqB,GACzC,UAAAb,EAAA,CACH,EAAA,CACF;AAAA,UACA,gBAAAoB,EAAC,OAAA,EAAI,WAAU,sDACb,UAAA;AAAA,YAAA,gBAAA/B;AAAA,cAACkC;AAAA,cAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAQ;AAAA,gBACR,UAAUN;AAAA,gBACV,SAASH;AAAA,gBACV,UAAA;AAAA,cAAA;AAAA,YAAA;AAAA,YAGD,gBAAAM,EAAC,OAAA,EAAI,WAAU,mCACb,UAAA;AAAA,cAAA,gBAAA/B,EAACkC,KAAO,MAAK,UAAS,SAAQ,aAAY,SAASR,GAAc,UAAA,SAAA,CAEjE;AAAA,cACA,gBAAA1B,EAACkC,KAAO,MAAK,UAAS,SAAQ,WAAU,UAAUL,GAAe,UAAA,QAAA,CAEjE;AAAA,YAAA,EAAA,CACF;AAAA,UAAA,EAAA,CACF;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA,EACF,CACF;AAAA,EAAA,GACF;AAEJ,CAAC;AACDvB,EAAoB,cAAc;AAElC,SAASgC,EAAuBlB,GAAab,GAAwB;AACnE,QAAMgC,IAAO,MAAM,QAAQhC,CAAK,IAC5BA,EAAM,IAAI,CAACiC,MAAU,OAAOA,CAAK,CAAC,EAAE,KAAK,IAAI,IAC7C,OAAOjC,KAAU,YAAYA,MAAU,OACrC,KAAK,UAAUA,CAAK,IACpB,OAAOA,CAAK;AAClB,SAAO,GAAGa,CAAG,KAAKmB,CAAI;AACxB;AAiBA,MAAME,IAA6B7C,EAAM,WAGvC,CAAC,EAAE,WAAAC,GAAW,SAAA6C,GAAS,eAAAlC,GAAe,oBAAAmC,GAAoB,GAAG7C,EAAA,GAASC,MAAQ;AAC9E,QAAM6C,IAAO,OAAO,KAAKF,CAAO;AAChC,MAAIE,EAAK,WAAW,EAAG,QAAO;AAE9B,QAAMC,IAAe,CAACzB,MAAgB;AACpC,UAAMG,IAAO,EAAE,GAAGmB,EAAA;AAClB,WAAOnB,EAAKH,CAAG,GACfZ,EAAce,CAAI;AAAA,EACpB;AAEA,SACE,gBAAAQ;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAhC;AAAA,MACA,WAAWE,EAAG,qCAAqCJ,CAAS;AAAA,MAC3D,GAAGC;AAAA,MAEH,UAAA;AAAA,QAAA8C,EAAK,IAAI,CAACxB,MACT,gBAAApB;AAAA,UAAC8C;AAAA,UAAA;AAAA,YAEC,SAAQ;AAAA,YACR,UAAU,MAAMD,EAAazB,CAAG;AAAA,YAChC,aAAa,UAAUA,CAAG;AAAA,YAEzB,UAAAuB,IACGA,EAAmBvB,GAAKsB,EAAQtB,CAAG,CAAC,IACpCkB,EAAuBlB,GAAKsB,EAAQtB,CAAG,CAAC;AAAA,UAAA;AAAA,UAPvCA;AAAA,QAAA,CASR;AAAA,QACD,gBAAApB;AAAA,UAACkC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,SAAS,MAAM1B,EAAc,EAAE;AAAA,YAChC,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MAED;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC;AACDiC,EAA2B,cAAc;"}
@@ -0,0 +1,87 @@
1
+ import { jsx as t, jsxs as i } from "react/jsx-runtime";
2
+ import * as g from "react";
3
+ import { cva as v } from "../../../node_modules/.pnpm/class-variance-authority@0.7.1/node_modules/class-variance-authority/dist/index.js";
4
+ import { FunnelChart as F, Funnel as N, Cell as b, LabelList as x } from "recharts";
5
+ import { cn as L } from "../../../lib/utils.js";
6
+ import { ChartContainer as T, ChartTooltip as $, ChartTooltipContent as j } from "../chart/chart.js";
7
+ const A = v("", {
8
+ variants: {
9
+ lastShape: {
10
+ triangle: "",
11
+ rectangle: ""
12
+ }
13
+ },
14
+ defaultVariants: {
15
+ lastShape: "triangle"
16
+ }
17
+ }), R = g.forwardRef(
18
+ ({
19
+ className: n,
20
+ config: o,
21
+ data: s,
22
+ dataKey: c,
23
+ nameKey: a,
24
+ lastShape: e = "triangle",
25
+ reversed: f = !1,
26
+ showLabels: h = !0,
27
+ showTooltip: m = !0,
28
+ tooltipContent: p,
29
+ ...d
30
+ }, u) => {
31
+ const l = s.map((r) => ({
32
+ ...r,
33
+ fill: `var(--color-${r[a]})`
34
+ }));
35
+ return /* @__PURE__ */ t(
36
+ "div",
37
+ {
38
+ ref: u,
39
+ "data-last-shape": e,
40
+ className: L(A({ lastShape: e }), n),
41
+ ...d,
42
+ children: /* @__PURE__ */ t(T, { config: o, className: "size-full", children: /* @__PURE__ */ i(F, { margin: { top: 8, right: 96, bottom: 8, left: 24 }, children: [
43
+ m && /* @__PURE__ */ t(
44
+ $,
45
+ {
46
+ content: p ?? /* @__PURE__ */ t(j, { nameKey: a, hideLabel: !0 })
47
+ }
48
+ ),
49
+ /* @__PURE__ */ i(
50
+ N,
51
+ {
52
+ dataKey: c,
53
+ nameKey: a,
54
+ data: l,
55
+ lastShapeType: e ?? "triangle",
56
+ reversed: f,
57
+ isAnimationActive: !1,
58
+ children: [
59
+ l.map((r, C) => (
60
+ // Keyed by index, not the name: two stages could share a nameKey
61
+ // value, which would collide as a React key. Same-named stages
62
+ // intentionally share a color/config entry via `--color-<name>`.
63
+ /* @__PURE__ */ t(b, { fill: `var(--color-${r[a]})` }, C)
64
+ )),
65
+ h && /* @__PURE__ */ t(
66
+ x,
67
+ {
68
+ position: "right",
69
+ dataKey: a,
70
+ className: "fill-foreground",
71
+ stroke: "none"
72
+ }
73
+ )
74
+ ]
75
+ }
76
+ )
77
+ ] }) })
78
+ }
79
+ );
80
+ }
81
+ );
82
+ R.displayName = "FunnelChart";
83
+ export {
84
+ R as FunnelChart,
85
+ A as funnelChartVariants
86
+ };
87
+ //# sourceMappingURL=funnel-chart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"funnel-chart.js","sources":["../../../../src/components/ui/funnel-chart/funnel-chart.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { Cell, Funnel, FunnelChart as RechartsFunnelChart, LabelList } from 'recharts';\n\nimport { cn } from '@/lib/utils';\nimport {\n ChartContainer,\n ChartTooltip,\n ChartTooltipContent,\n type ChartConfig,\n} from '../chart';\n\n// A typed recharts composition over the shared `Chart` primitives. The single\n// CVA axis is the design's Funnel-chart variant set: `lastShape` (does the\n// funnel narrow to a point — `triangle` — or end flat — `rectangle`). The class\n// stays empty because recharts' SVG — not CSS — draws the funnel: `lastShape`\n// drives the `<Funnel>`'s `lastShapeType`. CVA is kept so the variant set is a\n// first-class, spec-conformant part of the API (matched against ui-spec's\n// api.yaml enums) and exposed via `VariantProps`; the resolved value is also\n// mirrored onto `data-last-shape`.\nconst funnelChartVariants = cva('', {\n variants: {\n lastShape: {\n triangle: '',\n rectangle: '',\n },\n },\n defaultVariants: {\n lastShape: 'triangle',\n },\n});\n\nexport interface FunnelChartProps\n extends Omit<React.ComponentProps<'div'>, 'children'>,\n VariantProps<typeof funnelChartVariants> {\n /** Row-per-stage data. Each object holds the stage's `nameKey` label + its `dataKey` numeric value. */\n data: ReadonlyArray<Record<string, string | number>>;\n /**\n * Per-stage map of `label` / `color`, keyed by the stage's `nameKey` value\n * (imported from the shared `Chart` primitives). Turned into `--color-<name>`\n * custom properties. Colors are caller-supplied — reference an existing\n * semantic `--ui-*` token; there is no chart palette tier yet.\n */\n config: ChartConfig;\n /** Numeric field that sizes each stage (the funnel narrows as it drops). */\n dataKey: string;\n /**\n * Label field that names each stage (drives the legend, tooltip, on-chart\n * labels, and the `--color-<name>` lookup). Values should be unique per chart —\n * stages sharing a name share one `config`/color entry.\n */\n nameKey: string;\n /** Flip the funnel so it widens toward the bottom instead of narrowing. */\n reversed?: boolean;\n /** Render each stage's name beside its segment. */\n showLabels?: boolean;\n showTooltip?: boolean;\n /**\n * Replace the default tooltip. Pass a configured `ChartTooltipContent`\n * (imported from this library) — e.g. with a `formatter` / `labelFormatter` —\n * to customize formatting, per-series rows, or extra fields without composing\n * recharts yourself. Ignored when `showTooltip` is false.\n */\n tooltipContent?: React.ComponentProps<typeof ChartTooltip>['content'];\n}\n\nconst FunnelChart = React.forwardRef<HTMLDivElement, FunnelChartProps>(\n (\n {\n className,\n config,\n data,\n dataKey,\n nameKey,\n lastShape = 'triangle',\n reversed = false,\n showLabels = true,\n showTooltip = true,\n tooltipContent,\n ...props\n },\n ref\n ) => {\n // Stamp each row with its `fill` (the shadcn data-driven pattern). A Funnel's\n // default fill is grey (#808080) and recharts doesn't carry a per-segment\n // color on the tooltip/legend payload item, so putting the color on the data\n // row is what lets a real hover resolve each segment's color. (The forced-open\n // `defaultIndex` VR snapshot can still show a neutral indicator — it\n // synthesizes the open state without a pointer hover.)\n const seriesData: Record<string, string | number>[] = data.map((row) => ({\n ...row,\n fill: `var(--color-${row[nameKey]})`,\n }));\n\n return (\n <div\n ref={ref}\n data-last-shape={lastShape}\n className={cn(funnelChartVariants({ lastShape }), className)}\n {...props}\n >\n <ChartContainer config={config} className=\"size-full\">\n <RechartsFunnelChart margin={{ top: 8, right: 96, bottom: 8, left: 24 }}>\n {showTooltip && (\n <ChartTooltip\n content={\n tooltipContent ?? (\n <ChartTooltipContent nameKey={nameKey} hideLabel />\n )\n }\n />\n )}\n <Funnel\n dataKey={dataKey}\n nameKey={nameKey}\n data={seriesData}\n lastShapeType={lastShape ?? 'triangle'}\n reversed={reversed}\n isAnimationActive={false}\n >\n {seriesData.map((entry, index) => (\n // Keyed by index, not the name: two stages could share a nameKey\n // value, which would collide as a React key. Same-named stages\n // intentionally share a color/config entry via `--color-<name>`.\n <Cell key={index} fill={`var(--color-${entry[nameKey]})`} />\n ))}\n {showLabels && (\n <LabelList\n position=\"right\"\n dataKey={nameKey}\n className=\"fill-foreground\"\n stroke=\"none\"\n />\n )}\n </Funnel>\n </RechartsFunnelChart>\n </ChartContainer>\n </div>\n );\n }\n);\nFunnelChart.displayName = 'FunnelChart';\n\nexport { FunnelChart, funnelChartVariants };\n"],"names":["funnelChartVariants","cva","FunnelChart","React","className","config","data","dataKey","nameKey","lastShape","reversed","showLabels","showTooltip","tooltipContent","props","ref","seriesData","row","jsx","cn","ChartContainer","RechartsFunnelChart","ChartTooltip","ChartTooltipContent","jsxs","Funnel","entry","index","Cell","LabelList"],"mappings":";;;;;;AAsBA,MAAMA,IAAsBC,EAAI,IAAI;AAAA,EAClC,UAAU;AAAA,IACR,WAAW;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,IAAA;AAAA,EACb;AAAA,EAEF,iBAAiB;AAAA,IACf,WAAW;AAAA,EAAA;AAEf,CAAC,GAoCKC,IAAcC,EAAM;AAAA,EACxB,CACE;AAAA,IACE,WAAAC;AAAA,IACA,QAAAC;AAAA,IACA,MAAAC;AAAA,IACA,SAAAC;AAAA,IACA,SAAAC;AAAA,IACA,WAAAC,IAAY;AAAA,IACZ,UAAAC,IAAW;AAAA,IACX,YAAAC,IAAa;AAAA,IACb,aAAAC,IAAc;AAAA,IACd,gBAAAC;AAAA,IACA,GAAGC;AAAA,EAAA,GAELC,MACG;AAOH,UAAMC,IAAgDV,EAAK,IAAI,CAACW,OAAS;AAAA,MACvE,GAAGA;AAAA,MACH,MAAM,eAAeA,EAAIT,CAAO,CAAC;AAAA,IAAA,EACjC;AAEF,WACE,gBAAAU;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAAH;AAAA,QACA,mBAAiBN;AAAA,QACjB,WAAWU,EAAGnB,EAAoB,EAAE,WAAAS,EAAA,CAAW,GAAGL,CAAS;AAAA,QAC1D,GAAGU;AAAA,QAEJ,4BAACM,GAAA,EAAe,QAAAf,GAAgB,WAAU,aACxC,4BAACgB,GAAA,EAAoB,QAAQ,EAAE,KAAK,GAAG,OAAO,IAAI,QAAQ,GAAG,MAAM,MAChE,UAAA;AAAA,UAAAT,KACC,gBAAAM;AAAA,YAACI;AAAA,YAAA;AAAA,cACC,SACET,KACE,gBAAAK,EAACK,GAAA,EAAoB,SAAAf,GAAkB,WAAS,GAAA,CAAC;AAAA,YAAA;AAAA,UAAA;AAAA,UAKzD,gBAAAgB;AAAA,YAACC;AAAA,YAAA;AAAA,cACC,SAAAlB;AAAA,cACA,SAAAC;AAAA,cACA,MAAMQ;AAAA,cACN,eAAeP,KAAa;AAAA,cAC5B,UAAAC;AAAA,cACA,mBAAmB;AAAA,cAElB,UAAA;AAAA,gBAAAM,EAAW,IAAI,CAACU,GAAOC;AAAA;AAAA;AAAA;AAAA,kBAItB,gBAAAT,EAACU,KAAiB,MAAM,eAAeF,EAAMlB,CAAO,CAAC,OAA1CmB,CAA+C;AAAA,iBAC3D;AAAA,gBACAhB,KACC,gBAAAO;AAAA,kBAACW;AAAA,kBAAA;AAAA,oBACC,UAAS;AAAA,oBACT,SAASrB;AAAA,oBACT,WAAU;AAAA,oBACV,QAAO;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACT;AAAA,YAAA;AAAA,UAAA;AAAA,QAEJ,EAAA,CACF,EAAA,CACF;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AACF;AACAN,EAAY,cAAc;"}
@@ -0,0 +1,108 @@
1
+ import { jsx as s, jsxs as N } from "react/jsx-runtime";
2
+ import * as g from "react";
3
+ import { BarChart as w, CartesianGrid as A, XAxis as B, YAxis as T, Bar as b } from "recharts";
4
+ import { cn as H } from "../../../lib/utils.js";
5
+ import { ChartContainer as $, ChartTooltip as j, ChartTooltipContent as C } from "../chart/chart.js";
6
+ const v = (l) => Number(l.toFixed(2));
7
+ function F(l, i, d) {
8
+ const r = l.filter(
9
+ (e) => typeof e == "number" && Number.isFinite(e)
10
+ );
11
+ if (r.length === 0 || i < 1) return [];
12
+ const [c, m] = d ?? [Math.min(...r), Math.max(...r)], t = Math.min(c, m), n = Math.max(c, m), f = t === n ? t + 1 : n, a = (f - t) / i, h = Array.from({ length: i }, (e, o) => {
13
+ const u = t + o * a, p = o === i - 1 ? f : t + (o + 1) * a;
14
+ return { x0: u, x1: p, label: `${v(u)}–${v(p)}`, count: 0 };
15
+ });
16
+ for (const e of r) {
17
+ if (e < t || e > f) continue;
18
+ let o = Math.floor((e - t) / a);
19
+ o >= i && (o = i - 1), h[o].count += 1;
20
+ }
21
+ return h;
22
+ }
23
+ const G = g.forwardRef(
24
+ ({
25
+ className: l,
26
+ config: i,
27
+ values: d,
28
+ dataKey: r = "count",
29
+ binCount: c = 10,
30
+ domain: m,
31
+ xAxisLabel: t,
32
+ yAxisLabel: n,
33
+ yUnit: f,
34
+ barRadius: a = 0,
35
+ showGrid: h = !0,
36
+ showTooltip: e = !0,
37
+ tooltipContent: o,
38
+ ...u
39
+ }, p) => {
40
+ const M = t ? { value: t, position: "insideBottom", offset: 0 } : void 0, k = n ? {
41
+ value: n,
42
+ angle: -90,
43
+ position: "insideLeft",
44
+ style: { textAnchor: "middle" }
45
+ } : void 0, y = g.useMemo(
46
+ () => F(d, c, m),
47
+ [d, c, m]
48
+ ).map((x) => ({ label: x.label, [r]: x.count }));
49
+ return /* @__PURE__ */ s(
50
+ "div",
51
+ {
52
+ ref: p,
53
+ className: H(l),
54
+ ...u,
55
+ children: /* @__PURE__ */ s(
56
+ $,
57
+ {
58
+ config: i,
59
+ className: "size-full [&_.recharts-label]:fill-foreground",
60
+ children: /* @__PURE__ */ N(w, { data: y, barCategoryGap: 0, children: [
61
+ h && /* @__PURE__ */ s(A, { vertical: !1 }),
62
+ /* @__PURE__ */ s(
63
+ B,
64
+ {
65
+ dataKey: "label",
66
+ tickLine: !1,
67
+ axisLine: !1,
68
+ height: t ? 48 : void 0,
69
+ label: M
70
+ }
71
+ ),
72
+ /* @__PURE__ */ s(
73
+ T,
74
+ {
75
+ type: "number",
76
+ tickLine: !1,
77
+ axisLine: !1,
78
+ allowDecimals: !1,
79
+ unit: f,
80
+ width: n ? 72 : void 0,
81
+ label: k
82
+ }
83
+ ),
84
+ e && /* @__PURE__ */ s(j, { content: o ?? /* @__PURE__ */ s(C, {}) }),
85
+ /* @__PURE__ */ s(
86
+ b,
87
+ {
88
+ dataKey: r,
89
+ fill: `var(--color-${r})`,
90
+ stroke: "var(--ui-background-surface-primary)",
91
+ strokeWidth: 1,
92
+ radius: a > 0 ? [a, a, 0, 0] : void 0,
93
+ isAnimationActive: !1
94
+ }
95
+ )
96
+ ] })
97
+ }
98
+ )
99
+ }
100
+ );
101
+ }
102
+ );
103
+ G.displayName = "Histogram";
104
+ export {
105
+ G as Histogram,
106
+ F as computeHistogramBins
107
+ };
108
+ //# sourceMappingURL=histogram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"histogram.js","sources":["../../../../src/components/ui/histogram/histogram.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport {\n Bar,\n BarChart as RechartsBarChart,\n CartesianGrid,\n XAxis,\n YAxis,\n} from 'recharts';\n\nimport { cn } from '@/lib/utils';\nimport {\n ChartContainer,\n ChartTooltip,\n ChartTooltipContent,\n type ChartConfig,\n} from '../chart';\n\n// A histogram bins a set of continuous samples into equal-width ranges and plots\n// the frequency of each as contiguous bars — distinct from BarChart, whose bars\n// are pre-categorized. The binning is the component's job (a pure helper), so the\n// consumer passes raw `values`, not pre-aggregated rows. Built on the shared\n// `Chart` primitives; no new tokens (the single series' color is caller-supplied\n// via `config`, same `--color-<key>` mechanism as the other chart types).\n\nexport interface HistogramBin {\n /** Lower bound of the bin (inclusive). */\n x0: number;\n /** Upper bound of the bin (exclusive, except the last bin which is inclusive). */\n x1: number;\n /** Human-readable range label, e.g. `\"0–10\"`. */\n label: string;\n /** How many samples fell in the bin. */\n count: number;\n}\n\nconst roundEdge = (n: number) => Number(n.toFixed(2));\n\n/**\n * Split `values` into `binCount` equal-width bins and count the samples in each.\n * The range is `domain` if given, else the data's [min, max]; values outside the\n * domain are dropped, and the max value lands in the last (inclusive) bin.\n * Returns [] when there's nothing to bin. Exported for unit tests; not part of\n * the package's public API.\n */\nexport function computeHistogramBins(\n values: ReadonlyArray<number>,\n binCount: number,\n domain?: [number, number]\n): HistogramBin[] {\n const nums = values.filter(\n (v): v is number => typeof v === 'number' && Number.isFinite(v)\n );\n if (nums.length === 0 || binCount < 1) return [];\n\n const [d0, d1] = domain ?? [Math.min(...nums), Math.max(...nums)];\n // Normalize an inverted domain ([max, min]) so binning stays robust.\n const min = Math.min(d0, d1);\n const hi = Math.max(d0, d1);\n // A zero-width range (all values equal, or a degenerate domain) can't be\n // divided — widen it by 1 so the single bin has extent.\n const max = min === hi ? min + 1 : hi;\n const width = (max - min) / binCount;\n\n const bins: HistogramBin[] = Array.from({ length: binCount }, (_, i) => {\n const x0 = min + i * width;\n const x1 = i === binCount - 1 ? max : min + (i + 1) * width;\n return { x0, x1, label: `${roundEdge(x0)}–${roundEdge(x1)}`, count: 0 };\n });\n\n for (const v of nums) {\n if (v < min || v > max) continue;\n let idx = Math.floor((v - min) / width);\n if (idx >= binCount) idx = binCount - 1; // the max value\n bins[idx].count += 1;\n }\n return bins;\n}\n\nexport interface HistogramProps\n extends Omit<React.ComponentProps<'div'>, 'children'> {\n /** Raw continuous samples to bin (non-finite values are ignored). */\n values: ReadonlyArray<number>;\n /**\n * Single-series map of `label` / `color`, keyed by `dataKey` (imported from\n * the shared `Chart` primitives). Color is caller-supplied — reference an\n * existing semantic `--ui-*` token; there is no chart palette tier yet.\n */\n config: ChartConfig;\n /** `config` key that colors + labels the frequency bars. */\n dataKey?: string;\n /** Number of equal-width bins. */\n binCount?: number;\n /** Fix the binning range instead of deriving it from the data's min/max. */\n domain?: [number, number];\n /** Title rendered beneath the horizontal (X) axis. */\n xAxisLabel?: string;\n /** Title rendered beside the vertical (Y) axis (rotated). */\n yAxisLabel?: string;\n /** Unit suffix appended to Y-axis tick values (recharts `unit`; the X axis is the bin label). */\n yUnit?: string;\n /** Corner radius on the top of each bar. */\n barRadius?: number;\n showGrid?: boolean;\n showTooltip?: boolean;\n /**\n * Replace the default tooltip. Pass a configured `ChartTooltipContent`\n * (imported from this library) — e.g. with a `formatter` / `labelFormatter` —\n * to customize the tooltip without composing recharts yourself. Ignored when\n * `showTooltip` is false.\n */\n tooltipContent?: React.ComponentProps<typeof ChartTooltip>['content'];\n}\n\nconst Histogram = React.forwardRef<HTMLDivElement, HistogramProps>(\n (\n {\n className,\n config,\n values,\n dataKey = 'count',\n binCount = 10,\n domain,\n xAxisLabel,\n yAxisLabel,\n yUnit,\n barRadius = 0,\n showGrid = true,\n showTooltip = true,\n tooltipContent,\n ...props\n },\n ref\n ) => {\n const xAxisTitle = xAxisLabel\n ? { value: xAxisLabel, position: 'insideBottom' as const, offset: 0 }\n : undefined;\n const yAxisTitle = yAxisLabel\n ? {\n value: yAxisLabel,\n angle: -90,\n position: 'insideLeft' as const,\n style: { textAnchor: 'middle' as const },\n }\n : undefined;\n\n const bins = React.useMemo(\n () => computeHistogramBins(values, binCount, domain),\n [values, binCount, domain]\n );\n // Bars carry the bin `label` as the category and `count` under `dataKey`, so\n // the tooltip resolves the frequency from `config[dataKey]`.\n const data = bins.map((bin) => ({ label: bin.label, [dataKey]: bin.count }));\n\n return (\n <div\n ref={ref}\n className={cn(className)}\n {...props}\n >\n <ChartContainer\n config={config}\n className=\"size-full [&_.recharts-label]:fill-foreground\"\n >\n <RechartsBarChart data={data} barCategoryGap={0}>\n {showGrid && <CartesianGrid vertical={false} />}\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n height={xAxisLabel ? 48 : undefined}\n label={xAxisTitle}\n />\n <YAxis\n type=\"number\"\n tickLine={false}\n axisLine={false}\n allowDecimals={false}\n unit={yUnit}\n width={yAxisLabel ? 72 : undefined}\n label={yAxisTitle}\n />\n {showTooltip && (\n <ChartTooltip content={tooltipContent ?? <ChartTooltipContent />} />\n )}\n <Bar\n dataKey={dataKey}\n fill={`var(--color-${dataKey})`}\n // A hairline in the surface color separates the contiguous bars so\n // they don't blend into one mass (same trick as the Treemap cells).\n stroke=\"var(--ui-background-surface-primary)\"\n strokeWidth={1}\n radius={barRadius > 0 ? [barRadius, barRadius, 0, 0] : undefined}\n isAnimationActive={false}\n />\n </RechartsBarChart>\n </ChartContainer>\n </div>\n );\n }\n);\nHistogram.displayName = 'Histogram';\n\nexport { Histogram };\n"],"names":["roundEdge","n","computeHistogramBins","values","binCount","domain","nums","v","d0","d1","min","hi","max","width","bins","_","i","x0","x1","idx","Histogram","React","className","config","dataKey","xAxisLabel","yAxisLabel","yUnit","barRadius","showGrid","showTooltip","tooltipContent","props","ref","xAxisTitle","yAxisTitle","data","bin","jsx","cn","ChartContainer","jsxs","RechartsBarChart","CartesianGrid","XAxis","YAxis","ChartTooltip","ChartTooltipContent","Bar"],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ import { jsx as h } from "react/jsx-runtime";
2
+ import * as c from "react";
3
+ import { cn as S } from "../../../lib/utils.js";
4
+ const _ = (s, e) => `Digit ${s} of ${e}`, $ = /^[0-9]$/, R = (s, e) => Array.from({ length: e }, (n, p) => s[p] ?? ""), E = c.forwardRef(
5
+ ({
6
+ className: s,
7
+ length: e = 6,
8
+ value: n,
9
+ defaultValue: p,
10
+ onChange: f,
11
+ onComplete: b,
12
+ disabled: w,
13
+ error: m,
14
+ autoFocus: x,
15
+ slotAriaLabel: A = _,
16
+ ...j
17
+ }, k) => {
18
+ const [I, P] = c.useState(
19
+ () => R(p ?? "", e)
20
+ ), y = n != null, u = y ? R(n, e) : I, g = c.useRef([]), d = c.useRef(!1), v = (r) => {
21
+ y || P(r);
22
+ const t = r.join("");
23
+ f == null || f(t), r.every((o) => o !== "") ? d.current || (d.current = !0, b == null || b(t)) : d.current = !1;
24
+ }, a = (r) => {
25
+ const t = g.current[r];
26
+ t == null || t.focus(), t == null || t.select();
27
+ };
28
+ c.useEffect(() => {
29
+ x && a(0);
30
+ }, []);
31
+ const D = (r, t) => {
32
+ const o = [...u];
33
+ let i = r;
34
+ for (const l of t) {
35
+ if (i >= e) break;
36
+ o[i] = l, i += 1;
37
+ }
38
+ v(o), a(Math.min(i, e - 1));
39
+ }, T = (r, t) => {
40
+ if (t.length > 1) {
41
+ const l = t.replace(/\D/g, "");
42
+ l && D(r, l);
43
+ return;
44
+ }
45
+ const o = t;
46
+ if (o !== "" && !$.test(o)) return;
47
+ const i = [...u];
48
+ i[r] = o, v(i), o && r < e - 1 && a(r + 1);
49
+ }, N = (r, t) => {
50
+ if (t.key === "Backspace" && !u[r] && r > 0) {
51
+ t.preventDefault();
52
+ const o = [...u];
53
+ o[r - 1] = "", v(o), a(r - 1);
54
+ } else t.key === "ArrowLeft" && r > 0 ? (t.preventDefault(), a(r - 1)) : t.key === "ArrowRight" && r < e - 1 && (t.preventDefault(), a(r + 1));
55
+ }, L = (r, t) => {
56
+ const o = t.clipboardData.getData("text").replace(/\D/g, "");
57
+ o && (t.preventDefault(), D(r, o));
58
+ };
59
+ return /* @__PURE__ */ h(
60
+ "div",
61
+ {
62
+ ref: k,
63
+ role: "group",
64
+ className: S(
65
+ "inline-flex justify-[var(--ui-input-otp-container-justify-content)] gap-3",
66
+ s
67
+ ),
68
+ ...j,
69
+ children: Array.from({ length: e }, (r, t) => /* @__PURE__ */ h(
70
+ "input",
71
+ {
72
+ ref: (o) => {
73
+ g.current[t] = o;
74
+ },
75
+ type: "text",
76
+ inputMode: "numeric",
77
+ pattern: "[0-9]*",
78
+ autoComplete: "one-time-code",
79
+ maxLength: 1,
80
+ value: u[t],
81
+ placeholder: String(t + 1),
82
+ disabled: w,
83
+ "aria-label": A(t + 1, e),
84
+ "aria-invalid": m || void 0,
85
+ onChange: (o) => T(t, o.target.value),
86
+ onKeyDown: (o) => N(t, o),
87
+ onPaste: (o) => L(t, o),
88
+ onFocus: (o) => o.target.select(),
89
+ className: S(
90
+ "ui-input-otp-value-text-style h-[var(--ui-input-otp-box-height)] w-[var(--ui-input-otp-box-width)] rounded-[var(--ui-input-otp-box-border-radius)] border-[length:var(--ui-input-otp-box-border-width)] bg-[var(--ui-input-otp-box-color)] px-[var(--ui-input-otp-box-padding-x)] py-[var(--ui-input-otp-box-padding-y)] text-center text-[var(--ui-input-otp-value-color)] caret-[var(--ui-input-otp-value-color)] placeholder:text-[var(--ui-input-otp-placeholder-color)] transition-colors focus-visible:outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
91
+ m ? "border-[var(--ui-input-otp-box-border-color-error)] focus-visible:ring-[var(--ui-focus-error)]" : "border-[var(--ui-input-otp-box-border-color-idle)] focus-visible:border-[var(--ui-input-otp-box-border-color-active)] focus-visible:ring-[var(--ui-focus-primary)]"
92
+ )
93
+ },
94
+ t
95
+ ))
96
+ }
97
+ );
98
+ }
99
+ );
100
+ E.displayName = "InputOTP";
101
+ export {
102
+ E as InputOTP
103
+ };
104
+ //# sourceMappingURL=input-otp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-otp.js","sources":["../../../../src/components/ui/input-otp/input-otp.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { cn } from '@/lib/utils';\n\n// Mirrors the Figma \"inputOTP\" component: a bare row of single-character boxes\n// (no label/description — like `Input`, field furniture is a future `Field`\n// wrapper). Themed by the `--ui-input-otp-*` token tier. Each slot is a plain\n// `<input>`; typing a digit advances focus to the next slot, Backspace on an\n// empty slot clears and moves back to the previous one, and pasting — or a\n// browser/password-manager autofill that drops the full code into one slot's\n// `onChange` — distributes it across the slots from the focused one onward.\n// `error`\n// reddens every box (via `--ui-input-otp-box-border-color-error`); there is no\n// disabled treatment in the design, so `disabled` falls back to the generic\n// `disabled:opacity-50` convention used elsewhere in this package. OTP codes\n// are numeric-only by design (SMS/authenticator codes): `inputMode=\"numeric\"`\n// and `pattern=\"[0-9]*\"` are mobile-keyboard hints only, so both typed and\n// pasted input are also filtered to digits in JS — a non-digit keystroke is\n// rejected outright and non-digit characters are stripped from a paste.\nexport interface InputOTPProps\n extends Omit<\n React.ComponentPropsWithoutRef<'div'>,\n 'onChange' | 'defaultValue' | 'children'\n > {\n /** Number of single-character slots. */\n length?: number;\n /** Controlled value (the digits entered so far, left to right). */\n value?: string;\n /** Initial value for uncontrolled usage. */\n defaultValue?: string;\n /** Fires with the full value on every change. */\n onChange?: (value: string) => void;\n /** Fires once, the moment the value reaches `length` characters. */\n onComplete?: (value: string) => void;\n /** Disables every slot. */\n disabled?: boolean;\n /** Switches every slot to the error treatment. */\n error?: boolean;\n /** Focuses the first slot on mount. */\n autoFocus?: boolean;\n /** Builds the accessible name for a slot from its 1-based index and the total length. */\n slotAriaLabel?: (index: number, length: number) => string;\n}\n\nconst defaultSlotAriaLabel = (index: number, length: number) =>\n `Digit ${index} of ${length}`;\n\nconst DIGIT_PATTERN = /^[0-9]$/;\n\n// Fixed-length per-slot array, not a derived dense string — a slot's\n// position must survive edits to other slots (see `commitSlots`).\nconst toSlots = (value: string, length: number): string[] =>\n Array.from({ length }, (_, index) => value[index] ?? '');\n\nconst InputOTP = React.forwardRef<HTMLDivElement, InputOTPProps>(\n (\n {\n className,\n length = 6,\n value,\n defaultValue,\n onChange,\n onComplete,\n disabled,\n error,\n autoFocus,\n slotAriaLabel = defaultSlotAriaLabel,\n ...props\n },\n ref\n ) => {\n const [internalSlots, setInternalSlots] = React.useState(() =>\n toSlots(defaultValue ?? '', length)\n );\n const isControlled = value != null;\n const slots = isControlled ? toSlots(value, length) : internalSlots;\n const slotRefs = React.useRef<Array<HTMLInputElement | null>>([]);\n const completedRef = React.useRef(false);\n\n // `nextSlots` is the source of truth — each index keeps its own slot's\n // character regardless of whether earlier slots are filled. The joined\n // string handed to `onChange`/`onComplete` is a derived value; only\n // `nextSlots.every(...)` can tell whether every slot has been filled,\n // since a joined string with a blank slot collapses indistinguishably\n // from a shorter one.\n const commitSlots = (nextSlots: string[]) => {\n if (!isControlled) setInternalSlots(nextSlots);\n const joined = nextSlots.join('');\n onChange?.(joined);\n if (nextSlots.every((char) => char !== '')) {\n if (!completedRef.current) {\n completedRef.current = true;\n onComplete?.(joined);\n }\n } else {\n completedRef.current = false;\n }\n };\n\n const focusSlot = (index: number) => {\n const slot = slotRefs.current[index];\n slot?.focus();\n slot?.select();\n };\n\n React.useEffect(() => {\n if (autoFocus) focusSlot(0);\n // Only run on mount.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Distributes digits across slots starting at `index` — shared by a\n // multi-character paste and a multi-character autofill `onChange` (a\n // password manager or Chrome's OTP autofill can set a single slot's\n // `.value` to the entire code, bypassing `maxLength`).\n const distributeFrom = (index: number, digits: string) => {\n const nextSlots = [...slots];\n let cursor = index;\n for (const char of digits) {\n if (cursor >= length) break;\n nextSlots[cursor] = char;\n cursor += 1;\n }\n commitSlots(nextSlots);\n focusSlot(Math.min(cursor, length - 1));\n };\n\n const handleSlotChange = (index: number, raw: string) => {\n if (raw.length > 1) {\n const digits = raw.replace(/\\D/g, '');\n if (digits) distributeFrom(index, digits);\n return;\n }\n const char = raw;\n if (char !== '' && !DIGIT_PATTERN.test(char)) return;\n const nextSlots = [...slots];\n nextSlots[index] = char;\n commitSlots(nextSlots);\n if (char && index < length - 1) focusSlot(index + 1);\n };\n\n const handleKeyDown = (\n index: number,\n event: React.KeyboardEvent<HTMLInputElement>\n ) => {\n if (event.key === 'Backspace' && !slots[index] && index > 0) {\n event.preventDefault();\n const nextSlots = [...slots];\n nextSlots[index - 1] = '';\n commitSlots(nextSlots);\n focusSlot(index - 1);\n } else if (event.key === 'ArrowLeft' && index > 0) {\n event.preventDefault();\n focusSlot(index - 1);\n } else if (event.key === 'ArrowRight' && index < length - 1) {\n event.preventDefault();\n focusSlot(index + 1);\n }\n };\n\n const handlePaste = (\n index: number,\n event: React.ClipboardEvent<HTMLInputElement>\n ) => {\n const digits = event.clipboardData.getData('text').replace(/\\D/g, '');\n if (!digits) return;\n event.preventDefault();\n distributeFrom(index, digits);\n };\n\n return (\n <div\n ref={ref}\n role=\"group\"\n className={cn(\n 'inline-flex justify-[var(--ui-input-otp-container-justify-content)] gap-3',\n className\n )}\n {...props}\n >\n {Array.from({ length }, (_, index) => (\n <input\n key={index}\n ref={(el) => {\n slotRefs.current[index] = el;\n }}\n type=\"text\"\n inputMode=\"numeric\"\n pattern=\"[0-9]*\"\n autoComplete=\"one-time-code\"\n maxLength={1}\n value={slots[index]}\n placeholder={String(index + 1)}\n disabled={disabled}\n aria-label={slotAriaLabel(index + 1, length)}\n aria-invalid={error || undefined}\n onChange={(event) => handleSlotChange(index, event.target.value)}\n onKeyDown={(event) => handleKeyDown(index, event)}\n onPaste={(event) => handlePaste(index, event)}\n onFocus={(event) => event.target.select()}\n className={cn(\n 'ui-input-otp-value-text-style h-[var(--ui-input-otp-box-height)] w-[var(--ui-input-otp-box-width)] rounded-[var(--ui-input-otp-box-border-radius)] border-[length:var(--ui-input-otp-box-border-width)] bg-[var(--ui-input-otp-box-color)] px-[var(--ui-input-otp-box-padding-x)] py-[var(--ui-input-otp-box-padding-y)] text-center text-[var(--ui-input-otp-value-color)] caret-[var(--ui-input-otp-value-color)] placeholder:text-[var(--ui-input-otp-placeholder-color)] transition-colors focus-visible:outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',\n error\n ? 'border-[var(--ui-input-otp-box-border-color-error)] focus-visible:ring-[var(--ui-focus-error)]'\n : 'border-[var(--ui-input-otp-box-border-color-idle)] focus-visible:border-[var(--ui-input-otp-box-border-color-active)] focus-visible:ring-[var(--ui-focus-primary)]'\n )}\n />\n ))}\n </div>\n );\n }\n);\nInputOTP.displayName = 'InputOTP';\n\nexport { InputOTP };\n"],"names":["defaultSlotAriaLabel","index","length","DIGIT_PATTERN","toSlots","value","_","InputOTP","React","className","defaultValue","onChange","onComplete","disabled","error","autoFocus","slotAriaLabel","props","ref","internalSlots","setInternalSlots","isControlled","slots","slotRefs","completedRef","commitSlots","nextSlots","joined","char","focusSlot","slot","distributeFrom","digits","cursor","handleSlotChange","raw","handleKeyDown","event","handlePaste","jsx","cn","el"],"mappings":"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"}
@@ -0,0 +1,120 @@
1
+ import { jsxs as b, jsx as o } from "react/jsx-runtime";
2
+ import * as c from "react";
3
+ import { EyeIcon as P, EyeCrossedIcon as C } from "@acronis-platform/icons-react/stroke-mono";
4
+ import { cn as t } from "../../../lib/utils.js";
5
+ import { ButtonIconInput as E } from "../button-icon-input/button-icon-input.js";
6
+ function j({
7
+ htmlFor: a,
8
+ disabled: r,
9
+ required: s,
10
+ children: i
11
+ }) {
12
+ return /* @__PURE__ */ b(
13
+ "label",
14
+ {
15
+ htmlFor: a,
16
+ className: t(
17
+ "flex gap-[var(--ui-input-password-global-container-label-gap)] text-sm leading-4",
18
+ r ? "text-[var(--ui-input-password-global-label-color-disabled)]" : "text-[var(--ui-input-password-global-label-color-idle)]"
19
+ ),
20
+ children: [
21
+ i,
22
+ s && /* @__PURE__ */ o(
23
+ "span",
24
+ {
25
+ "aria-hidden": "true",
26
+ className: "text-[var(--ui-input-password-global-required-color)]",
27
+ children: "*"
28
+ }
29
+ )
30
+ ]
31
+ }
32
+ );
33
+ }
34
+ function k({
35
+ id: a,
36
+ hasError: r,
37
+ disabled: s,
38
+ children: i
39
+ }) {
40
+ return /* @__PURE__ */ o(
41
+ "p",
42
+ {
43
+ id: a,
44
+ className: t(
45
+ "text-xs leading-4",
46
+ r ? "text-[var(--ui-input-password-error-msg-error-color)]" : s ? "text-[var(--ui-input-password-normal-description-color-disabled)]" : "text-[var(--ui-input-password-normal-description-color-idle)]"
47
+ ),
48
+ children: i
49
+ }
50
+ );
51
+ }
52
+ function B(a, r) {
53
+ return t(
54
+ "flex h-[var(--ui-input-password-global-box-height)] items-center gap-[var(--ui-input-password-global-box-gap)] rounded-[var(--ui-input-password-global-box-border-radius)] border-[length:var(--ui-input-password-global-box-border-width)] px-[var(--ui-input-password-global-box-padding-x)] py-[var(--ui-input-password-global-box-padding-y)] transition-colors",
55
+ a ? "border-[var(--ui-input-password-error-msg-box-border-color-idle)] bg-[var(--ui-input-password-global-box-color-idle)] has-[:focus-visible]:border-[var(--ui-input-password-error-msg-box-border-color-hover)] has-[:focus-visible]:ring-[3px] has-[:focus-visible]:ring-[var(--ui-focus-error)]" : t(
56
+ "border-[var(--ui-input-password-normal-box-border-color-idle)] bg-[var(--ui-input-password-global-box-color-idle)]",
57
+ !r && "hover:border-[var(--ui-input-password-normal-box-border-color-hover)] hover:bg-[var(--ui-input-password-global-box-color-hover)]",
58
+ "has-[:focus-visible]:border-[var(--ui-input-password-normal-box-border-color-hover)] has-[:focus-visible]:ring-[3px] has-[:focus-visible]:ring-[var(--ui-focus-primary)]"
59
+ ),
60
+ r && "cursor-not-allowed border-[var(--ui-input-password-normal-box-border-color-disabled)] bg-[var(--ui-input-password-global-box-color-disabled)]"
61
+ );
62
+ }
63
+ const M = c.forwardRef(
64
+ ({
65
+ className: a,
66
+ id: r,
67
+ label: s,
68
+ required: i,
69
+ description: w,
70
+ error: d,
71
+ disabled: e,
72
+ showPasswordLabel: x = "Show password",
73
+ hidePasswordLabel: m = "Hide password",
74
+ ...h
75
+ }, f) => {
76
+ const I = c.useId(), p = r ?? I, v = `${p}-message`, [n, N] = c.useState(!1), l = d != null && d !== "", u = l ? d : w, g = u != null && u !== "";
77
+ return /* @__PURE__ */ b("div", { className: "flex w-full min-w-[var(--ui-input-password-global-container-width-min)] flex-col gap-[var(--ui-input-password-global-container-gap)]", children: [
78
+ s != null && s !== "" && /* @__PURE__ */ o(j, { htmlFor: p, disabled: e, required: i, children: s }),
79
+ /* @__PURE__ */ b("div", { className: B(l, e), children: [
80
+ /* @__PURE__ */ o(
81
+ "input",
82
+ {
83
+ ref: f,
84
+ id: p,
85
+ type: n ? "text" : "password",
86
+ disabled: e,
87
+ "aria-invalid": l || void 0,
88
+ "aria-required": i || void 0,
89
+ "aria-describedby": g ? v : void 0,
90
+ className: t(
91
+ "min-w-0 flex-1 bg-transparent text-sm leading-6 outline-none",
92
+ e ? "text-[var(--ui-input-password-global-value-color-disabled)] placeholder:text-[var(--ui-input-password-global-placeholder-color-disabled)]" : "text-[var(--ui-input-password-global-value-color-idle)] placeholder:text-[var(--ui-input-password-global-placeholder-color-idle)]",
93
+ a
94
+ ),
95
+ ...h
96
+ }
97
+ ),
98
+ /* @__PURE__ */ o(
99
+ E,
100
+ {
101
+ variant: l ? "error" : "normal",
102
+ onClick: () => N((y) => !y),
103
+ disabled: e,
104
+ "aria-label": n ? m : x,
105
+ "aria-pressed": n,
106
+ className: "shrink-0",
107
+ children: n ? /* @__PURE__ */ o(P, {}) : /* @__PURE__ */ o(C, {})
108
+ }
109
+ )
110
+ ] }),
111
+ g && /* @__PURE__ */ o(k, { id: v, hasError: l, disabled: e, children: u })
112
+ ] });
113
+ }
114
+ );
115
+ M.displayName = "InputPassword";
116
+ export {
117
+ M as InputPassword,
118
+ B as getInputPasswordBoxClassName
119
+ };
120
+ //# sourceMappingURL=input-password.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-password.js","sources":["../../../../src/components/ui/input-password/input-password.tsx"],"sourcesContent":["import * as React from 'react';\nimport {\n EyeIcon,\n EyeCrossedIcon,\n} from '@acronis-platform/icons-react/stroke-mono';\n\nimport { cn } from '@/lib/utils';\nimport { ButtonIconInput } from '../button-icon-input';\n\n// Mirrors the Figma \"InputPassword\" component: an `InputText`-shaped field\n// (label / required / description / error) with a built-in show/hide toggle.\n// Unlike `InputText`, the box itself isn't the bare `Input` primitive — the\n// design gives InputPassword its own token tier (`--ui-input-password-*`)\n// down to the box fill/border, so the value input renders borderless/\n// transparent inside a themed flex container alongside the toggle button\n// (the tier's own `global-box-gap` token implies this layout, not an\n// absolutely-positioned overlay). The toggle is the Figma \"ButtonIconInput\"\n// instance, rendered here via the `ButtonIconInput` component. Visibility is\n// fully internal state; nothing in the design calls for a consumer-controlled\n// mode.\nexport interface InputPasswordProps\n extends Omit<React.ComponentPropsWithoutRef<'input'>, 'children' | 'type'> {\n /** Field label, rendered above the input. */\n label?: React.ReactNode;\n /** Marks the field required — appends a `*` after the label. */\n required?: boolean;\n /** Helper text below the input. Hidden while `error` is set. */\n description?: React.ReactNode;\n /**\n * Error message below the input. Its presence switches the field to the error\n * treatment (red box border + red message).\n */\n error?: React.ReactNode;\n /** Accessible label for the toggle button while the password is hidden. */\n showPasswordLabel?: string;\n /** Accessible label for the toggle button while the password is shown. */\n hidePasswordLabel?: string;\n}\n\ninterface InputPasswordLabelProps {\n htmlFor: string;\n disabled?: boolean;\n required?: boolean;\n children: React.ReactNode;\n}\n\nfunction InputPasswordLabel({\n htmlFor,\n disabled,\n required,\n children,\n}: InputPasswordLabelProps) {\n return (\n <label\n htmlFor={htmlFor}\n className={cn(\n 'flex gap-[var(--ui-input-password-global-container-label-gap)] text-sm leading-4',\n disabled\n ? 'text-[var(--ui-input-password-global-label-color-disabled)]'\n : 'text-[var(--ui-input-password-global-label-color-idle)]'\n )}\n >\n {children}\n {required && (\n <span\n aria-hidden=\"true\"\n className=\"text-[var(--ui-input-password-global-required-color)]\"\n >\n *\n </span>\n )}\n </label>\n );\n}\n\ninterface InputPasswordMessageProps {\n id: string;\n hasError: boolean;\n disabled?: boolean;\n children: React.ReactNode;\n}\n\nfunction InputPasswordMessage({\n id,\n hasError,\n disabled,\n children,\n}: InputPasswordMessageProps) {\n return (\n <p\n id={id}\n className={cn(\n 'text-xs leading-4',\n hasError\n ? 'text-[var(--ui-input-password-error-msg-error-color)]'\n : disabled\n ? 'text-[var(--ui-input-password-normal-description-color-disabled)]'\n : 'text-[var(--ui-input-password-normal-description-color-idle)]'\n )}\n >\n {children}\n </p>\n );\n}\n\n// The box's border/background/focus-ring classes branch on `hasError` and\n// `disabled` independently, so inlining it is a 3-way nested ternary. Pulled\n// out as a pure function so the branches can be unit-tested directly.\nexport function getInputPasswordBoxClassName(\n hasError: boolean,\n disabled?: boolean\n) {\n return cn(\n 'flex h-[var(--ui-input-password-global-box-height)] items-center gap-[var(--ui-input-password-global-box-gap)] rounded-[var(--ui-input-password-global-box-border-radius)] border-[length:var(--ui-input-password-global-box-border-width)] px-[var(--ui-input-password-global-box-padding-x)] py-[var(--ui-input-password-global-box-padding-y)] transition-colors',\n hasError\n ? 'border-[var(--ui-input-password-error-msg-box-border-color-idle)] bg-[var(--ui-input-password-global-box-color-idle)] has-[:focus-visible]:border-[var(--ui-input-password-error-msg-box-border-color-hover)] has-[:focus-visible]:ring-[3px] has-[:focus-visible]:ring-[var(--ui-focus-error)]'\n : cn(\n 'border-[var(--ui-input-password-normal-box-border-color-idle)] bg-[var(--ui-input-password-global-box-color-idle)]',\n !disabled &&\n 'hover:border-[var(--ui-input-password-normal-box-border-color-hover)] hover:bg-[var(--ui-input-password-global-box-color-hover)]',\n 'has-[:focus-visible]:border-[var(--ui-input-password-normal-box-border-color-hover)] has-[:focus-visible]:ring-[3px] has-[:focus-visible]:ring-[var(--ui-focus-primary)]'\n ),\n disabled &&\n 'cursor-not-allowed border-[var(--ui-input-password-normal-box-border-color-disabled)] bg-[var(--ui-input-password-global-box-color-disabled)]'\n );\n}\n\nconst InputPassword = React.forwardRef<HTMLInputElement, InputPasswordProps>(\n (\n {\n className,\n id,\n label,\n required,\n description,\n error,\n disabled,\n showPasswordLabel = 'Show password',\n hidePasswordLabel = 'Hide password',\n ...props\n },\n ref\n ) => {\n const reactId = React.useId();\n const inputId = id ?? reactId;\n const messageId = `${inputId}-message`;\n const [visible, setVisible] = React.useState(false);\n\n const hasError = error != null && error !== '';\n const message = hasError ? error : description;\n const hasMessage = message != null && message !== '';\n\n return (\n <div className=\"flex w-full min-w-[var(--ui-input-password-global-container-width-min)] flex-col gap-[var(--ui-input-password-global-container-gap)]\">\n {label != null && label !== '' && (\n <InputPasswordLabel htmlFor={inputId} disabled={disabled} required={required}>\n {label}\n </InputPasswordLabel>\n )}\n\n <div className={getInputPasswordBoxClassName(hasError, disabled)}>\n <input\n ref={ref}\n id={inputId}\n type={visible ? 'text' : 'password'}\n disabled={disabled}\n aria-invalid={hasError || undefined}\n aria-required={required || undefined}\n aria-describedby={hasMessage ? messageId : undefined}\n className={cn(\n 'min-w-0 flex-1 bg-transparent text-sm leading-6 outline-none',\n disabled\n ? 'text-[var(--ui-input-password-global-value-color-disabled)] placeholder:text-[var(--ui-input-password-global-placeholder-color-disabled)]'\n : 'text-[var(--ui-input-password-global-value-color-idle)] placeholder:text-[var(--ui-input-password-global-placeholder-color-idle)]',\n className\n )}\n {...props}\n />\n <ButtonIconInput\n variant={hasError ? 'error' : 'normal'}\n onClick={() => setVisible((prev) => !prev)}\n disabled={disabled}\n aria-label={visible ? hidePasswordLabel : showPasswordLabel}\n aria-pressed={visible}\n className=\"shrink-0\"\n >\n {visible ? <EyeIcon /> : <EyeCrossedIcon />}\n </ButtonIconInput>\n </div>\n\n {hasMessage && (\n <InputPasswordMessage id={messageId} hasError={hasError} disabled={disabled}>\n {message}\n </InputPasswordMessage>\n )}\n </div>\n );\n }\n);\nInputPassword.displayName = 'InputPassword';\n\nexport { InputPassword };\n"],"names":["InputPasswordLabel","htmlFor","disabled","required","children","jsxs","cn","jsx","InputPasswordMessage","id","hasError","getInputPasswordBoxClassName","InputPassword","React","className","label","description","error","showPasswordLabel","hidePasswordLabel","props","ref","reactId","inputId","messageId","visible","setVisible","message","hasMessage","ButtonIconInput","prev","EyeIcon","EyeCrossedIcon"],"mappings":"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"}