tempest-react-sdk 0.24.0 → 0.26.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.
- package/README.md +25 -4
- package/bin/lib/alias/collect.mjs +94 -0
- package/bin/lib/alias/collect.test.mjs +95 -0
- package/bin/lib/alias/discover.mjs +77 -0
- package/bin/lib/alias/discover.test.mjs +189 -0
- package/bin/lib/alias/index.mjs +88 -0
- package/bin/lib/alias/index.test.mjs +138 -0
- package/bin/lib/alias/rewrite-css.mjs +76 -0
- package/bin/lib/alias/rewrite-css.test.mjs +85 -0
- package/bin/lib/alias/rewrite.mjs +141 -0
- package/bin/lib/alias/rewrite.test.mjs +148 -0
- package/bin/lib/alias/specifier.mjs +49 -0
- package/bin/lib/alias/specifier.test.mjs +85 -0
- package/bin/lib/alias/tsconfig.mjs +116 -0
- package/bin/lib/alias/typescript.mjs +23 -0
- package/bin/lib/icons/generate.mjs +156 -0
- package/bin/lib/icons/generate.test.mjs +125 -0
- package/bin/tempest.mjs +158 -13
- package/dist/audio/use-audio.cjs +1 -1
- package/dist/audio/use-audio.cjs.map +1 -1
- package/dist/audio/use-audio.js +12 -12
- package/dist/audio/use-audio.js.map +1 -1
- package/dist/charts/AreaChart.cjs +1 -1
- package/dist/charts/AreaChart.cjs.map +1 -1
- package/dist/charts/AreaChart.js +7 -7
- package/dist/charts/AreaChart.js.map +1 -1
- package/dist/charts/BarChart.cjs +1 -1
- package/dist/charts/BarChart.cjs.map +1 -1
- package/dist/charts/BarChart.js +7 -7
- package/dist/charts/BarChart.js.map +1 -1
- package/dist/charts/LineChart.cjs +1 -1
- package/dist/charts/LineChart.cjs.map +1 -1
- package/dist/charts/LineChart.js +6 -6
- package/dist/charts/LineChart.js.map +1 -1
- package/dist/charts/PieChart.cjs +1 -1
- package/dist/charts/PieChart.cjs.map +1 -1
- package/dist/charts/PieChart.js +6 -6
- package/dist/charts/PieChart.js.map +1 -1
- package/dist/charts/RadarChart.cjs +1 -1
- package/dist/charts/RadarChart.cjs.map +1 -1
- package/dist/charts/RadarChart.js +6 -6
- package/dist/charts/RadarChart.js.map +1 -1
- package/dist/charts/palette.cjs +1 -1
- package/dist/charts/palette.cjs.map +1 -1
- package/dist/charts/palette.js +19 -2
- package/dist/charts/palette.js.map +1 -1
- package/dist/charts/use-chart-colors.cjs +2 -0
- package/dist/charts/use-chart-colors.cjs.map +1 -0
- package/dist/charts/use-chart-colors.js +29 -0
- package/dist/charts/use-chart-colors.js.map +1 -0
- package/dist/charts.cjs +1 -1
- package/dist/charts.d.ts +61 -10
- package/dist/charts.js +8 -7
- package/dist/components/AvatarGroup/AvatarGroup.cjs +2 -0
- package/dist/components/AvatarGroup/AvatarGroup.cjs.map +1 -0
- package/dist/components/AvatarGroup/AvatarGroup.js +39 -0
- package/dist/components/AvatarGroup/AvatarGroup.js.map +1 -0
- package/dist/components/AvatarGroup/AvatarGroup.module.cjs +2 -0
- package/dist/components/AvatarGroup/AvatarGroup.module.cjs.map +1 -0
- package/dist/components/AvatarGroup/AvatarGroup.module.js +16 -0
- package/dist/components/AvatarGroup/AvatarGroup.module.js.map +1 -0
- package/dist/components/ClickOutside/ClickOutside.cjs +1 -1
- package/dist/components/ClickOutside/ClickOutside.cjs.map +1 -1
- package/dist/components/ClickOutside/ClickOutside.js +3 -1
- package/dist/components/ClickOutside/ClickOutside.js.map +1 -1
- package/dist/components/DataTable/DataTable.cjs +1 -1
- package/dist/components/DataTable/DataTable.cjs.map +1 -1
- package/dist/components/DataTable/DataTable.js +21 -23
- package/dist/components/DataTable/DataTable.js.map +1 -1
- package/dist/components/Kanban/Kanban.cjs +2 -0
- package/dist/components/Kanban/Kanban.cjs.map +1 -0
- package/dist/components/Kanban/Kanban.js +102 -0
- package/dist/components/Kanban/Kanban.js.map +1 -0
- package/dist/components/Kanban/Kanban.module.cjs +2 -0
- package/dist/components/Kanban/Kanban.module.cjs.map +1 -0
- package/dist/components/Kanban/Kanban.module.js +19 -0
- package/dist/components/Kanban/Kanban.module.js.map +1 -0
- package/dist/components/Lightbox/Lightbox.cjs +2 -0
- package/dist/components/Lightbox/Lightbox.cjs.map +1 -0
- package/dist/components/Lightbox/Lightbox.js +148 -0
- package/dist/components/Lightbox/Lightbox.js.map +1 -0
- package/dist/components/Lightbox/Lightbox.module.cjs +2 -0
- package/dist/components/Lightbox/Lightbox.module.cjs.map +1 -0
- package/dist/components/Lightbox/Lightbox.module.js +21 -0
- package/dist/components/Lightbox/Lightbox.module.js.map +1 -0
- package/dist/components/NotificationCenter/NotificationCenter.cjs +2 -0
- package/dist/components/NotificationCenter/NotificationCenter.cjs.map +1 -0
- package/dist/components/NotificationCenter/NotificationCenter.js +83 -0
- package/dist/components/NotificationCenter/NotificationCenter.js.map +1 -0
- package/dist/components/NotificationCenter/NotificationCenter.module.cjs +2 -0
- package/dist/components/NotificationCenter/NotificationCenter.module.cjs.map +1 -0
- package/dist/components/NotificationCenter/NotificationCenter.module.js +21 -0
- package/dist/components/NotificationCenter/NotificationCenter.module.js.map +1 -0
- package/dist/components/NotificationCenter/use-notification-inbox.cjs +2 -0
- package/dist/components/NotificationCenter/use-notification-inbox.cjs.map +1 -0
- package/dist/components/NotificationCenter/use-notification-inbox.js +63 -0
- package/dist/components/NotificationCenter/use-notification-inbox.js.map +1 -0
- package/dist/components/RefreshIndicator/RefreshIndicator.cjs +1 -1
- package/dist/components/RefreshIndicator/RefreshIndicator.cjs.map +1 -1
- package/dist/components/RefreshIndicator/RefreshIndicator.js +17 -17
- package/dist/components/RefreshIndicator/RefreshIndicator.js.map +1 -1
- package/dist/components/SignaturePad/SignaturePad.cjs +2 -0
- package/dist/components/SignaturePad/SignaturePad.cjs.map +1 -0
- package/dist/components/SignaturePad/SignaturePad.js +115 -0
- package/dist/components/SignaturePad/SignaturePad.js.map +1 -0
- package/dist/components/SignaturePad/SignaturePad.module.cjs +2 -0
- package/dist/components/SignaturePad/SignaturePad.module.cjs.map +1 -0
- package/dist/components/SignaturePad/SignaturePad.module.js +11 -0
- package/dist/components/SignaturePad/SignaturePad.module.js.map +1 -0
- package/dist/components/Stepper/Stepper.cjs +1 -1
- package/dist/components/Stepper/Stepper.cjs.map +1 -1
- package/dist/components/Stepper/Stepper.js +43 -19
- package/dist/components/Stepper/Stepper.js.map +1 -1
- package/dist/components/Stepper/Stepper.module.cjs +1 -1
- package/dist/components/Stepper/Stepper.module.cjs.map +1 -1
- package/dist/components/Stepper/Stepper.module.js +6 -3
- package/dist/components/Stepper/Stepper.module.js.map +1 -1
- package/dist/components/Tooltip/Tooltip.cjs +1 -1
- package/dist/components/Tooltip/Tooltip.cjs.map +1 -1
- package/dist/components/Tooltip/Tooltip.js +26 -20
- package/dist/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/components/TreeView/TreeView.cjs +2 -0
- package/dist/components/TreeView/TreeView.cjs.map +1 -0
- package/dist/components/TreeView/TreeView.js +124 -0
- package/dist/components/TreeView/TreeView.js.map +1 -0
- package/dist/components/TreeView/TreeView.module.cjs +2 -0
- package/dist/components/TreeView/TreeView.module.cjs.map +1 -0
- package/dist/components/TreeView/TreeView.module.js +17 -0
- package/dist/components/TreeView/TreeView.module.js.map +1 -0
- package/dist/components/VirtualTable/VirtualTable.cjs +2 -0
- package/dist/components/VirtualTable/VirtualTable.cjs.map +1 -0
- package/dist/components/VirtualTable/VirtualTable.js +116 -0
- package/dist/components/VirtualTable/VirtualTable.js.map +1 -0
- package/dist/components/VirtualTable/VirtualTable.module.cjs +2 -0
- package/dist/components/VirtualTable/VirtualTable.module.cjs.map +1 -0
- package/dist/components/VirtualTable/VirtualTable.module.js +20 -0
- package/dist/components/VirtualTable/VirtualTable.module.js.map +1 -0
- package/dist/components/Wizard/Wizard.cjs +2 -0
- package/dist/components/Wizard/Wizard.cjs.map +1 -0
- package/dist/components/Wizard/Wizard.js +111 -0
- package/dist/components/Wizard/Wizard.js.map +1 -0
- package/dist/components/Wizard/Wizard.module.cjs +2 -0
- package/dist/components/Wizard/Wizard.module.cjs.map +1 -0
- package/dist/components/Wizard/Wizard.module.js +10 -0
- package/dist/components/Wizard/Wizard.module.js.map +1 -0
- package/dist/geo/use-position-tracker.cjs +1 -1
- package/dist/geo/use-position-tracker.cjs.map +1 -1
- package/dist/geo/use-position-tracker.js +3 -1
- package/dist/geo/use-position-tracker.js.map +1 -1
- package/dist/hooks/use-async.cjs +1 -1
- package/dist/hooks/use-async.cjs.map +1 -1
- package/dist/hooks/use-async.js +3 -1
- package/dist/hooks/use-async.js.map +1 -1
- package/dist/hooks/use-click-outside.cjs +1 -1
- package/dist/hooks/use-click-outside.cjs.map +1 -1
- package/dist/hooks/use-click-outside.js +3 -1
- package/dist/hooks/use-click-outside.js.map +1 -1
- package/dist/hooks/use-deep-memo.cjs.map +1 -1
- package/dist/hooks/use-deep-memo.js.map +1 -1
- package/dist/hooks/use-event-listener.cjs +1 -1
- package/dist/hooks/use-event-listener.cjs.map +1 -1
- package/dist/hooks/use-event-listener.js +3 -1
- package/dist/hooks/use-event-listener.js.map +1 -1
- package/dist/hooks/use-interval.cjs +1 -1
- package/dist/hooks/use-interval.cjs.map +1 -1
- package/dist/hooks/use-interval.js +3 -1
- package/dist/hooks/use-interval.js.map +1 -1
- package/dist/hooks/use-is-first-render.cjs.map +1 -1
- package/dist/hooks/use-is-first-render.js.map +1 -1
- package/dist/hooks/use-long-press-handlers.cjs +1 -1
- package/dist/hooks/use-long-press-handlers.cjs.map +1 -1
- package/dist/hooks/use-long-press-handlers.js +25 -23
- package/dist/hooks/use-long-press-handlers.js.map +1 -1
- package/dist/hooks/use-long-press.cjs +1 -1
- package/dist/hooks/use-long-press.cjs.map +1 -1
- package/dist/hooks/use-long-press.js +3 -1
- package/dist/hooks/use-long-press.js.map +1 -1
- package/dist/hooks/use-online.cjs +1 -1
- package/dist/hooks/use-online.cjs.map +1 -1
- package/dist/hooks/use-online.js +6 -4
- package/dist/hooks/use-online.js.map +1 -1
- package/dist/hooks/use-previous.cjs.map +1 -1
- package/dist/hooks/use-previous.js.map +1 -1
- package/dist/hooks/use-service-worker-update.cjs +1 -1
- package/dist/hooks/use-service-worker-update.cjs.map +1 -1
- package/dist/hooks/use-service-worker-update.js +3 -1
- package/dist/hooks/use-service-worker-update.js.map +1 -1
- package/dist/hooks/use-sortable.cjs +2 -0
- package/dist/hooks/use-sortable.cjs.map +1 -0
- package/dist/hooks/use-sortable.js +169 -0
- package/dist/hooks/use-sortable.js.map +1 -0
- package/dist/hooks/use-stable-callback.cjs.map +1 -1
- package/dist/hooks/use-stable-callback.js.map +1 -1
- package/dist/hooks/use-timeout.cjs +1 -1
- package/dist/hooks/use-timeout.cjs.map +1 -1
- package/dist/hooks/use-timeout.js +3 -1
- package/dist/hooks/use-timeout.js.map +1 -1
- package/dist/icons/Icon.cjs +2 -0
- package/dist/icons/Icon.cjs.map +1 -0
- package/dist/icons/Icon.js +24 -0
- package/dist/icons/Icon.js.map +1 -0
- package/dist/icons/IconProvider.cjs +2 -0
- package/dist/icons/IconProvider.cjs.map +1 -0
- package/dist/icons/IconProvider.js +24 -0
- package/dist/icons/IconProvider.js.map +1 -0
- package/dist/icons/generated/aliases.cjs +2 -0
- package/dist/icons/generated/aliases.cjs.map +1 -0
- package/dist/icons/generated/aliases.js +255 -0
- package/dist/icons/generated/aliases.js.map +1 -0
- package/dist/icons/generated/icon-names.cjs +2 -0
- package/dist/icons/generated/icon-names.cjs.map +1 -0
- package/dist/icons/generated/icon-names.js +6 -0
- package/dist/icons/generated/icon-names.js.map +1 -0
- package/dist/icons/generated/loaders.cjs +2 -0
- package/dist/icons/generated/loaders.cjs.map +1 -0
- package/dist/icons/generated/loaders.js +32 -0
- package/dist/icons/generated/loaders.js.map +1 -0
- package/dist/icons/generated/shard-a.cjs +2 -0
- package/dist/icons/generated/shard-a.cjs.map +1 -0
- package/dist/icons/generated/shard-a.js +114 -0
- package/dist/icons/generated/shard-a.js.map +1 -0
- package/dist/icons/generated/shard-b.cjs +2 -0
- package/dist/icons/generated/shard-b.cjs.map +1 -0
- package/dist/icons/generated/shard-b.js +156 -0
- package/dist/icons/generated/shard-b.js.map +1 -0
- package/dist/icons/generated/shard-c.cjs +2 -0
- package/dist/icons/generated/shard-c.cjs.map +1 -0
- package/dist/icons/generated/shard-c.js +269 -0
- package/dist/icons/generated/shard-c.js.map +1 -0
- package/dist/icons/generated/shard-d.cjs +2 -0
- package/dist/icons/generated/shard-d.cjs.map +1 -0
- package/dist/icons/generated/shard-d.js +62 -0
- package/dist/icons/generated/shard-d.js.map +1 -0
- package/dist/icons/generated/shard-e.cjs +2 -0
- package/dist/icons/generated/shard-e.cjs.map +1 -0
- package/dist/icons/generated/shard-e.js +32 -0
- package/dist/icons/generated/shard-e.js.map +1 -0
- package/dist/icons/generated/shard-f.cjs +2 -0
- package/dist/icons/generated/shard-f.cjs.map +1 -0
- package/dist/icons/generated/shard-f.js +139 -0
- package/dist/icons/generated/shard-f.js.map +1 -0
- package/dist/icons/generated/shard-g.cjs +2 -0
- package/dist/icons/generated/shard-g.cjs.map +1 -0
- package/dist/icons/generated/shard-g.js +61 -0
- package/dist/icons/generated/shard-g.js.map +1 -0
- package/dist/icons/generated/shard-h.cjs +2 -0
- package/dist/icons/generated/shard-h.cjs.map +1 -0
- package/dist/icons/generated/shard-h.js +63 -0
- package/dist/icons/generated/shard-h.js.map +1 -0
- package/dist/icons/generated/shard-i.cjs +2 -0
- package/dist/icons/generated/shard-i.cjs.map +1 -0
- package/dist/icons/generated/shard-i.js +30 -0
- package/dist/icons/generated/shard-i.js.map +1 -0
- package/dist/icons/generated/shard-j.cjs +2 -0
- package/dist/icons/generated/shard-j.cjs.map +1 -0
- package/dist/icons/generated/shard-j.js +10 -0
- package/dist/icons/generated/shard-j.js.map +1 -0
- package/dist/icons/generated/shard-k.cjs +2 -0
- package/dist/icons/generated/shard-k.cjs.map +1 -0
- package/dist/icons/generated/shard-k.js +16 -0
- package/dist/icons/generated/shard-k.js.map +1 -0
- package/dist/icons/generated/shard-l.cjs +2 -0
- package/dist/icons/generated/shard-l.cjs.map +1 -0
- package/dist/icons/generated/shard-l.js +88 -0
- package/dist/icons/generated/shard-l.js.map +1 -0
- package/dist/icons/generated/shard-m.cjs +2 -0
- package/dist/icons/generated/shard-m.cjs.map +1 -0
- package/dist/icons/generated/shard-m.js +140 -0
- package/dist/icons/generated/shard-m.js.map +1 -0
- package/dist/icons/generated/shard-n.cjs +2 -0
- package/dist/icons/generated/shard-n.cjs.map +1 -0
- package/dist/icons/generated/shard-n.js +24 -0
- package/dist/icons/generated/shard-n.js.map +1 -0
- package/dist/icons/generated/shard-o.cjs +2 -0
- package/dist/icons/generated/shard-o.cjs.map +1 -0
- package/dist/icons/generated/shard-o.js +17 -0
- package/dist/icons/generated/shard-o.js.map +1 -0
- package/dist/icons/generated/shard-p.cjs +2 -0
- package/dist/icons/generated/shard-p.cjs.map +1 -0
- package/dist/icons/generated/shard-p.js +115 -0
- package/dist/icons/generated/shard-p.js.map +1 -0
- package/dist/icons/generated/shard-q.cjs +2 -0
- package/dist/icons/generated/shard-q.cjs.map +1 -0
- package/dist/icons/generated/shard-q.js +10 -0
- package/dist/icons/generated/shard-q.js.map +1 -0
- package/dist/icons/generated/shard-r.cjs +2 -0
- package/dist/icons/generated/shard-r.cjs.map +1 -0
- package/dist/icons/generated/shard-r.js +77 -0
- package/dist/icons/generated/shard-r.js.map +1 -0
- package/dist/icons/generated/shard-s.cjs +2 -0
- package/dist/icons/generated/shard-s.cjs.map +1 -0
- package/dist/icons/generated/shard-s.js +255 -0
- package/dist/icons/generated/shard-s.js.map +1 -0
- package/dist/icons/generated/shard-t.cjs +2 -0
- package/dist/icons/generated/shard-t.cjs.map +1 -0
- package/dist/icons/generated/shard-t.js +109 -0
- package/dist/icons/generated/shard-t.js.map +1 -0
- package/dist/icons/generated/shard-u.cjs +2 -0
- package/dist/icons/generated/shard-u.cjs.map +1 -0
- package/dist/icons/generated/shard-u.js +49 -0
- package/dist/icons/generated/shard-u.js.map +1 -0
- package/dist/icons/generated/shard-v.cjs +2 -0
- package/dist/icons/generated/shard-v.cjs.map +1 -0
- package/dist/icons/generated/shard-v.js +30 -0
- package/dist/icons/generated/shard-v.js.map +1 -0
- package/dist/icons/generated/shard-w.cjs +2 -0
- package/dist/icons/generated/shard-w.cjs.map +1 -0
- package/dist/icons/generated/shard-w.js +48 -0
- package/dist/icons/generated/shard-w.js.map +1 -0
- package/dist/icons/generated/shard-x.cjs +2 -0
- package/dist/icons/generated/shard-x.cjs.map +1 -0
- package/dist/icons/generated/shard-x.js +10 -0
- package/dist/icons/generated/shard-x.js.map +1 -0
- package/dist/icons/generated/shard-z.cjs +2 -0
- package/dist/icons/generated/shard-z.cjs.map +1 -0
- package/dist/icons/generated/shard-z.js +25 -0
- package/dist/icons/generated/shard-z.js.map +1 -0
- package/dist/icons/icon-context.cjs +2 -0
- package/dist/icons/icon-context.cjs.map +1 -0
- package/dist/icons/icon-context.js +13 -0
- package/dist/icons/icon-context.js.map +1 -0
- package/dist/icons/is-icon-name.cjs +2 -0
- package/dist/icons/is-icon-name.cjs.map +1 -0
- package/dist/icons/is-icon-name.js +10 -0
- package/dist/icons/is-icon-name.js.map +1 -0
- package/dist/icons/shard-cache.cjs +2 -0
- package/dist/icons/shard-cache.cjs.map +1 -0
- package/dist/icons/shard-cache.js +41 -0
- package/dist/icons/shard-cache.js.map +1 -0
- package/dist/icons/use-icon.cjs +2 -0
- package/dist/icons/use-icon.cjs.map +1 -0
- package/dist/icons/use-icon.js +20 -0
- package/dist/icons/use-icon.js.map +1 -0
- package/dist/icons-virtual.d.ts +22 -0
- package/dist/icons.cjs +1 -0
- package/dist/icons.d.ts +220 -0
- package/dist/icons.js +9 -0
- package/dist/offline/create-offline-sync.cjs +1 -1
- package/dist/offline/create-offline-sync.cjs.map +1 -1
- package/dist/offline/create-offline-sync.js +8 -2
- package/dist/offline/create-offline-sync.js.map +1 -1
- package/dist/offline/use-offline-sync.cjs +1 -1
- package/dist/offline/use-offline-sync.cjs.map +1 -1
- package/dist/offline/use-offline-sync.js +3 -1
- package/dist/offline/use-offline-sync.js.map +1 -1
- package/dist/push/use-push-subscription.cjs +1 -1
- package/dist/push/use-push-subscription.cjs.map +1 -1
- package/dist/push/use-push-subscription.js +3 -1
- package/dist/push/use-push-subscription.js.map +1 -1
- package/dist/sse/use-event-stream.cjs +1 -1
- package/dist/sse/use-event-stream.cjs.map +1 -1
- package/dist/sse/use-event-stream.js +3 -1
- package/dist/sse/use-event-stream.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +1187 -2
- package/dist/tempest-react-sdk.js +261 -246
- package/dist/theme/ThemeProvider.cjs +1 -1
- package/dist/theme/ThemeProvider.cjs.map +1 -1
- package/dist/theme/ThemeProvider.js +6 -2
- package/dist/theme/ThemeProvider.js.map +1 -1
- package/dist/theme/apply-theme.cjs +2 -0
- package/dist/theme/apply-theme.cjs.map +1 -0
- package/dist/theme/apply-theme.js +24 -0
- package/dist/theme/apply-theme.js.map +1 -0
- package/dist/theme/color.cjs +2 -0
- package/dist/theme/color.cjs.map +1 -0
- package/dist/theme/color.js +161 -0
- package/dist/theme/color.js.map +1 -0
- package/dist/theme/create-theme.cjs +5 -0
- package/dist/theme/create-theme.cjs.map +1 -0
- package/dist/theme/create-theme.js +141 -0
- package/dist/theme/create-theme.js.map +1 -0
- package/dist/theme/theme-presets.cjs +2 -0
- package/dist/theme/theme-presets.cjs.map +1 -0
- package/dist/theme/theme-presets.js +82 -0
- package/dist/theme/theme-presets.js.map +1 -0
- package/dist/utilities.css +432 -0
- package/dist/utils/compare-values.cjs +2 -0
- package/dist/utils/compare-values.cjs.map +1 -0
- package/dist/utils/compare-values.js +8 -0
- package/dist/utils/compare-values.js.map +1 -0
- package/dist/vision/use-camera-stream.cjs +1 -1
- package/dist/vision/use-camera-stream.cjs.map +1 -1
- package/dist/vision/use-camera-stream.js +3 -1
- package/dist/vision/use-camera-stream.js.map +1 -1
- package/dist/vite/create-vite-config.cjs +1 -1
- package/dist/vite/create-vite-config.cjs.map +1 -1
- package/dist/vite/create-vite-config.js +30 -22
- package/dist/vite/create-vite-config.js.map +1 -1
- package/dist/vite/tempest-icons.cjs +9 -0
- package/dist/vite/tempest-icons.cjs.map +1 -0
- package/dist/vite/tempest-icons.js +99 -0
- package/dist/vite/tempest-icons.js.map +1 -0
- package/dist/vite.cjs +1 -1
- package/dist/vite.d.ts +79 -0
- package/dist/vite.js +6 -5
- package/dist/ws/use-web-socket.cjs +1 -1
- package/dist/ws/use-web-socket.cjs.map +1 -1
- package/dist/ws/use-web-socket.js +3 -1
- package/dist/ws/use-web-socket.js.map +1 -1
- package/package.json +14 -4
- package/template/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.cjs","names":[],"sources":["../../../src/components/DataTable/DataTable.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { usePagination } from \"@/hooks\";\nimport { Table, type TableAlign, type TableColumn, type TablePriority } from \"../Table\";\nimport { Pagination } from \"../Pagination\";\nimport { SearchBar } from \"../SearchBar\";\nimport styles from \"./DataTable.module.css\";\n\nexport type SortDirection = \"asc\" | \"desc\";\n\nexport interface DataTableSort<T> {\n key: keyof T;\n direction: SortDirection;\n}\n\n/**\n * Column definition for {@link DataTable}. Extends the headless {@link Table}\n * column shape with a typed `key`, opt-in sorting, and the visual options that\n * are forwarded to the underlying Table cell.\n */\nexport interface DataTableColumn<T> {\n /** Property of the row this column reads from. Doubles as the cell key. */\n key: keyof T;\n /** Column heading. */\n header: ReactNode;\n /** Custom cell renderer. Defaults to `String(row[key])`. */\n render?: (row: T) => ReactNode;\n /** Enable click-to-sort on this column's header. */\n sortable?: boolean;\n /** Text alignment forwarded to the Table cell. */\n align?: TableAlign;\n /** Responsive visibility priority forwarded to the Table cell. */\n priority?: TablePriority;\n /** Fixed column width forwarded to the Table cell. */\n width?: string | number;\n}\n\nexport interface DataTableProps<T> extends HTMLAttributes<HTMLDivElement> {\n /** Full, unfiltered dataset. Sorting/filtering/pagination happen client-side. */\n data: T[];\n /** Column definitions. */\n columns: DataTableColumn<T>[];\n /** Rows per page. Default 10. */\n pageSize?: number;\n /** Render a search input above the table. Default false. */\n searchable?: boolean;\n /**\n * Keys to match the search term against. When omitted, every column whose\n * value is a string or number is searched.\n */\n searchKeys?: (keyof T)[];\n /** Initial sort applied before any header interaction. */\n initialSort?: DataTableSort<T>;\n /** Stable key extractor for rows. Defaults to the row index. */\n rowKey?: (row: T, index: number) => string | number;\n /** Content shown when no rows match. */\n emptyMessage?: ReactNode;\n}\n\n/**\n * Compare two arbitrary cell values with stable, type-aware ordering:\n * numbers numerically, dates by timestamp, everything else by `localeCompare`.\n *\n * @param a - First value.\n * @param b - Second value.\n * @returns Negative when `a < b`, positive when `a > b`, zero when equal.\n */\nfunction compareValues(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();\n if (typeof a === \"boolean\" && typeof b === \"boolean\") {\n return Number(a) - Number(b);\n }\n return String(a).localeCompare(String(b), undefined, { numeric: true });\n}\n\n/**\n * Stateful, headless data table built on top of {@link Table}. Adds\n * client-side searching, click-to-sort columns, and pagination while\n * delegating all table markup to the underlying Table component.\n *\n * - Clicking a sortable header cycles asc → desc → unsorted.\n * - Search matches a case-insensitive substring across `searchKeys`\n * (or every string/number column when not provided).\n * - Pagination is hidden when the result fits on a single page.\n *\n * The header memo depends on `columns` and `sort` only: `toggleSort` is\n * recreated each render but always closes over the same setter, so including it\n * would rebuild every header on every render without changing behaviour. That\n * is why `exhaustive-deps` is silenced on that dependency array.\n */\nexport function DataTable<T>({\n data,\n columns,\n pageSize = 10,\n searchable = false,\n searchKeys,\n initialSort,\n rowKey = (_row, index) => index,\n emptyMessage,\n className,\n ...rest\n}: DataTableProps<T>) {\n const [search, setSearch] = useState<string>(\"\");\n const [sort, setSort] = useState<DataTableSort<T> | null>(initialSort ?? null);\n const { page, setPage } = usePagination(1, pageSize);\n\n const effectiveSearchKeys = useMemo<(keyof T)[]>(() => {\n if (searchKeys && searchKeys.length > 0) return searchKeys;\n return columns\n .filter((column) => {\n const sample = data.find((row) => row[column.key] != null);\n const value = sample ? sample[column.key] : undefined;\n return typeof value === \"string\" || typeof value === \"number\";\n })\n .map((column) => column.key);\n }, [searchKeys, columns, data]);\n\n const filtered = useMemo<T[]>(() => {\n const term = search.trim().toLowerCase();\n if (!term || !searchable) return data;\n return data.filter((row) =>\n effectiveSearchKeys.some((key) => {\n const value = row[key];\n return value != null && String(value).toLowerCase().includes(term);\n }),\n );\n }, [data, search, searchable, effectiveSearchKeys]);\n\n const sorted = useMemo<T[]>(() => {\n if (!sort) return filtered;\n const factor = sort.direction === \"asc\" ? 1 : -1;\n return [...filtered].sort((a, b) => compareValues(a[sort.key], b[sort.key]) * factor);\n }, [filtered, sort]);\n\n const totalPages = Math.max(1, Math.ceil(sorted.length / pageSize));\n\n // Clamp the current page when the dataset shrinks (e.g. after filtering).\n useEffect(() => {\n if (page > totalPages) setPage(totalPages);\n }, [page, totalPages, setPage]);\n\n const safePage = Math.min(page, totalPages);\n const pageRows = useMemo<T[]>(() => {\n const start = (safePage - 1) * pageSize;\n return sorted.slice(start, start + pageSize);\n }, [sorted, safePage, pageSize]);\n\n function toggleSort(key: keyof T): void {\n setSort((current) => {\n if (!current || current.key !== key) return { key, direction: \"asc\" };\n if (current.direction === \"asc\") return { key, direction: \"desc\" };\n return null;\n });\n }\n\n const tableColumns = useMemo<TableColumn<T>[]>(\n () =>\n columns.map((column) => {\n const isSorted = sort?.key === column.key;\n const indicator = isSorted ? (sort?.direction === \"asc\" ? \" ▲\" : \" ▼\") : \"\";\n const header = column.sortable ? (\n <button\n type=\"button\"\n className={styles.sortButton}\n onClick={() => toggleSort(column.key)}\n aria-label={`Ordenar por ${\n typeof column.header === \"string\" ? column.header : String(column.key)\n }`}\n >\n {column.header}\n <span className={styles.sortIndicator} aria-hidden>\n {indicator}\n </span>\n </button>\n ) : (\n column.header\n );\n\n return {\n key: String(column.key),\n header,\n align: column.align,\n priority: column.priority,\n width: column.width,\n render: column.render\n ? (row: T) => column.render!(row)\n : (row: T) => (row[column.key] as ReactNode) ?? null,\n };\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [columns, sort],\n );\n\n return (\n <div className={cn(styles.wrapper, className)} {...rest}>\n {searchable && (\n <SearchBar\n value={search}\n onChange={(value) => {\n setSearch(value);\n setPage(1);\n }}\n wrapperClassName={styles.search}\n />\n )}\n <Table\n columns={tableColumns}\n data={pageRows}\n rowKey={(row, index) => rowKey(row, (safePage - 1) * pageSize + index)}\n emptyMessage={emptyMessage}\n />\n {totalPages > 1 && (\n <Pagination\n page={safePage}\n totalPages={totalPages}\n onPageChange={setPage}\n totalItems={sorted.length}\n />\n )}\n </div>\n );\n}\n"],"mappings":"8RAoEA,SAAS,EAAc,EAAY,EAAoB,CASnD,OARI,GAAK,MAAQ,GAAK,KAAa,EAC/B,GAAK,KAAa,GAClB,GAAK,KAAa,EAClB,OAAO,GAAM,UAAY,OAAO,GAAM,SAAiB,EAAI,EAC3D,aAAa,MAAQ,aAAa,KAAa,EAAE,QAAQ,EAAI,EAAE,QAAQ,EACvE,OAAO,GAAM,WAAa,OAAO,GAAM,UAChC,OAAO,CAAC,EAAI,OAAO,CAAC,EAExB,OAAO,CAAC,CAAC,CAAC,cAAc,OAAO,CAAC,EAAG,IAAA,GAAW,CAAE,QAAS,EAAK,CAAC,CAC1E,CAiBA,SAAgB,EAAa,CACzB,OACA,UACA,WAAW,GACX,aAAa,GACb,aACA,cACA,UAAU,EAAM,IAAU,EAC1B,eACA,YACA,GAAG,GACe,CAClB,GAAM,CAAC,EAAQ,IAAA,EAAA,EAAA,SAAA,CAA8B,EAAE,EACzC,CAAC,EAAM,IAAA,EAAA,EAAA,SAAA,CAA6C,GAAe,IAAI,EACvE,CAAE,OAAM,WAAY,EAAA,cAAc,EAAG,CAAQ,EAE7C,GAAA,EAAA,EAAA,QAAA,KACE,GAAc,EAAW,OAAS,EAAU,EACzC,EACF,OAAQ,GAAW,CAChB,IAAM,EAAS,EAAK,KAAM,GAAQ,EAAI,EAAO,MAAQ,IAAI,EACnD,EAAQ,EAAS,EAAO,EAAO,KAAO,IAAA,GAC5C,OAAO,OAAO,GAAU,UAAY,OAAO,GAAU,QACzD,CAAC,CAAC,CACD,IAAK,GAAW,EAAO,GAAG,EAChC,CAAC,EAAY,EAAS,CAAI,CAAC,EAExB,GAAA,EAAA,EAAA,QAAA,KAA8B,CAChC,IAAM,EAAO,EAAO,KAAK,CAAC,CAAC,YAAY,EAEvC,MADI,CAAC,GAAQ,CAAC,EAAmB,EAC1B,EAAK,OAAQ,GAChB,EAAoB,KAAM,GAAQ,CAC9B,IAAM,EAAQ,EAAI,GAClB,OAAO,GAAS,MAAQ,OAAO,CAAK,CAAC,CAAC,YAAY,CAAC,CAAC,SAAS,CAAI,CACrE,CAAC,CACL,CACJ,EAAG,CAAC,EAAM,EAAQ,EAAY,CAAmB,CAAC,EAE5C,GAAA,EAAA,EAAA,QAAA,KAA4B,CAC9B,GAAI,CAAC,EAAM,OAAO,EAClB,IAAM,EAAS,EAAK,YAAc,MAAQ,EAAI,GAC9C,MAAO,CAAC,GAAG,CAAQ,CAAC,CAAC,MAAM,EAAG,IAAM,EAAc,EAAE,EAAK,KAAM,EAAE,EAAK,IAAI,EAAI,CAAM,CACxF,EAAG,CAAC,EAAU,CAAI,CAAC,EAEb,EAAa,KAAK,IAAI,EAAG,KAAK,KAAK,EAAO,OAAS,CAAQ,CAAC,GAGlE,EAAA,EAAA,UAAA,KAAgB,CACR,EAAO,GAAY,EAAQ,CAAU,CAC7C,EAAG,CAAC,EAAM,EAAY,CAAO,CAAC,EAE9B,IAAM,EAAW,KAAK,IAAI,EAAM,CAAU,EACpC,GAAA,EAAA,EAAA,QAAA,KAA8B,CAChC,IAAM,GAAS,EAAW,GAAK,EAC/B,OAAO,EAAO,MAAM,EAAO,EAAQ,CAAQ,CAC/C,EAAG,CAAC,EAAQ,EAAU,CAAQ,CAAC,EAE/B,SAAS,EAAW,EAAoB,CACpC,EAAS,GACD,CAAC,GAAW,EAAQ,MAAQ,EAAY,CAAE,MAAK,UAAW,KAAM,EAChE,EAAQ,YAAc,MAAc,CAAE,MAAK,UAAW,MAAO,EAC1D,IACV,CACL,CAEA,IAAM,GAAA,EAAA,EAAA,QAAA,KAEE,EAAQ,IAAK,GAAW,CAEpB,IAAM,EADW,GAAM,MAAQ,EAAO,IACR,GAAM,YAAc,MAAQ,KAAO,KAAQ,GACnE,EAAS,EAAO,UAClB,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,WAClB,YAAe,EAAW,EAAO,GAAG,EACpC,aAAY,eACR,OAAO,EAAO,QAAW,SAAW,EAAO,OAAS,OAAO,EAAO,GAAG,aAL7E,CAQK,EAAO,QACR,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,cAAe,cAAA,YAClC,CACC,CAAA,CACF,IAER,EAAO,OAGX,MAAO,CACH,IAAK,OAAO,EAAO,GAAG,EACtB,SACA,MAAO,EAAO,MACd,SAAU,EAAO,SACjB,MAAO,EAAO,MACd,OAAQ,EAAO,OACR,GAAW,EAAO,OAAQ,CAAG,EAC7B,GAAY,EAAI,EAAO,MAAsB,IACxD,CACJ,CAAC,EAEL,CAAC,EAAS,CAAI,CAClB,EAEA,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,CAAS,EAAG,GAAI,WAAnD,CACK,IACG,EAAA,EAAA,IAAA,CAAC,EAAA,UAAD,CACI,MAAO,EACP,SAAW,GAAU,CACjB,EAAU,CAAK,EACf,EAAQ,CAAC,CACb,EACA,iBAAkB,EAAA,QAAO,MAC5B,CAAA,GAEL,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CACI,QAAS,EACT,KAAM,EACN,QAAS,EAAK,IAAU,EAAO,GAAM,EAAW,GAAK,EAAW,CAAK,EACvD,cACjB,CAAA,EACA,EAAa,IACV,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CACI,KAAM,EACM,aACZ,aAAc,EACd,WAAY,EAAO,MACtB,CAAA,CAEJ,GAEb"}
|
|
1
|
+
{"version":3,"file":"DataTable.cjs","names":[],"sources":["../../../src/components/DataTable/DataTable.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { compareValues } from \"@/utils/compare-values\";\nimport { usePagination } from \"@/hooks\";\nimport { Table, type TableAlign, type TableColumn, type TablePriority } from \"../Table\";\nimport { Pagination } from \"../Pagination\";\nimport { SearchBar } from \"../SearchBar\";\nimport styles from \"./DataTable.module.css\";\n\nexport type SortDirection = \"asc\" | \"desc\";\n\nexport interface DataTableSort<T> {\n key: keyof T;\n direction: SortDirection;\n}\n\n/**\n * Column definition for {@link DataTable}. Extends the headless {@link Table}\n * column shape with a typed `key`, opt-in sorting, and the visual options that\n * are forwarded to the underlying Table cell.\n */\nexport interface DataTableColumn<T> {\n /** Property of the row this column reads from. Doubles as the cell key. */\n key: keyof T;\n /** Column heading. */\n header: ReactNode;\n /** Custom cell renderer. Defaults to `String(row[key])`. */\n render?: (row: T) => ReactNode;\n /** Enable click-to-sort on this column's header. */\n sortable?: boolean;\n /** Text alignment forwarded to the Table cell. */\n align?: TableAlign;\n /** Responsive visibility priority forwarded to the Table cell. */\n priority?: TablePriority;\n /** Fixed column width forwarded to the Table cell. */\n width?: string | number;\n}\n\nexport interface DataTableProps<T> extends HTMLAttributes<HTMLDivElement> {\n /** Full, unfiltered dataset. Sorting/filtering/pagination happen client-side. */\n data: T[];\n /** Column definitions. */\n columns: DataTableColumn<T>[];\n /** Rows per page. Default 10. */\n pageSize?: number;\n /** Render a search input above the table. Default false. */\n searchable?: boolean;\n /**\n * Keys to match the search term against. When omitted, every column whose\n * value is a string or number is searched.\n */\n searchKeys?: (keyof T)[];\n /** Initial sort applied before any header interaction. */\n initialSort?: DataTableSort<T>;\n /** Stable key extractor for rows. Defaults to the row index. */\n rowKey?: (row: T, index: number) => string | number;\n /** Content shown when no rows match. */\n emptyMessage?: ReactNode;\n}\n\n/**\n * Stateful, headless data table built on top of {@link Table}. Adds\n * client-side searching, click-to-sort columns, and pagination while\n * delegating all table markup to the underlying Table component.\n *\n * - Clicking a sortable header cycles asc → desc → unsorted.\n * - Search matches a case-insensitive substring across `searchKeys`\n * (or every string/number column when not provided).\n * - Pagination is hidden when the result fits on a single page.\n *\n * The header memo depends on `columns` and `sort` only: `toggleSort` is\n * recreated each render but always closes over the same setter, so including it\n * would rebuild every header on every render without changing behaviour. That\n * is why `exhaustive-deps` is silenced on that dependency array.\n */\nexport function DataTable<T>({\n data,\n columns,\n pageSize = 10,\n searchable = false,\n searchKeys,\n initialSort,\n rowKey = (_row, index) => index,\n emptyMessage,\n className,\n ...rest\n}: DataTableProps<T>) {\n const [search, setSearch] = useState<string>(\"\");\n const [sort, setSort] = useState<DataTableSort<T> | null>(initialSort ?? null);\n const { page, setPage } = usePagination(1, pageSize);\n\n const effectiveSearchKeys = useMemo<(keyof T)[]>(() => {\n if (searchKeys && searchKeys.length > 0) return searchKeys;\n return columns\n .filter((column) => {\n const sample = data.find((row) => row[column.key] != null);\n const value = sample ? sample[column.key] : undefined;\n return typeof value === \"string\" || typeof value === \"number\";\n })\n .map((column) => column.key);\n }, [searchKeys, columns, data]);\n\n const filtered = useMemo<T[]>(() => {\n const term = search.trim().toLowerCase();\n if (!term || !searchable) return data;\n return data.filter((row) =>\n effectiveSearchKeys.some((key) => {\n const value = row[key];\n return value != null && String(value).toLowerCase().includes(term);\n }),\n );\n }, [data, search, searchable, effectiveSearchKeys]);\n\n const sorted = useMemo<T[]>(() => {\n if (!sort) return filtered;\n const factor = sort.direction === \"asc\" ? 1 : -1;\n return [...filtered].sort((a, b) => compareValues(a[sort.key], b[sort.key]) * factor);\n }, [filtered, sort]);\n\n const totalPages = Math.max(1, Math.ceil(sorted.length / pageSize));\n\n // Clamp the current page when the dataset shrinks (e.g. after filtering).\n useEffect(() => {\n if (page > totalPages) setPage(totalPages);\n }, [page, totalPages, setPage]);\n\n const safePage = Math.min(page, totalPages);\n const pageRows = useMemo<T[]>(() => {\n const start = (safePage - 1) * pageSize;\n return sorted.slice(start, start + pageSize);\n }, [sorted, safePage, pageSize]);\n\n function toggleSort(key: keyof T): void {\n setSort((current) => {\n if (!current || current.key !== key) return { key, direction: \"asc\" };\n if (current.direction === \"asc\") return { key, direction: \"desc\" };\n return null;\n });\n }\n\n const tableColumns = useMemo<TableColumn<T>[]>(\n () =>\n columns.map((column) => {\n const isSorted = sort?.key === column.key;\n const indicator = isSorted ? (sort?.direction === \"asc\" ? \" ▲\" : \" ▼\") : \"\";\n const header = column.sortable ? (\n <button\n type=\"button\"\n className={styles.sortButton}\n onClick={() => toggleSort(column.key)}\n aria-label={`Ordenar por ${\n typeof column.header === \"string\" ? column.header : String(column.key)\n }`}\n >\n {column.header}\n <span className={styles.sortIndicator} aria-hidden>\n {indicator}\n </span>\n </button>\n ) : (\n column.header\n );\n\n return {\n key: String(column.key),\n header,\n align: column.align,\n priority: column.priority,\n width: column.width,\n render: column.render\n ? (row: T) => column.render!(row)\n : (row: T) => (row[column.key] as ReactNode) ?? null,\n };\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [columns, sort],\n );\n\n return (\n <div className={cn(styles.wrapper, className)} {...rest}>\n {searchable && (\n <SearchBar\n value={search}\n onChange={(value) => {\n setSearch(value);\n setPage(1);\n }}\n wrapperClassName={styles.search}\n />\n )}\n <Table\n columns={tableColumns}\n data={pageRows}\n rowKey={(row, index) => rowKey(row, (safePage - 1) * pageSize + index)}\n emptyMessage={emptyMessage}\n />\n {totalPages > 1 && (\n <Pagination\n page={safePage}\n totalPages={totalPages}\n onPageChange={setPage}\n totalItems={sorted.length}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -3,22 +3,20 @@ import { usePagination as t } from "../../hooks/use-pagination.js";
|
|
|
3
3
|
import { Pagination as n } from "../Pagination/Pagination.js";
|
|
4
4
|
import { SearchBar as r } from "../SearchBar/SearchBar.js";
|
|
5
5
|
import { Table as i } from "../Table/Table.js";
|
|
6
|
-
import a from "
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
6
|
+
import { compareValues as a } from "../../utils/compare-values.js";
|
|
7
|
+
import o from "./DataTable.module.js";
|
|
8
|
+
import { useEffect as s, useMemo as c, useState as l } from "react";
|
|
9
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
9
10
|
//#region src/components/DataTable/DataTable.tsx
|
|
10
|
-
function d(e, t) {
|
|
11
|
-
return e == null && t == null ? 0 : e == null ? -1 : t == null ? 1 : typeof e == "number" && typeof t == "number" ? e - t : e instanceof Date && t instanceof Date ? e.getTime() - t.getTime() : typeof e == "boolean" && typeof t == "boolean" ? Number(e) - Number(t) : String(e).localeCompare(String(t), void 0, { numeric: !0 });
|
|
12
|
-
}
|
|
13
11
|
function f({ data: f, columns: p, pageSize: m = 10, searchable: h = !1, searchKeys: g, initialSort: _, rowKey: v = (e, t) => t, emptyMessage: y, className: b, ...x }) {
|
|
14
|
-
let [S, C] =
|
|
12
|
+
let [S, C] = l(""), [w, T] = l(_ ?? null), { page: E, setPage: D } = t(1, m), O = c(() => g && g.length > 0 ? g : p.filter((e) => {
|
|
15
13
|
let t = f.find((t) => t[e.key] != null), n = t ? t[e.key] : void 0;
|
|
16
14
|
return typeof n == "string" || typeof n == "number";
|
|
17
15
|
}).map((e) => e.key), [
|
|
18
16
|
g,
|
|
19
17
|
p,
|
|
20
18
|
f
|
|
21
|
-
]), k =
|
|
19
|
+
]), k = c(() => {
|
|
22
20
|
let e = S.trim().toLowerCase();
|
|
23
21
|
return !e || !h ? f : f.filter((t) => O.some((n) => {
|
|
24
22
|
let r = t[n];
|
|
@@ -29,19 +27,19 @@ function f({ data: f, columns: p, pageSize: m = 10, searchable: h = !1, searchKe
|
|
|
29
27
|
S,
|
|
30
28
|
h,
|
|
31
29
|
O
|
|
32
|
-
]), A =
|
|
30
|
+
]), A = c(() => {
|
|
33
31
|
if (!w) return k;
|
|
34
32
|
let e = w.direction === "asc" ? 1 : -1;
|
|
35
|
-
return [...k].sort((t, n) =>
|
|
33
|
+
return [...k].sort((t, n) => a(t[w.key], n[w.key]) * e);
|
|
36
34
|
}, [k, w]), j = Math.max(1, Math.ceil(A.length / m));
|
|
37
|
-
|
|
35
|
+
s(() => {
|
|
38
36
|
E > j && D(j);
|
|
39
37
|
}, [
|
|
40
38
|
E,
|
|
41
39
|
j,
|
|
42
40
|
D
|
|
43
41
|
]);
|
|
44
|
-
let M = Math.min(E, j), N =
|
|
42
|
+
let M = Math.min(E, j), N = c(() => {
|
|
45
43
|
let e = (M - 1) * m;
|
|
46
44
|
return A.slice(e, e + m);
|
|
47
45
|
}, [
|
|
@@ -58,14 +56,14 @@ function f({ data: f, columns: p, pageSize: m = 10, searchable: h = !1, searchKe
|
|
|
58
56
|
direction: "desc"
|
|
59
57
|
} : null);
|
|
60
58
|
}
|
|
61
|
-
let F =
|
|
62
|
-
let t = w?.key === e.key ? w?.direction === "asc" ? " ▲" : " ▼" : "", n = e.sortable ? /* @__PURE__ */
|
|
59
|
+
let F = c(() => p.map((e) => {
|
|
60
|
+
let t = w?.key === e.key ? w?.direction === "asc" ? " ▲" : " ▼" : "", n = e.sortable ? /* @__PURE__ */ d("button", {
|
|
63
61
|
type: "button",
|
|
64
|
-
className:
|
|
62
|
+
className: o.sortButton,
|
|
65
63
|
onClick: () => P(e.key),
|
|
66
64
|
"aria-label": `Ordenar por ${typeof e.header == "string" ? e.header : String(e.key)}`,
|
|
67
|
-
children: [e.header, /* @__PURE__ */
|
|
68
|
-
className:
|
|
65
|
+
children: [e.header, /* @__PURE__ */ u("span", {
|
|
66
|
+
className: o.sortIndicator,
|
|
69
67
|
"aria-hidden": !0,
|
|
70
68
|
children: t
|
|
71
69
|
})]
|
|
@@ -79,24 +77,24 @@ function f({ data: f, columns: p, pageSize: m = 10, searchable: h = !1, searchKe
|
|
|
79
77
|
render: e.render ? (t) => e.render(t) : (t) => t[e.key] ?? null
|
|
80
78
|
};
|
|
81
79
|
}), [p, w]);
|
|
82
|
-
return /* @__PURE__ */
|
|
83
|
-
className: e(
|
|
80
|
+
return /* @__PURE__ */ d("div", {
|
|
81
|
+
className: e(o.wrapper, b),
|
|
84
82
|
...x,
|
|
85
83
|
children: [
|
|
86
|
-
h && /* @__PURE__ */
|
|
84
|
+
h && /* @__PURE__ */ u(r, {
|
|
87
85
|
value: S,
|
|
88
86
|
onChange: (e) => {
|
|
89
87
|
C(e), D(1);
|
|
90
88
|
},
|
|
91
|
-
wrapperClassName:
|
|
89
|
+
wrapperClassName: o.search
|
|
92
90
|
}),
|
|
93
|
-
/* @__PURE__ */
|
|
91
|
+
/* @__PURE__ */ u(i, {
|
|
94
92
|
columns: F,
|
|
95
93
|
data: N,
|
|
96
94
|
rowKey: (e, t) => v(e, (M - 1) * m + t),
|
|
97
95
|
emptyMessage: y
|
|
98
96
|
}),
|
|
99
|
-
j > 1 && /* @__PURE__ */
|
|
97
|
+
j > 1 && /* @__PURE__ */ u(n, {
|
|
100
98
|
page: M,
|
|
101
99
|
totalPages: j,
|
|
102
100
|
onPageChange: D,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.js","names":[],"sources":["../../../src/components/DataTable/DataTable.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { usePagination } from \"@/hooks\";\nimport { Table, type TableAlign, type TableColumn, type TablePriority } from \"../Table\";\nimport { Pagination } from \"../Pagination\";\nimport { SearchBar } from \"../SearchBar\";\nimport styles from \"./DataTable.module.css\";\n\nexport type SortDirection = \"asc\" | \"desc\";\n\nexport interface DataTableSort<T> {\n key: keyof T;\n direction: SortDirection;\n}\n\n/**\n * Column definition for {@link DataTable}. Extends the headless {@link Table}\n * column shape with a typed `key`, opt-in sorting, and the visual options that\n * are forwarded to the underlying Table cell.\n */\nexport interface DataTableColumn<T> {\n /** Property of the row this column reads from. Doubles as the cell key. */\n key: keyof T;\n /** Column heading. */\n header: ReactNode;\n /** Custom cell renderer. Defaults to `String(row[key])`. */\n render?: (row: T) => ReactNode;\n /** Enable click-to-sort on this column's header. */\n sortable?: boolean;\n /** Text alignment forwarded to the Table cell. */\n align?: TableAlign;\n /** Responsive visibility priority forwarded to the Table cell. */\n priority?: TablePriority;\n /** Fixed column width forwarded to the Table cell. */\n width?: string | number;\n}\n\nexport interface DataTableProps<T> extends HTMLAttributes<HTMLDivElement> {\n /** Full, unfiltered dataset. Sorting/filtering/pagination happen client-side. */\n data: T[];\n /** Column definitions. */\n columns: DataTableColumn<T>[];\n /** Rows per page. Default 10. */\n pageSize?: number;\n /** Render a search input above the table. Default false. */\n searchable?: boolean;\n /**\n * Keys to match the search term against. When omitted, every column whose\n * value is a string or number is searched.\n */\n searchKeys?: (keyof T)[];\n /** Initial sort applied before any header interaction. */\n initialSort?: DataTableSort<T>;\n /** Stable key extractor for rows. Defaults to the row index. */\n rowKey?: (row: T, index: number) => string | number;\n /** Content shown when no rows match. */\n emptyMessage?: ReactNode;\n}\n\n/**\n * Compare two arbitrary cell values with stable, type-aware ordering:\n * numbers numerically, dates by timestamp, everything else by `localeCompare`.\n *\n * @param a - First value.\n * @param b - Second value.\n * @returns Negative when `a < b`, positive when `a > b`, zero when equal.\n */\nfunction compareValues(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();\n if (typeof a === \"boolean\" && typeof b === \"boolean\") {\n return Number(a) - Number(b);\n }\n return String(a).localeCompare(String(b), undefined, { numeric: true });\n}\n\n/**\n * Stateful, headless data table built on top of {@link Table}. Adds\n * client-side searching, click-to-sort columns, and pagination while\n * delegating all table markup to the underlying Table component.\n *\n * - Clicking a sortable header cycles asc → desc → unsorted.\n * - Search matches a case-insensitive substring across `searchKeys`\n * (or every string/number column when not provided).\n * - Pagination is hidden when the result fits on a single page.\n *\n * The header memo depends on `columns` and `sort` only: `toggleSort` is\n * recreated each render but always closes over the same setter, so including it\n * would rebuild every header on every render without changing behaviour. That\n * is why `exhaustive-deps` is silenced on that dependency array.\n */\nexport function DataTable<T>({\n data,\n columns,\n pageSize = 10,\n searchable = false,\n searchKeys,\n initialSort,\n rowKey = (_row, index) => index,\n emptyMessage,\n className,\n ...rest\n}: DataTableProps<T>) {\n const [search, setSearch] = useState<string>(\"\");\n const [sort, setSort] = useState<DataTableSort<T> | null>(initialSort ?? null);\n const { page, setPage } = usePagination(1, pageSize);\n\n const effectiveSearchKeys = useMemo<(keyof T)[]>(() => {\n if (searchKeys && searchKeys.length > 0) return searchKeys;\n return columns\n .filter((column) => {\n const sample = data.find((row) => row[column.key] != null);\n const value = sample ? sample[column.key] : undefined;\n return typeof value === \"string\" || typeof value === \"number\";\n })\n .map((column) => column.key);\n }, [searchKeys, columns, data]);\n\n const filtered = useMemo<T[]>(() => {\n const term = search.trim().toLowerCase();\n if (!term || !searchable) return data;\n return data.filter((row) =>\n effectiveSearchKeys.some((key) => {\n const value = row[key];\n return value != null && String(value).toLowerCase().includes(term);\n }),\n );\n }, [data, search, searchable, effectiveSearchKeys]);\n\n const sorted = useMemo<T[]>(() => {\n if (!sort) return filtered;\n const factor = sort.direction === \"asc\" ? 1 : -1;\n return [...filtered].sort((a, b) => compareValues(a[sort.key], b[sort.key]) * factor);\n }, [filtered, sort]);\n\n const totalPages = Math.max(1, Math.ceil(sorted.length / pageSize));\n\n // Clamp the current page when the dataset shrinks (e.g. after filtering).\n useEffect(() => {\n if (page > totalPages) setPage(totalPages);\n }, [page, totalPages, setPage]);\n\n const safePage = Math.min(page, totalPages);\n const pageRows = useMemo<T[]>(() => {\n const start = (safePage - 1) * pageSize;\n return sorted.slice(start, start + pageSize);\n }, [sorted, safePage, pageSize]);\n\n function toggleSort(key: keyof T): void {\n setSort((current) => {\n if (!current || current.key !== key) return { key, direction: \"asc\" };\n if (current.direction === \"asc\") return { key, direction: \"desc\" };\n return null;\n });\n }\n\n const tableColumns = useMemo<TableColumn<T>[]>(\n () =>\n columns.map((column) => {\n const isSorted = sort?.key === column.key;\n const indicator = isSorted ? (sort?.direction === \"asc\" ? \" ▲\" : \" ▼\") : \"\";\n const header = column.sortable ? (\n <button\n type=\"button\"\n className={styles.sortButton}\n onClick={() => toggleSort(column.key)}\n aria-label={`Ordenar por ${\n typeof column.header === \"string\" ? column.header : String(column.key)\n }`}\n >\n {column.header}\n <span className={styles.sortIndicator} aria-hidden>\n {indicator}\n </span>\n </button>\n ) : (\n column.header\n );\n\n return {\n key: String(column.key),\n header,\n align: column.align,\n priority: column.priority,\n width: column.width,\n render: column.render\n ? (row: T) => column.render!(row)\n : (row: T) => (row[column.key] as ReactNode) ?? null,\n };\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [columns, sort],\n );\n\n return (\n <div className={cn(styles.wrapper, className)} {...rest}>\n {searchable && (\n <SearchBar\n value={search}\n onChange={(value) => {\n setSearch(value);\n setPage(1);\n }}\n wrapperClassName={styles.search}\n />\n )}\n <Table\n columns={tableColumns}\n data={pageRows}\n rowKey={(row, index) => rowKey(row, (safePage - 1) * pageSize + index)}\n emptyMessage={emptyMessage}\n />\n {totalPages > 1 && (\n <Pagination\n page={safePage}\n totalPages={totalPages}\n onPageChange={setPage}\n totalItems={sorted.length}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DataTable.js","names":[],"sources":["../../../src/components/DataTable/DataTable.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { compareValues } from \"@/utils/compare-values\";\nimport { usePagination } from \"@/hooks\";\nimport { Table, type TableAlign, type TableColumn, type TablePriority } from \"../Table\";\nimport { Pagination } from \"../Pagination\";\nimport { SearchBar } from \"../SearchBar\";\nimport styles from \"./DataTable.module.css\";\n\nexport type SortDirection = \"asc\" | \"desc\";\n\nexport interface DataTableSort<T> {\n key: keyof T;\n direction: SortDirection;\n}\n\n/**\n * Column definition for {@link DataTable}. Extends the headless {@link Table}\n * column shape with a typed `key`, opt-in sorting, and the visual options that\n * are forwarded to the underlying Table cell.\n */\nexport interface DataTableColumn<T> {\n /** Property of the row this column reads from. Doubles as the cell key. */\n key: keyof T;\n /** Column heading. */\n header: ReactNode;\n /** Custom cell renderer. Defaults to `String(row[key])`. */\n render?: (row: T) => ReactNode;\n /** Enable click-to-sort on this column's header. */\n sortable?: boolean;\n /** Text alignment forwarded to the Table cell. */\n align?: TableAlign;\n /** Responsive visibility priority forwarded to the Table cell. */\n priority?: TablePriority;\n /** Fixed column width forwarded to the Table cell. */\n width?: string | number;\n}\n\nexport interface DataTableProps<T> extends HTMLAttributes<HTMLDivElement> {\n /** Full, unfiltered dataset. Sorting/filtering/pagination happen client-side. */\n data: T[];\n /** Column definitions. */\n columns: DataTableColumn<T>[];\n /** Rows per page. Default 10. */\n pageSize?: number;\n /** Render a search input above the table. Default false. */\n searchable?: boolean;\n /**\n * Keys to match the search term against. When omitted, every column whose\n * value is a string or number is searched.\n */\n searchKeys?: (keyof T)[];\n /** Initial sort applied before any header interaction. */\n initialSort?: DataTableSort<T>;\n /** Stable key extractor for rows. Defaults to the row index. */\n rowKey?: (row: T, index: number) => string | number;\n /** Content shown when no rows match. */\n emptyMessage?: ReactNode;\n}\n\n/**\n * Stateful, headless data table built on top of {@link Table}. Adds\n * client-side searching, click-to-sort columns, and pagination while\n * delegating all table markup to the underlying Table component.\n *\n * - Clicking a sortable header cycles asc → desc → unsorted.\n * - Search matches a case-insensitive substring across `searchKeys`\n * (or every string/number column when not provided).\n * - Pagination is hidden when the result fits on a single page.\n *\n * The header memo depends on `columns` and `sort` only: `toggleSort` is\n * recreated each render but always closes over the same setter, so including it\n * would rebuild every header on every render without changing behaviour. That\n * is why `exhaustive-deps` is silenced on that dependency array.\n */\nexport function DataTable<T>({\n data,\n columns,\n pageSize = 10,\n searchable = false,\n searchKeys,\n initialSort,\n rowKey = (_row, index) => index,\n emptyMessage,\n className,\n ...rest\n}: DataTableProps<T>) {\n const [search, setSearch] = useState<string>(\"\");\n const [sort, setSort] = useState<DataTableSort<T> | null>(initialSort ?? null);\n const { page, setPage } = usePagination(1, pageSize);\n\n const effectiveSearchKeys = useMemo<(keyof T)[]>(() => {\n if (searchKeys && searchKeys.length > 0) return searchKeys;\n return columns\n .filter((column) => {\n const sample = data.find((row) => row[column.key] != null);\n const value = sample ? sample[column.key] : undefined;\n return typeof value === \"string\" || typeof value === \"number\";\n })\n .map((column) => column.key);\n }, [searchKeys, columns, data]);\n\n const filtered = useMemo<T[]>(() => {\n const term = search.trim().toLowerCase();\n if (!term || !searchable) return data;\n return data.filter((row) =>\n effectiveSearchKeys.some((key) => {\n const value = row[key];\n return value != null && String(value).toLowerCase().includes(term);\n }),\n );\n }, [data, search, searchable, effectiveSearchKeys]);\n\n const sorted = useMemo<T[]>(() => {\n if (!sort) return filtered;\n const factor = sort.direction === \"asc\" ? 1 : -1;\n return [...filtered].sort((a, b) => compareValues(a[sort.key], b[sort.key]) * factor);\n }, [filtered, sort]);\n\n const totalPages = Math.max(1, Math.ceil(sorted.length / pageSize));\n\n // Clamp the current page when the dataset shrinks (e.g. after filtering).\n useEffect(() => {\n if (page > totalPages) setPage(totalPages);\n }, [page, totalPages, setPage]);\n\n const safePage = Math.min(page, totalPages);\n const pageRows = useMemo<T[]>(() => {\n const start = (safePage - 1) * pageSize;\n return sorted.slice(start, start + pageSize);\n }, [sorted, safePage, pageSize]);\n\n function toggleSort(key: keyof T): void {\n setSort((current) => {\n if (!current || current.key !== key) return { key, direction: \"asc\" };\n if (current.direction === \"asc\") return { key, direction: \"desc\" };\n return null;\n });\n }\n\n const tableColumns = useMemo<TableColumn<T>[]>(\n () =>\n columns.map((column) => {\n const isSorted = sort?.key === column.key;\n const indicator = isSorted ? (sort?.direction === \"asc\" ? \" ▲\" : \" ▼\") : \"\";\n const header = column.sortable ? (\n <button\n type=\"button\"\n className={styles.sortButton}\n onClick={() => toggleSort(column.key)}\n aria-label={`Ordenar por ${\n typeof column.header === \"string\" ? column.header : String(column.key)\n }`}\n >\n {column.header}\n <span className={styles.sortIndicator} aria-hidden>\n {indicator}\n </span>\n </button>\n ) : (\n column.header\n );\n\n return {\n key: String(column.key),\n header,\n align: column.align,\n priority: column.priority,\n width: column.width,\n render: column.render\n ? (row: T) => column.render!(row)\n : (row: T) => (row[column.key] as ReactNode) ?? null,\n };\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [columns, sort],\n );\n\n return (\n <div className={cn(styles.wrapper, className)} {...rest}>\n {searchable && (\n <SearchBar\n value={search}\n onChange={(value) => {\n setSearch(value);\n setPage(1);\n }}\n wrapperClassName={styles.search}\n />\n )}\n <Table\n columns={tableColumns}\n data={pageRows}\n rowKey={(row, index) => rowKey(row, (safePage - 1) * pageSize + index)}\n emptyMessage={emptyMessage}\n />\n {totalPages > 1 && (\n <Pagination\n page={safePage}\n totalPages={totalPages}\n onPageChange={setPage}\n totalItems={sorted.length}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("../../hooks/use-sortable.cjs"),n=require("./Kanban.module.cjs");let r=require("react"),i=require("react/jsx-runtime");function a({columns:a,onMove:o,renderCard:s,emptyLabel:c=`Nenhum card`,cardRoleDescription:l=`Card do quadro — Espaço pega, setas movem, Espaço solta`,label:u=`Quadro`,disabled:d=!1,className:f}){let p=(0,r.useMemo)(()=>a.flatMap((e,t)=>e.cards.map((n,r)=>({card:n,column:e,columnIndex:t,cardIndex:r}))),[a]),m=t.useSortable({itemCount:p.length,disabled:d,roleDescription:l,onReorder:({from:e,to:t,fromGroup:n,toGroup:r})=>{let i=p[e];if(!i)return;let s=r??n??i.column.id,c=a.find(e=>e.id===s);if(!c||c.locked)return;let l=p[t],u=l&&l.column.id===s?l.cardIndex:c.cards.length;o({cardId:i.card.id,fromColumn:i.column.id,toColumn:s,toIndex:u})}}),h=(0,r.useMemo)(()=>{let e=[],t=0;for(let n of a)e.push(t),t+=n.cards.length;return e},[a]);return(0,i.jsx)(`div`,{className:e.cn(n.default.board,f),"aria-label":u,ref:m.setContainer,children:a.map((t,r)=>{let a=m.overGroup===t.id&&!t.locked;return(0,i.jsxs)(`div`,{className:e.cn(n.default.column,a&&n.default.columnActive,t.locked&&n.default.columnLocked),children:[(0,i.jsxs)(`div`,{className:n.default.columnHeader,children:[(0,i.jsx)(`span`,{className:n.default.columnTitle,children:t.title}),(0,i.jsx)(`span`,{className:n.default.count,children:t.cards.length})]}),(0,i.jsxs)(`div`,{className:n.default.cards,...t.cards.length>0?m.getListProps():{},"aria-label":typeof t.title==`string`?t.title:t.id,children:[t.cards.map((a,o)=>{let c=h[r]+o,l=m.activeIndex===c,u=m.overIndex===c&&!l;return(0,i.jsx)(`div`,{...m.getItemProps(c,t.id),className:e.cn(n.default.card,l&&n.default.cardActive,u&&n.default.cardOver),children:s?s(a,t):a.content},a.id)}),t.cards.length===0?(0,i.jsx)(`div`,{...m.getEmptyGroupProps(t.id),className:n.default.empty,children:c}):null]})]},t.id)})})}function o(e,t){let n=e.find(e=>e.id===t.fromColumn),r=n?.cards.find(e=>e.id===t.cardId);if(!n||!r)return e;let i=t.toIndex;return e.map(e=>{if(e.id===t.fromColumn&&e.id===t.toColumn){let n=e.cards.filter(e=>e.id!==t.cardId);return n.splice(i,0,r),{...e,cards:n}}if(e.id===t.fromColumn)return{...e,cards:e.cards.filter(e=>e.id!==t.cardId)};if(e.id===t.toColumn){let t=e.cards.slice();return t.splice(i,0,r),{...e,cards:t}}return e})}exports.Kanban=a,exports.applyKanbanMove=o;
|
|
2
|
+
//# sourceMappingURL=Kanban.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Kanban.cjs","names":[],"sources":["../../../src/components/Kanban/Kanban.tsx"],"sourcesContent":["/* eslint-disable react-hooks/refs -- `getItemProps` is called during render and its\n handlers close over `useSortable`'s mirror refs, so the rule reports the call site\n here. The refs are only written from event handlers and read from a window\n listener; the two reads that were genuinely render-time (the drag group in the\n rendered class/aria, and the group lookup inside `commit`) were fixed instead of\n silenced — see the hook's docstring. */\nimport { useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { useSortable } from \"@/hooks/use-sortable\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Kanban.module.css\";\n\n/** One card on the board. */\nexport interface KanbanCard {\n /** Stable identifier, unique across the whole board. */\n id: string;\n /** Card body. A string renders as the card's title. */\n content: ReactNode;\n}\n\n/** One column of the board. */\nexport interface KanbanColumn {\n /** Stable identifier, used as the drop group. */\n id: string;\n /** Column heading. */\n title: ReactNode;\n /** Cards in display order. */\n cards: KanbanCard[];\n /**\n * Refuses drops into this column. The cards inside can still be picked up and\n * moved elsewhere — a \"done\" column that no longer accepts work, for instance.\n */\n locked?: boolean;\n}\n\n/** Where a card ended up. */\nexport interface KanbanMove {\n cardId: string;\n fromColumn: string;\n toColumn: string;\n /** Index within the destination column. */\n toIndex: number;\n}\n\nexport interface KanbanProps {\n columns: KanbanColumn[];\n /**\n * Called once per committed move — on pointer release or on a keyboard drop.\n * You own the data: apply the move and pass the new `columns` back.\n */\n onMove: (move: KanbanMove) => void;\n /** Renders a card. Defaults to its `content`. */\n renderCard?: (card: KanbanCard, column: KanbanColumn) => ReactNode;\n /** Text shown in a column with no cards. */\n emptyLabel?: ReactNode;\n /** Announced per card, so a screen reader user learns the keyboard contract. */\n cardRoleDescription?: string;\n /**\n * Accessible name of the board. A `listbox` without a name is unusable with a\n * screen reader, so this has a default rather than being optional in practice.\n */\n label?: string;\n /** Blocks all dragging. */\n disabled?: boolean;\n className?: string;\n}\n\n/**\n * Kanban board: columns of cards, reorderable within a column and movable across\n * columns, by pointer **or** keyboard.\n *\n * The drag machine is `useSortable` — the board does not reimplement it. Cards are\n * indexed globally across columns so a single sortable can span the whole board;\n * the component translates that flat index back into `{ column, index }` for\n * `onMove`, which is the shape an app actually needs.\n *\n * Nothing is mutated here: `onMove` reports the intent once and the parent applies\n * it. That keeps the board a controlled value and avoids a re-render per pointer\n * frame.\n *\n * ARIA shape: **one `listbox` per column**, named by the column title, containing\n * only `option` cards. A single board-wide listbox does not survive the markup a\n * board needs — `listbox` requires `option`/`group` children, and the column header\n * and card wrapper in between break that ownership. Per-column listboxes also read\n * better: a screen reader announces which column it is in and how many cards it\n * holds. The column header is a plain `div`, not a `<header>`, because outside a\n * sectioning element every `<header>` becomes a page-level banner landmark — with\n * three columns that is three duplicate banners. An **empty** column is not marked\n * as a listbox at all: a listbox with zero options fails `aria-required-children`,\n * and \"no cards\" is exactly zero options.\n *\n * Known limitation: a **keyboard** move can only target positions that hold a card,\n * because the move walks the flat index space of existing cards. Dropping into an\n * empty column works by pointer; by keyboard it does not. Moving a card to a column\n * that already has one card, then reordering, is the workaround until column-switch\n * keys land.\n *\n * @example\n * ```tsx\n * const [columns, setColumns] = useState<KanbanColumn[]>([\n * { id: \"todo\", title: \"A fazer\", cards: [{ id: \"1\", content: \"Corrigir login\" }] },\n * { id: \"doing\", title: \"Fazendo\", cards: [] },\n * { id: \"done\", title: \"Feito\", cards: [], locked: true },\n * ]);\n *\n * <Kanban\n * columns={columns}\n * onMove={({ cardId, fromColumn, toColumn, toIndex }) =>\n * setColumns((current) => applyKanbanMove(current, { cardId, fromColumn, toColumn, toIndex }))\n * }\n * />\n * ```\n */\nexport function Kanban({\n columns,\n onMove,\n renderCard,\n emptyLabel = \"Nenhum card\",\n cardRoleDescription = \"Card do quadro — Espaço pega, setas movem, Espaço solta\",\n label = \"Quadro\",\n disabled = false,\n className,\n}: KanbanProps) {\n /**\n * Flat view of every card, in column order.\n *\n * `useSortable` works on a single index space, so the board flattens itself and\n * keeps the mapping back to `{ column, index }` here — the only place that has\n * to know both shapes.\n */\n const flat = useMemo(\n () =>\n columns.flatMap((column, columnIndex) =>\n column.cards.map((card, cardIndex) => ({ card, column, columnIndex, cardIndex })),\n ),\n [columns],\n );\n\n const sortable = useSortable({\n itemCount: flat.length,\n disabled,\n roleDescription: cardRoleDescription,\n onReorder: ({ from, to, fromGroup, toGroup }) => {\n const source = flat[from];\n if (!source) return;\n\n const destinationId = toGroup ?? fromGroup ?? source.column.id;\n const destination = columns.find((column) => column.id === destinationId);\n if (!destination || destination.locked) return;\n\n const target = flat[to];\n const toIndex =\n target && target.column.id === destinationId\n ? target.cardIndex\n : destination.cards.length;\n\n onMove({\n cardId: source.card.id,\n fromColumn: source.column.id,\n toColumn: destinationId,\n toIndex,\n });\n },\n });\n\n /**\n * First flat index of each column.\n *\n * Derived instead of counted with a mutable `flatIndex++` during render: a\n * render-scoped counter mutated inside the map is exactly the pattern the React\n * Compiler rules reject, and for good reason — a partial or replayed render\n * would resume from a half-advanced counter.\n */\n const columnOffsets = useMemo(() => {\n const offsets: number[] = [];\n let start = 0;\n for (const column of columns) {\n offsets.push(start);\n start += column.cards.length;\n }\n return offsets;\n }, [columns]);\n\n return (\n <div className={cn(styles.board, className)} aria-label={label} ref={sortable.setContainer}>\n {columns.map((column, columnIndex) => {\n const isDropTarget = sortable.overGroup === column.id && !column.locked;\n return (\n <div\n key={column.id}\n className={cn(\n styles.column,\n isDropTarget && styles.columnActive,\n column.locked && styles.columnLocked,\n )}\n >\n <div className={styles.columnHeader}>\n <span className={styles.columnTitle}>{column.title}</span>\n <span className={styles.count}>{column.cards.length}</span>\n </div>\n\n <div\n className={styles.cards}\n {...(column.cards.length > 0 ? sortable.getListProps() : {})}\n aria-label={typeof column.title === \"string\" ? column.title : column.id}\n >\n {column.cards.map((card, cardIndex) => {\n const index = columnOffsets[columnIndex] + cardIndex;\n const isActive = sortable.activeIndex === index;\n const isOver = sortable.overIndex === index && !isActive;\n return (\n <div\n key={card.id}\n {...sortable.getItemProps(index, column.id)}\n className={cn(\n styles.card,\n isActive && styles.cardActive,\n isOver && styles.cardOver,\n )}\n >\n {renderCard ? renderCard(card, column) : card.content}\n </div>\n );\n })}\n\n {column.cards.length === 0 ? (\n <div\n {...sortable.getEmptyGroupProps(column.id)}\n className={styles.empty}\n >\n {emptyLabel}\n </div>\n ) : null}\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n\n/**\n * Apply a {@link KanbanMove} to a column list, returning new arrays.\n *\n * Exported because every consumer needs the same reducer.\n *\n * `toIndex` is the position of the card that was dropped **onto**, read from the\n * board as it looked before the move. Inserting at that same index *after* removing\n * the dragged card lands where the user aimed in both directions — moving down, the\n * removal shifts the target up by one and the insertion lands after it; moving up,\n * nothing shifted. Compensating for the shift (the \"obvious\" `toIndex - 1`) is what\n * turns a one-step move down into a no-op.\n *\n * @param columns - Current board.\n * @param move - The move reported by `onMove`.\n * @returns A new column list, or the same reference when the move cannot apply.\n */\nexport function applyKanbanMove(columns: KanbanColumn[], move: KanbanMove): KanbanColumn[] {\n const fromColumn = columns.find((column) => column.id === move.fromColumn);\n const card = fromColumn?.cards.find((candidate) => candidate.id === move.cardId);\n if (!fromColumn || !card) return columns;\n\n const insertAt = move.toIndex;\n\n return columns.map((column) => {\n if (column.id === move.fromColumn && column.id === move.toColumn) {\n const without = column.cards.filter((candidate) => candidate.id !== move.cardId);\n without.splice(insertAt, 0, card);\n return { ...column, cards: without };\n }\n if (column.id === move.fromColumn) {\n return { ...column, cards: column.cards.filter((c) => c.id !== move.cardId) };\n }\n if (column.id === move.toColumn) {\n const next = column.cards.slice();\n next.splice(insertAt, 0, card);\n return { ...column, cards: next };\n }\n return column;\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { cn as e } from "../../utils/cn.js";
|
|
2
|
+
import { useSortable as t } from "../../hooks/use-sortable.js";
|
|
3
|
+
import n from "./Kanban.module.js";
|
|
4
|
+
import { useMemo as r } from "react";
|
|
5
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
6
|
+
//#region src/components/Kanban/Kanban.tsx
|
|
7
|
+
function o({ columns: o, onMove: s, renderCard: c, emptyLabel: l = "Nenhum card", cardRoleDescription: u = "Card do quadro — Espaço pega, setas movem, Espaço solta", label: d = "Quadro", disabled: f = !1, className: p }) {
|
|
8
|
+
let m = r(() => o.flatMap((e, t) => e.cards.map((n, r) => ({
|
|
9
|
+
card: n,
|
|
10
|
+
column: e,
|
|
11
|
+
columnIndex: t,
|
|
12
|
+
cardIndex: r
|
|
13
|
+
}))), [o]), h = t({
|
|
14
|
+
itemCount: m.length,
|
|
15
|
+
disabled: f,
|
|
16
|
+
roleDescription: u,
|
|
17
|
+
onReorder: ({ from: e, to: t, fromGroup: n, toGroup: r }) => {
|
|
18
|
+
let i = m[e];
|
|
19
|
+
if (!i) return;
|
|
20
|
+
let a = r ?? n ?? i.column.id, c = o.find((e) => e.id === a);
|
|
21
|
+
if (!c || c.locked) return;
|
|
22
|
+
let l = m[t], u = l && l.column.id === a ? l.cardIndex : c.cards.length;
|
|
23
|
+
s({
|
|
24
|
+
cardId: i.card.id,
|
|
25
|
+
fromColumn: i.column.id,
|
|
26
|
+
toColumn: a,
|
|
27
|
+
toIndex: u
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}), g = r(() => {
|
|
31
|
+
let e = [], t = 0;
|
|
32
|
+
for (let n of o) e.push(t), t += n.cards.length;
|
|
33
|
+
return e;
|
|
34
|
+
}, [o]);
|
|
35
|
+
return /* @__PURE__ */ i("div", {
|
|
36
|
+
className: e(n.board, p),
|
|
37
|
+
"aria-label": d,
|
|
38
|
+
ref: h.setContainer,
|
|
39
|
+
children: o.map((t, r) => {
|
|
40
|
+
let o = h.overGroup === t.id && !t.locked;
|
|
41
|
+
return /* @__PURE__ */ a("div", {
|
|
42
|
+
className: e(n.column, o && n.columnActive, t.locked && n.columnLocked),
|
|
43
|
+
children: [/* @__PURE__ */ a("div", {
|
|
44
|
+
className: n.columnHeader,
|
|
45
|
+
children: [/* @__PURE__ */ i("span", {
|
|
46
|
+
className: n.columnTitle,
|
|
47
|
+
children: t.title
|
|
48
|
+
}), /* @__PURE__ */ i("span", {
|
|
49
|
+
className: n.count,
|
|
50
|
+
children: t.cards.length
|
|
51
|
+
})]
|
|
52
|
+
}), /* @__PURE__ */ a("div", {
|
|
53
|
+
className: n.cards,
|
|
54
|
+
...t.cards.length > 0 ? h.getListProps() : {},
|
|
55
|
+
"aria-label": typeof t.title == "string" ? t.title : t.id,
|
|
56
|
+
children: [t.cards.map((a, o) => {
|
|
57
|
+
let s = g[r] + o, l = h.activeIndex === s, u = h.overIndex === s && !l;
|
|
58
|
+
return /* @__PURE__ */ i("div", {
|
|
59
|
+
...h.getItemProps(s, t.id),
|
|
60
|
+
className: e(n.card, l && n.cardActive, u && n.cardOver),
|
|
61
|
+
children: c ? c(a, t) : a.content
|
|
62
|
+
}, a.id);
|
|
63
|
+
}), t.cards.length === 0 ? /* @__PURE__ */ i("div", {
|
|
64
|
+
...h.getEmptyGroupProps(t.id),
|
|
65
|
+
className: n.empty,
|
|
66
|
+
children: l
|
|
67
|
+
}) : null]
|
|
68
|
+
})]
|
|
69
|
+
}, t.id);
|
|
70
|
+
})
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function s(e, t) {
|
|
74
|
+
let n = e.find((e) => e.id === t.fromColumn), r = n?.cards.find((e) => e.id === t.cardId);
|
|
75
|
+
if (!n || !r) return e;
|
|
76
|
+
let i = t.toIndex;
|
|
77
|
+
return e.map((e) => {
|
|
78
|
+
if (e.id === t.fromColumn && e.id === t.toColumn) {
|
|
79
|
+
let n = e.cards.filter((e) => e.id !== t.cardId);
|
|
80
|
+
return n.splice(i, 0, r), {
|
|
81
|
+
...e,
|
|
82
|
+
cards: n
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (e.id === t.fromColumn) return {
|
|
86
|
+
...e,
|
|
87
|
+
cards: e.cards.filter((e) => e.id !== t.cardId)
|
|
88
|
+
};
|
|
89
|
+
if (e.id === t.toColumn) {
|
|
90
|
+
let t = e.cards.slice();
|
|
91
|
+
return t.splice(i, 0, r), {
|
|
92
|
+
...e,
|
|
93
|
+
cards: t
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return e;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
export { o as Kanban, s as applyKanbanMove };
|
|
101
|
+
|
|
102
|
+
//# sourceMappingURL=Kanban.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Kanban.js","names":[],"sources":["../../../src/components/Kanban/Kanban.tsx"],"sourcesContent":["/* eslint-disable react-hooks/refs -- `getItemProps` is called during render and its\n handlers close over `useSortable`'s mirror refs, so the rule reports the call site\n here. The refs are only written from event handlers and read from a window\n listener; the two reads that were genuinely render-time (the drag group in the\n rendered class/aria, and the group lookup inside `commit`) were fixed instead of\n silenced — see the hook's docstring. */\nimport { useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { useSortable } from \"@/hooks/use-sortable\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Kanban.module.css\";\n\n/** One card on the board. */\nexport interface KanbanCard {\n /** Stable identifier, unique across the whole board. */\n id: string;\n /** Card body. A string renders as the card's title. */\n content: ReactNode;\n}\n\n/** One column of the board. */\nexport interface KanbanColumn {\n /** Stable identifier, used as the drop group. */\n id: string;\n /** Column heading. */\n title: ReactNode;\n /** Cards in display order. */\n cards: KanbanCard[];\n /**\n * Refuses drops into this column. The cards inside can still be picked up and\n * moved elsewhere — a \"done\" column that no longer accepts work, for instance.\n */\n locked?: boolean;\n}\n\n/** Where a card ended up. */\nexport interface KanbanMove {\n cardId: string;\n fromColumn: string;\n toColumn: string;\n /** Index within the destination column. */\n toIndex: number;\n}\n\nexport interface KanbanProps {\n columns: KanbanColumn[];\n /**\n * Called once per committed move — on pointer release or on a keyboard drop.\n * You own the data: apply the move and pass the new `columns` back.\n */\n onMove: (move: KanbanMove) => void;\n /** Renders a card. Defaults to its `content`. */\n renderCard?: (card: KanbanCard, column: KanbanColumn) => ReactNode;\n /** Text shown in a column with no cards. */\n emptyLabel?: ReactNode;\n /** Announced per card, so a screen reader user learns the keyboard contract. */\n cardRoleDescription?: string;\n /**\n * Accessible name of the board. A `listbox` without a name is unusable with a\n * screen reader, so this has a default rather than being optional in practice.\n */\n label?: string;\n /** Blocks all dragging. */\n disabled?: boolean;\n className?: string;\n}\n\n/**\n * Kanban board: columns of cards, reorderable within a column and movable across\n * columns, by pointer **or** keyboard.\n *\n * The drag machine is `useSortable` — the board does not reimplement it. Cards are\n * indexed globally across columns so a single sortable can span the whole board;\n * the component translates that flat index back into `{ column, index }` for\n * `onMove`, which is the shape an app actually needs.\n *\n * Nothing is mutated here: `onMove` reports the intent once and the parent applies\n * it. That keeps the board a controlled value and avoids a re-render per pointer\n * frame.\n *\n * ARIA shape: **one `listbox` per column**, named by the column title, containing\n * only `option` cards. A single board-wide listbox does not survive the markup a\n * board needs — `listbox` requires `option`/`group` children, and the column header\n * and card wrapper in between break that ownership. Per-column listboxes also read\n * better: a screen reader announces which column it is in and how many cards it\n * holds. The column header is a plain `div`, not a `<header>`, because outside a\n * sectioning element every `<header>` becomes a page-level banner landmark — with\n * three columns that is three duplicate banners. An **empty** column is not marked\n * as a listbox at all: a listbox with zero options fails `aria-required-children`,\n * and \"no cards\" is exactly zero options.\n *\n * Known limitation: a **keyboard** move can only target positions that hold a card,\n * because the move walks the flat index space of existing cards. Dropping into an\n * empty column works by pointer; by keyboard it does not. Moving a card to a column\n * that already has one card, then reordering, is the workaround until column-switch\n * keys land.\n *\n * @example\n * ```tsx\n * const [columns, setColumns] = useState<KanbanColumn[]>([\n * { id: \"todo\", title: \"A fazer\", cards: [{ id: \"1\", content: \"Corrigir login\" }] },\n * { id: \"doing\", title: \"Fazendo\", cards: [] },\n * { id: \"done\", title: \"Feito\", cards: [], locked: true },\n * ]);\n *\n * <Kanban\n * columns={columns}\n * onMove={({ cardId, fromColumn, toColumn, toIndex }) =>\n * setColumns((current) => applyKanbanMove(current, { cardId, fromColumn, toColumn, toIndex }))\n * }\n * />\n * ```\n */\nexport function Kanban({\n columns,\n onMove,\n renderCard,\n emptyLabel = \"Nenhum card\",\n cardRoleDescription = \"Card do quadro — Espaço pega, setas movem, Espaço solta\",\n label = \"Quadro\",\n disabled = false,\n className,\n}: KanbanProps) {\n /**\n * Flat view of every card, in column order.\n *\n * `useSortable` works on a single index space, so the board flattens itself and\n * keeps the mapping back to `{ column, index }` here — the only place that has\n * to know both shapes.\n */\n const flat = useMemo(\n () =>\n columns.flatMap((column, columnIndex) =>\n column.cards.map((card, cardIndex) => ({ card, column, columnIndex, cardIndex })),\n ),\n [columns],\n );\n\n const sortable = useSortable({\n itemCount: flat.length,\n disabled,\n roleDescription: cardRoleDescription,\n onReorder: ({ from, to, fromGroup, toGroup }) => {\n const source = flat[from];\n if (!source) return;\n\n const destinationId = toGroup ?? fromGroup ?? source.column.id;\n const destination = columns.find((column) => column.id === destinationId);\n if (!destination || destination.locked) return;\n\n const target = flat[to];\n const toIndex =\n target && target.column.id === destinationId\n ? target.cardIndex\n : destination.cards.length;\n\n onMove({\n cardId: source.card.id,\n fromColumn: source.column.id,\n toColumn: destinationId,\n toIndex,\n });\n },\n });\n\n /**\n * First flat index of each column.\n *\n * Derived instead of counted with a mutable `flatIndex++` during render: a\n * render-scoped counter mutated inside the map is exactly the pattern the React\n * Compiler rules reject, and for good reason — a partial or replayed render\n * would resume from a half-advanced counter.\n */\n const columnOffsets = useMemo(() => {\n const offsets: number[] = [];\n let start = 0;\n for (const column of columns) {\n offsets.push(start);\n start += column.cards.length;\n }\n return offsets;\n }, [columns]);\n\n return (\n <div className={cn(styles.board, className)} aria-label={label} ref={sortable.setContainer}>\n {columns.map((column, columnIndex) => {\n const isDropTarget = sortable.overGroup === column.id && !column.locked;\n return (\n <div\n key={column.id}\n className={cn(\n styles.column,\n isDropTarget && styles.columnActive,\n column.locked && styles.columnLocked,\n )}\n >\n <div className={styles.columnHeader}>\n <span className={styles.columnTitle}>{column.title}</span>\n <span className={styles.count}>{column.cards.length}</span>\n </div>\n\n <div\n className={styles.cards}\n {...(column.cards.length > 0 ? sortable.getListProps() : {})}\n aria-label={typeof column.title === \"string\" ? column.title : column.id}\n >\n {column.cards.map((card, cardIndex) => {\n const index = columnOffsets[columnIndex] + cardIndex;\n const isActive = sortable.activeIndex === index;\n const isOver = sortable.overIndex === index && !isActive;\n return (\n <div\n key={card.id}\n {...sortable.getItemProps(index, column.id)}\n className={cn(\n styles.card,\n isActive && styles.cardActive,\n isOver && styles.cardOver,\n )}\n >\n {renderCard ? renderCard(card, column) : card.content}\n </div>\n );\n })}\n\n {column.cards.length === 0 ? (\n <div\n {...sortable.getEmptyGroupProps(column.id)}\n className={styles.empty}\n >\n {emptyLabel}\n </div>\n ) : null}\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n\n/**\n * Apply a {@link KanbanMove} to a column list, returning new arrays.\n *\n * Exported because every consumer needs the same reducer.\n *\n * `toIndex` is the position of the card that was dropped **onto**, read from the\n * board as it looked before the move. Inserting at that same index *after* removing\n * the dragged card lands where the user aimed in both directions — moving down, the\n * removal shifts the target up by one and the insertion lands after it; moving up,\n * nothing shifted. Compensating for the shift (the \"obvious\" `toIndex - 1`) is what\n * turns a one-step move down into a no-op.\n *\n * @param columns - Current board.\n * @param move - The move reported by `onMove`.\n * @returns A new column list, or the same reference when the move cannot apply.\n */\nexport function applyKanbanMove(columns: KanbanColumn[], move: KanbanMove): KanbanColumn[] {\n const fromColumn = columns.find((column) => column.id === move.fromColumn);\n const card = fromColumn?.cards.find((candidate) => candidate.id === move.cardId);\n if (!fromColumn || !card) return columns;\n\n const insertAt = move.toIndex;\n\n return columns.map((column) => {\n if (column.id === move.fromColumn && column.id === move.toColumn) {\n const without = column.cards.filter((candidate) => candidate.id !== move.cardId);\n without.splice(insertAt, 0, card);\n return { ...column, cards: without };\n }\n if (column.id === move.fromColumn) {\n return { ...column, cards: column.cards.filter((c) => c.id !== move.cardId) };\n }\n if (column.id === move.toColumn) {\n const next = column.cards.slice();\n next.splice(insertAt, 0, card);\n return { ...column, cards: next };\n }\n return column;\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=`tempest_board_4Ucop`,t=`tempest_column_-FohZ`,n=`tempest_columnActive_TJPM9`,r=`tempest_columnLocked_Ckkxx`,i=`tempest_columnHeader_2dsPR`,a=`tempest_columnTitle_MT8k1`,o=`tempest_count_5oF3H`,s=`tempest_cards_Gb36w`,c=`tempest_card_XJTzV`,l=`tempest_cardActive_rNhzK`,u=`tempest_cardOver_zi6kh`,d=`tempest_empty_kyA7k`,f={board:e,column:t,columnActive:n,columnLocked:r,columnHeader:i,columnTitle:a,count:o,cards:s,card:c,cardActive:l,cardOver:u,empty:d};exports.board=e,exports.card=c,exports.cardActive=l,exports.cardOver=u,exports.cards=s,exports.column=t,exports.columnActive=n,exports.columnHeader=i,exports.columnLocked=r,exports.columnTitle=a,exports.count=o,exports.default=f,exports.empty=d;
|
|
2
|
+
//# sourceMappingURL=Kanban.module.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Kanban.module.cjs","names":[],"sources":["../../../src/components/Kanban/Kanban.module.css"],"sourcesContent":[".board {\n display: flex;\n align-items: flex-start;\n gap: var(--tempest-space-4);\n overflow-x: auto;\n overscroll-behavior-x: contain;\n padding-bottom: var(--tempest-space-2);\n}\n\n.column {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-3);\n flex: 0 0 var(--tempest-kanban-column-width, 260px);\n min-width: 0;\n padding: var(--tempest-space-3);\n background-color: var(--tempest-surface);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n transition:\n background-color var(--tempest-duration-fast) var(--tempest-ease-out),\n border-color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.columnActive {\n border-color: var(--tempest-primary);\n background-color: var(--tempest-primary-soft);\n}\n\n.columnLocked {\n opacity: 0.75;\n}\n\n.columnHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-2);\n}\n\n.columnTitle {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n}\n\n.count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 22px;\n height: 22px;\n padding-inline: var(--tempest-space-2);\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text);\n font-size: var(--tempest-text-xs);\n font-variant-numeric: tabular-nums;\n}\n\n.cards {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\n min-height: 48px;\n}\n\n.card {\n padding: var(--tempest-space-3);\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n box-shadow: var(--tempest-shadow-xs);\n color: var(--tempest-text);\n font-size: var(--tempest-text-sm);\n cursor: grab;\n touch-action: none;\n user-select: none;\n}\n\n.card:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.cardActive {\n cursor: grabbing;\n opacity: 0.55;\n box-shadow: var(--tempest-shadow-md);\n}\n\n.cardOver {\n border-color: var(--tempest-primary);\n box-shadow: 0 -2px 0 0 var(--tempest-primary);\n}\n\n.empty {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 48px;\n border: 1px dashed var(--tempest-border-strong);\n border-radius: var(--tempest-radius-md);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .column {\n transition: none;\n }\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/components/Kanban/Kanban.module.css
|
|
2
|
+
var e = "tempest_board_4Ucop", t = "tempest_column_-FohZ", n = "tempest_columnActive_TJPM9", r = "tempest_columnLocked_Ckkxx", i = "tempest_columnHeader_2dsPR", a = "tempest_columnTitle_MT8k1", o = "tempest_count_5oF3H", s = "tempest_cards_Gb36w", c = "tempest_card_XJTzV", l = "tempest_cardActive_rNhzK", u = "tempest_cardOver_zi6kh", d = "tempest_empty_kyA7k", f = {
|
|
3
|
+
board: e,
|
|
4
|
+
column: t,
|
|
5
|
+
columnActive: n,
|
|
6
|
+
columnLocked: r,
|
|
7
|
+
columnHeader: i,
|
|
8
|
+
columnTitle: a,
|
|
9
|
+
count: o,
|
|
10
|
+
cards: s,
|
|
11
|
+
card: c,
|
|
12
|
+
cardActive: l,
|
|
13
|
+
cardOver: u,
|
|
14
|
+
empty: d
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { e as board, c as card, l as cardActive, u as cardOver, s as cards, t as column, n as columnActive, i as columnHeader, r as columnLocked, a as columnTitle, o as count, f as default, d as empty };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=Kanban.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Kanban.module.js","names":[],"sources":["../../../src/components/Kanban/Kanban.module.css"],"sourcesContent":[".board {\n display: flex;\n align-items: flex-start;\n gap: var(--tempest-space-4);\n overflow-x: auto;\n overscroll-behavior-x: contain;\n padding-bottom: var(--tempest-space-2);\n}\n\n.column {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-3);\n flex: 0 0 var(--tempest-kanban-column-width, 260px);\n min-width: 0;\n padding: var(--tempest-space-3);\n background-color: var(--tempest-surface);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n transition:\n background-color var(--tempest-duration-fast) var(--tempest-ease-out),\n border-color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.columnActive {\n border-color: var(--tempest-primary);\n background-color: var(--tempest-primary-soft);\n}\n\n.columnLocked {\n opacity: 0.75;\n}\n\n.columnHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-2);\n}\n\n.columnTitle {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n}\n\n.count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 22px;\n height: 22px;\n padding-inline: var(--tempest-space-2);\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text);\n font-size: var(--tempest-text-xs);\n font-variant-numeric: tabular-nums;\n}\n\n.cards {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\n min-height: 48px;\n}\n\n.card {\n padding: var(--tempest-space-3);\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n box-shadow: var(--tempest-shadow-xs);\n color: var(--tempest-text);\n font-size: var(--tempest-text-sm);\n cursor: grab;\n touch-action: none;\n user-select: none;\n}\n\n.card:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.cardActive {\n cursor: grabbing;\n opacity: 0.55;\n box-shadow: var(--tempest-shadow-md);\n}\n\n.cardOver {\n border-color: var(--tempest-primary);\n box-shadow: 0 -2px 0 0 var(--tempest-primary);\n}\n\n.empty {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 48px;\n border: 1px dashed var(--tempest-border-strong);\n border-radius: var(--tempest-radius-md);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .column {\n transition: none;\n }\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("../../hooks/use-scroll-lock.cjs"),n=require("../../hooks/use-focus-trap.cjs"),r=require("../Portal/Portal.cjs"),i=require("./Lightbox.module.cjs");let a=require("react"),o=require("react/jsx-runtime"),s=require("lucide-react");function c({items:c,open:l,index:u=0,onIndexChange:d,onClose:f,showThumbnails:p,showCounter:m=!0,loop:h=!0,closeLabel:g=`Close`,previousLabel:_=`Previous image`,nextLabel:v=`Next image`,className:y}){let b=(0,a.useRef)(null),[x,S]=(0,a.useState)(u),C=d!==void 0,w=C?u:x;n.useFocusTrap(b,l),t.useScrollLock(l);let T=(0,a.useCallback)(e=>{if(c.length===0)return;let t=h?(e+c.length)%c.length:Math.max(0,Math.min(e,c.length-1));C||S(t),d?.(t)},[C,c.length,h,d]);if((0,a.useEffect)(()=>{C||S(u)},[u,C]),(0,a.useEffect)(()=>{if(!l)return;function e(e){switch(e.key){case`Escape`:f();break;case`ArrowRight`:T(w+1);break;case`ArrowLeft`:T(w-1);break;case`Home`:T(0);break;case`End`:T(c.length-1);break;default:return}e.preventDefault()}return document.addEventListener(`keydown`,e),()=>document.removeEventListener(`keydown`,e)},[w,c.length,T,f,l]),(0,a.useEffect)(()=>{if(!(!l||typeof Image>`u`))for(let e of[-1,1]){let t=c[(w+e+c.length)%c.length];if(!t)continue;let n=new Image;n.src=t.src}},[w,c,l]),!l||c.length===0)return null;let E=c[Math.max(0,Math.min(w,c.length-1))],D=p??c.length>1,O=c.length>1;return(0,o.jsx)(r.Portal,{children:(0,o.jsxs)(`div`,{ref:b,role:`dialog`,"aria-modal":`true`,"aria-label":E.alt,className:e.cn(i.default.overlay,y),children:[(0,o.jsxs)(`div`,{className:i.default.topBar,children:[m&&O?(0,o.jsxs)(`span`,{className:i.default.counter,children:[w+1,` / `,c.length]}):(0,o.jsx)(`span`,{}),(0,o.jsx)(`button`,{type:`button`,className:i.default.iconButton,"aria-label":g,onClick:f,children:(0,o.jsx)(s.X,{size:20})})]}),(0,o.jsxs)(`div`,{className:i.default.stage,children:[O?(0,o.jsx)(`button`,{type:`button`,className:e.cn(i.default.iconButton,i.default.nav),"aria-label":_,disabled:!h&&w===0,onClick:()=>T(w-1),children:(0,o.jsx)(s.ChevronLeft,{size:24})}):null,(0,o.jsxs)(`figure`,{className:i.default.figure,children:[(0,o.jsx)(`img`,{src:E.src,alt:E.alt,className:i.default.image}),E.caption?(0,o.jsx)(`figcaption`,{className:i.default.caption,children:E.caption}):null]}),O?(0,o.jsx)(`button`,{type:`button`,className:e.cn(i.default.iconButton,i.default.nav),"aria-label":v,disabled:!h&&w===c.length-1,onClick:()=>T(w+1),children:(0,o.jsx)(s.ChevronRight,{size:24})}):null]}),D&&O?(0,o.jsx)(`div`,{className:i.default.thumbnails,role:`tablist`,"aria-label":E.alt,children:c.map((t,n)=>(0,o.jsx)(`button`,{type:`button`,role:`tab`,"aria-selected":n===w,"aria-label":t.alt,className:e.cn(i.default.thumbnail,n===w&&i.default.thumbnailActive),onClick:()=>T(n),children:(0,o.jsx)(`img`,{src:t.thumbnail??t.src,alt:``,className:i.default.thumbnailImage})},`${t.src}-${n}`))}):null]})})}exports.Lightbox=c;
|
|
2
|
+
//# sourceMappingURL=Lightbox.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Lightbox.cjs","names":[],"sources":["../../../src/components/Lightbox/Lightbox.tsx"],"sourcesContent":["import { ChevronLeft, ChevronRight, X } from \"lucide-react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useFocusTrap } from \"@/hooks/use-focus-trap\";\nimport { useScrollLock } from \"@/hooks/use-scroll-lock\";\nimport { cn } from \"@/utils/cn\";\nimport { Portal } from \"../Portal\";\nimport styles from \"./Lightbox.module.css\";\n\n/** One item of the gallery. */\nexport interface LightboxItem {\n /** Full-size image URL. */\n src: string;\n /** Alt text. Required: a gallery of unlabeled images is unusable with a screen reader. */\n alt: string;\n /** Caption rendered under the image. */\n caption?: string;\n /** Thumbnail URL for the strip. Falls back to `src`. */\n thumbnail?: string;\n}\n\nexport interface LightboxProps {\n items: LightboxItem[];\n /** Whether the overlay is open. */\n open: boolean;\n /** Index shown while open. Default `0`. */\n index?: number;\n onIndexChange?: (index: number) => void;\n onClose: () => void;\n /** Renders the thumbnail strip. Default `true` when there is more than one item. */\n showThumbnails?: boolean;\n /** Renders the `3 / 12` counter. Default `true`. */\n showCounter?: boolean;\n /**\n * Wrap around at the ends. Default `true` — in a photo viewer, hitting a dead\n * end at the last image reads as a bug more often than as a boundary.\n */\n loop?: boolean;\n closeLabel?: string;\n previousLabel?: string;\n nextLabel?: string;\n className?: string;\n}\n\n/**\n * Full-screen image viewer with keyboard navigation.\n *\n * `Esc` closes, `←`/`→` walk the gallery, `Home`/`End` jump to the ends. Focus is\n * trapped inside while open and the page behind is scroll-locked, so the overlay\n * behaves like the dialog it is instead of a fancy `<div>` the keyboard can escape.\n *\n * Only the current image is mounted; neighbours are **preloaded** via `Image()` so\n * pressing `→` does not flash an empty frame.\n *\n * @example\n * ```tsx\n * const [open, setOpen] = useState(false);\n * const [index, setIndex] = useState(0);\n *\n * <Lightbox\n * open={open}\n * items={photos.map((p) => ({ src: p.url, alt: p.description }))}\n * index={index}\n * onIndexChange={setIndex}\n * onClose={() => setOpen(false)}\n * />\n * ```\n */\nexport function Lightbox({\n items,\n open,\n index = 0,\n onIndexChange,\n onClose,\n showThumbnails,\n showCounter = true,\n loop = true,\n closeLabel = \"Close\",\n previousLabel = \"Previous image\",\n nextLabel = \"Next image\",\n className,\n}: LightboxProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [internalIndex, setInternalIndex] = useState(index);\n const isControlled = onIndexChange !== undefined;\n const current = isControlled ? index : internalIndex;\n\n useFocusTrap(containerRef, open);\n useScrollLock(open);\n\n const move = useCallback(\n (next: number): void => {\n if (items.length === 0) return;\n const target = loop\n ? (next + items.length) % items.length\n : Math.max(0, Math.min(next, items.length - 1));\n if (!isControlled) setInternalIndex(target);\n onIndexChange?.(target);\n },\n [isControlled, items.length, loop, onIndexChange],\n );\n\n useEffect(() => {\n if (!isControlled) setInternalIndex(index);\n }, [index, isControlled]);\n\n useEffect(() => {\n if (!open) return;\n function onKeyDown(event: KeyboardEvent): void {\n switch (event.key) {\n case \"Escape\":\n onClose();\n break;\n case \"ArrowRight\":\n move(current + 1);\n break;\n case \"ArrowLeft\":\n move(current - 1);\n break;\n case \"Home\":\n move(0);\n break;\n case \"End\":\n move(items.length - 1);\n break;\n default:\n return;\n }\n event.preventDefault();\n }\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, [current, items.length, move, onClose, open]);\n\n useEffect(() => {\n if (!open || typeof Image === \"undefined\") return;\n for (const offset of [-1, 1]) {\n const neighbour = items[(current + offset + items.length) % items.length];\n if (!neighbour) continue;\n const preload = new Image();\n preload.src = neighbour.src;\n }\n }, [current, items, open]);\n\n if (!open || items.length === 0) return null;\n\n const item = items[Math.max(0, Math.min(current, items.length - 1))];\n const withThumbnails = showThumbnails ?? items.length > 1;\n const multiple = items.length > 1;\n\n return (\n <Portal>\n <div\n ref={containerRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={item.alt}\n className={cn(styles.overlay, className)}\n >\n <div className={styles.topBar}>\n {showCounter && multiple ? (\n <span className={styles.counter}>\n {current + 1} / {items.length}\n </span>\n ) : (\n <span />\n )}\n <button\n type=\"button\"\n className={styles.iconButton}\n aria-label={closeLabel}\n onClick={onClose}\n >\n <X size={20} />\n </button>\n </div>\n\n <div className={styles.stage}>\n {multiple ? (\n <button\n type=\"button\"\n className={cn(styles.iconButton, styles.nav)}\n aria-label={previousLabel}\n disabled={!loop && current === 0}\n onClick={() => move(current - 1)}\n >\n <ChevronLeft size={24} />\n </button>\n ) : null}\n\n <figure className={styles.figure}>\n <img src={item.src} alt={item.alt} className={styles.image} />\n {item.caption ? (\n <figcaption className={styles.caption}>{item.caption}</figcaption>\n ) : null}\n </figure>\n\n {multiple ? (\n <button\n type=\"button\"\n className={cn(styles.iconButton, styles.nav)}\n aria-label={nextLabel}\n disabled={!loop && current === items.length - 1}\n onClick={() => move(current + 1)}\n >\n <ChevronRight size={24} />\n </button>\n ) : null}\n </div>\n\n {withThumbnails && multiple ? (\n <div className={styles.thumbnails} role=\"tablist\" aria-label={item.alt}>\n {items.map((entry, entryIndex) => (\n <button\n key={`${entry.src}-${entryIndex}`}\n type=\"button\"\n role=\"tab\"\n aria-selected={entryIndex === current}\n aria-label={entry.alt}\n className={cn(\n styles.thumbnail,\n entryIndex === current && styles.thumbnailActive,\n )}\n onClick={() => move(entryIndex)}\n >\n <img\n src={entry.thumbnail ?? entry.src}\n alt=\"\"\n className={styles.thumbnailImage}\n />\n </button>\n ))}\n </div>\n ) : null}\n </div>\n </Portal>\n );\n}\n"],"mappings":"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"}
|