@devxcrew/react-ui 0.0.0-stage → 0.1.7
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 +85 -2
- package/package.json +105 -4
- package/src/blocks/agent-chat-workspace/agent-chat-workspace.tsx +162 -0
- package/src/blocks/agent-chat-workspace/index.ts +2 -0
- package/src/blocks/agent-provider-settings/agent-provider-settings.tsx +183 -0
- package/src/blocks/agent-provider-settings/index.ts +2 -0
- package/src/blocks/agent-task-workspace/agent-task-workspace.tsx +48 -0
- package/src/blocks/agent-task-workspace/index.ts +2 -0
- package/src/blocks/analysis-context/analysis-context.tsx +6 -0
- package/src/blocks/analysis-context/index.ts +1 -0
- package/src/blocks/app-header/app-header.tsx +159 -0
- package/src/blocks/app-header/index.ts +2 -0
- package/src/blocks/archived-chat-workspace/archived-chat-workspace.tsx +29 -0
- package/src/blocks/archived-chat-workspace/index.ts +2 -0
- package/src/blocks/auth/README.md +32 -0
- package/src/blocks/auth/admin-login-page.tsx +15 -0
- package/src/blocks/auth/admin-password-forgot-page.tsx +5 -0
- package/src/blocks/auth/auth-page-layout.tsx +90 -0
- package/src/blocks/auth/client-login-page.tsx +20 -0
- package/src/blocks/auth/client-password-forgot-page.tsx +5 -0
- package/src/blocks/auth/identity-management-desk.tsx +530 -0
- package/src/blocks/auth/index.ts +12 -0
- package/src/blocks/auth/login-page.tsx +206 -0
- package/src/blocks/auth/password-forgot-page.tsx +60 -0
- package/src/blocks/auth/portal-pages.tsx +32 -0
- package/src/blocks/auth/privileged-desk.tsx +55 -0
- package/src/blocks/auth/register-page.tsx +117 -0
- package/src/blocks/auth/session-boundary.tsx +296 -0
- package/src/blocks/auth/super-admin-login-page.tsx +15 -0
- package/src/blocks/auth/super-admin-password-forgot-page.tsx +5 -0
- package/src/blocks/blog/blog-card.tsx +156 -0
- package/src/blocks/blog/blog-reader.tsx +175 -0
- package/src/blocks/blog/blog-types.ts +47 -0
- package/src/blocks/blog/index.ts +3 -0
- package/src/blocks/chat-composer/chat-composer.tsx +140 -0
- package/src/blocks/chat-composer/index.ts +1 -0
- package/src/blocks/chat-history/chat-history.tsx +59 -0
- package/src/blocks/chat-history/index.ts +1 -0
- package/src/blocks/chat-response-progress/chat-response-progress.tsx +34 -0
- package/src/blocks/chat-response-progress/index.ts +2 -0
- package/src/blocks/chat-runtime-controls/chat-runtime-controls.tsx +56 -0
- package/src/blocks/chat-runtime-controls/index.ts +2 -0
- package/src/blocks/chat-runtime-trace/chat-runtime-trace.tsx +71 -0
- package/src/blocks/chat-runtime-trace/index.ts +2 -0
- package/src/blocks/codex-connection-settings/codex-connection-settings.tsx +42 -0
- package/src/blocks/codex-connection-settings/index.ts +1 -0
- package/src/blocks/dropzone/dropzone-file-item.tsx +113 -0
- package/src/blocks/dropzone/dropzone-types.ts +32 -0
- package/src/blocks/dropzone/dropzone.tsx +216 -0
- package/src/blocks/dropzone/index.ts +3 -0
- package/src/blocks/ecommerce/cart/cart-drawer.tsx +228 -0
- package/src/blocks/ecommerce/cart/cart-types.ts +27 -0
- package/src/blocks/ecommerce/cart/index.ts +2 -0
- package/src/blocks/ecommerce/categories/categories-types.ts +23 -0
- package/src/blocks/ecommerce/categories/category-showcase.tsx +125 -0
- package/src/blocks/ecommerce/categories/index.ts +2 -0
- package/src/blocks/ecommerce/checkout/checkout-types.ts +48 -0
- package/src/blocks/ecommerce/checkout/checkout-wizard.tsx +468 -0
- package/src/blocks/ecommerce/checkout/index.ts +2 -0
- package/src/blocks/ecommerce/comparison/comparison-types.ts +28 -0
- package/src/blocks/ecommerce/comparison/index.ts +2 -0
- package/src/blocks/ecommerce/comparison/product-comparison.tsx +174 -0
- package/src/blocks/ecommerce/coupon-wallet/coupon-wallet-types.ts +21 -0
- package/src/blocks/ecommerce/coupon-wallet/coupon-wallet.tsx +154 -0
- package/src/blocks/ecommerce/coupon-wallet/index.ts +2 -0
- package/src/blocks/ecommerce/delivery-tracker/delivery-tracker-types.ts +20 -0
- package/src/blocks/ecommerce/delivery-tracker/delivery-tracker.tsx +169 -0
- package/src/blocks/ecommerce/delivery-tracker/index.ts +2 -0
- package/src/blocks/ecommerce/index.ts +12 -0
- package/src/blocks/ecommerce/payment-methods/index.ts +2 -0
- package/src/blocks/ecommerce/payment-methods/payment-method-selector.tsx +224 -0
- package/src/blocks/ecommerce/payment-methods/payment-methods-types.ts +21 -0
- package/src/blocks/ecommerce/price-history/index.ts +2 -0
- package/src/blocks/ecommerce/price-history/price-history-chart.tsx +217 -0
- package/src/blocks/ecommerce/price-history/price-history-types.ts +16 -0
- package/src/blocks/ecommerce/reviews/index.ts +2 -0
- package/src/blocks/ecommerce/reviews/reviews-section.tsx +309 -0
- package/src/blocks/ecommerce/reviews/reviews-types.ts +26 -0
- package/src/blocks/ecommerce/wishlist/index.ts +2 -0
- package/src/blocks/ecommerce/wishlist/wishlist-grid.tsx +149 -0
- package/src/blocks/ecommerce/wishlist/wishlist-types.ts +19 -0
- package/src/blocks/execution-status/README.md +25 -0
- package/src/blocks/execution-status/execution-status.tsx +135 -0
- package/src/blocks/execution-status/index.ts +2 -0
- package/src/blocks/file-tree/file-icons.tsx +84 -0
- package/src/blocks/file-tree/file-tree-node.tsx +158 -0
- package/src/blocks/file-tree/file-tree-types.ts +33 -0
- package/src/blocks/file-tree/file-tree.tsx +163 -0
- package/src/blocks/file-tree/index.ts +5 -0
- package/src/blocks/filter-builder/filter-builder.tsx +169 -0
- package/src/blocks/filter-builder/filter-row.tsx +164 -0
- package/src/blocks/filter-builder/filter-types.ts +61 -0
- package/src/blocks/filter-builder/index.ts +12 -0
- package/src/blocks/footer/footer-types.ts +35 -0
- package/src/blocks/footer/index.ts +2 -0
- package/src/blocks/footer/site-footer.tsx +212 -0
- package/src/blocks/form/form-block.tsx +113 -0
- package/src/blocks/form/form-lookup-field.tsx +60 -0
- package/src/blocks/form/index.ts +4 -0
- package/src/blocks/handover-stack/handover-stack.tsx +41 -0
- package/src/blocks/handover-stack/index.ts +1 -0
- package/src/blocks/idea-handover/idea-handover-readiness.ts +41 -0
- package/src/blocks/idea-handover/idea-handover-workspace.tsx +150 -0
- package/src/blocks/idea-handover/index.ts +4 -0
- package/src/blocks/kanban/index.ts +11 -0
- package/src/blocks/kanban/kanban-board.tsx +146 -0
- package/src/blocks/kanban/kanban-card.tsx +149 -0
- package/src/blocks/kanban/kanban-column.tsx +102 -0
- package/src/blocks/kanban/kanban-types.ts +38 -0
- package/src/blocks/loader/global-loader.tsx +90 -0
- package/src/blocks/loader/index.ts +1 -0
- package/src/blocks/mascot/README.md +21 -0
- package/src/blocks/mascot/index.ts +2 -0
- package/src/blocks/mascot/mascot.tsx +202 -0
- package/src/blocks/master-list/README.md +21 -0
- package/src/blocks/master-list/index.ts +12 -0
- package/src/blocks/master-list/master-form.tsx +157 -0
- package/src/blocks/master-list/master-list-desk.tsx +370 -0
- package/src/blocks/master-list/master-list.tsx +189 -0
- package/src/blocks/master-list/types.ts +18 -0
- package/src/blocks/mermaid-preview/index.ts +1 -0
- package/src/blocks/mermaid-preview/mermaid-preview.tsx +164 -0
- package/src/blocks/notifications/README.md +7 -0
- package/src/blocks/notifications/index.ts +1 -0
- package/src/blocks/notifications/notification-center-page.tsx +73 -0
- package/src/blocks/pricing/index.ts +7 -0
- package/src/blocks/pricing/pricing-table.tsx +173 -0
- package/src/blocks/pricing/pricing-types.ts +34 -0
- package/src/blocks/product-card/index.ts +2 -0
- package/src/blocks/product-card/product-card-types.ts +35 -0
- package/src/blocks/product-card/product-card.tsx +183 -0
- package/src/blocks/table/data-table-block.tsx +313 -0
- package/src/blocks/table/data-table-filter-menu.tsx +48 -0
- package/src/blocks/table/data-table-icon-menu-trigger.tsx +44 -0
- package/src/blocks/table/data-table-menu-content.tsx +32 -0
- package/src/blocks/table/data-table-pagination.tsx +149 -0
- package/src/blocks/table/data-table-row-actions.tsx +53 -0
- package/src/blocks/table/data-table-status.tsx +42 -0
- package/src/blocks/table/data-table-toolbar.tsx +124 -0
- package/src/blocks/table/data-table-totals.tsx +31 -0
- package/src/blocks/table/index.ts +13 -0
- package/src/blocks/workspace/index.ts +19 -0
- package/src/blocks/workspace/workspace-action-card.tsx +73 -0
- package/src/blocks/workspace/workspace-application-header.tsx +67 -0
- package/src/blocks/workspace/workspace-entity-card.tsx +48 -0
- package/src/blocks/workspace/workspace-health-summary.tsx +15 -0
- package/src/blocks/workspace/workspace-metric-card.tsx +97 -0
- package/src/blocks/workspace/workspace-page-header.tsx +43 -0
- package/src/blocks/workspace/workspace-publish-status.tsx +15 -0
- package/src/blocks/workspace/workspace-route-checklist.tsx +28 -0
- package/src/blocks/workspace/workspace-runtime-status.tsx +48 -0
- package/src/blocks/workspace/workspace-section-card.tsx +42 -0
- package/src/components/accordion.tsx +65 -0
- package/src/components/alert-dialog.tsx +160 -0
- package/src/components/alert.tsx +68 -0
- package/src/components/aspect-ratio.tsx +22 -0
- package/src/components/attachment.tsx +195 -0
- package/src/components/avatar.tsx +90 -0
- package/src/components/badge.tsx +55 -0
- package/src/components/breadcrumb.tsx +100 -0
- package/src/components/bubble.tsx +123 -0
- package/src/components/button-group.tsx +79 -0
- package/src/components/button.tsx +71 -0
- package/src/components/calendar.tsx +185 -0
- package/src/components/card.tsx +100 -0
- package/src/components/carousel.tsx +229 -0
- package/src/components/chart.tsx +338 -0
- package/src/components/checkbox.tsx +27 -0
- package/src/components/collapsible.tsx +15 -0
- package/src/components/combobox.tsx +269 -0
- package/src/components/command.tsx +181 -0
- package/src/components/compact-model-switcher.tsx +128 -0
- package/src/components/context-menu.tsx +244 -0
- package/src/components/dialog.tsx +138 -0
- package/src/components/direction.tsx +1 -0
- package/src/components/drawer.tsx +208 -0
- package/src/components/dropdown-menu.tsx +257 -0
- package/src/components/empty.tsx +93 -0
- package/src/components/field.tsx +232 -0
- package/src/components/hover-card.tsx +45 -0
- package/src/components/input-group.tsx +146 -0
- package/src/components/input-otp.tsx +83 -0
- package/src/components/input.tsx +19 -0
- package/src/components/item.tsx +188 -0
- package/src/components/kbd.tsx +26 -0
- package/src/components/label.tsx +17 -0
- package/src/components/markdown-content.tsx +113 -0
- package/src/components/marker.tsx +67 -0
- package/src/components/menubar.tsx +267 -0
- package/src/components/message-scroller.tsx +91 -0
- package/src/components/message.tsx +84 -0
- package/src/components/native-select.tsx +54 -0
- package/src/components/navigation-menu.tsx +160 -0
- package/src/components/pagination.tsx +118 -0
- package/src/components/popover.tsx +74 -0
- package/src/components/progress.tsx +82 -0
- package/src/components/questionnaire.tsx +150 -0
- package/src/components/radio-group.tsx +35 -0
- package/src/components/resizable.tsx +41 -0
- package/src/components/rich-text-editor.tsx +604 -0
- package/src/components/scroll-area.tsx +47 -0
- package/src/components/select.tsx +189 -0
- package/src/components/separator.tsx +18 -0
- package/src/components/sheet.tsx +125 -0
- package/src/components/sidebar.tsx +693 -0
- package/src/components/skeleton.tsx +13 -0
- package/src/components/slider.tsx +51 -0
- package/src/components/sonner.tsx +45 -0
- package/src/components/sparkline.tsx +231 -0
- package/src/components/spinner.tsx +20 -0
- package/src/components/status-badge.tsx +114 -0
- package/src/components/switch.tsx +31 -0
- package/src/components/table.tsx +91 -0
- package/src/components/tabs.tsx +72 -0
- package/src/components/textarea.tsx +17 -0
- package/src/components/toast.tsx +222 -0
- package/src/components/toggle-group.tsx +85 -0
- package/src/components/toggle.tsx +42 -0
- package/src/components/tooltip.tsx +53 -0
- package/src/design-system/README.md +55 -0
- package/src/design-system/action-intents.ts +21 -0
- package/src/design-system/agent-manifest.ts +83 -0
- package/src/design-system/block-registry.ts +272 -0
- package/src/design-system/component-registry.ts +142 -0
- package/src/design-system/contracts.ts +69 -0
- package/src/design-system/defaults.ts +3 -0
- package/src/design-system/index.ts +29 -0
- package/src/design-system/mcp-server.ts +156 -0
- package/src/design-system/page-registry.ts +74 -0
- package/src/design-system/selection.ts +67 -0
- package/src/design-system/template-registry.ts +33 -0
- package/src/features/interface-topology/index.ts +14 -0
- package/src/features/interface-topology/interface-topology.types.ts +40 -0
- package/src/features/interface-topology/topology-control.tsx +18 -0
- package/src/features/interface-topology/topology-inspector.tsx +228 -0
- package/src/features/interface-topology/topology-marker.tsx +47 -0
- package/src/features/interface-topology/topology-region.tsx +44 -0
- package/src/features/interface-topology/use-interface-topology.ts +178 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/hooks/use-scroll-follow.ts +22 -0
- package/src/index.ts +11 -0
- package/src/layouts/README.md +65 -0
- package/src/layouts/agent-workspace/agent-activity-rail.tsx +101 -0
- package/src/layouts/agent-workspace/agent-workspace.tsx +27 -0
- package/src/layouts/agent-workspace/agent-workspace.types.ts +28 -0
- package/src/layouts/agent-workspace/index.ts +7 -0
- package/src/layouts/blog-header/blog-header-types.ts +25 -0
- package/src/layouts/blog-header/blog-header.tsx +172 -0
- package/src/layouts/blog-header/index.ts +2 -0
- package/src/layouts/documentation-workspace/README.md +8 -0
- package/src/layouts/documentation-workspace/documentation-workspace.tsx +34 -0
- package/src/layouts/documentation-workspace/index.ts +1 -0
- package/src/layouts/ecommerce-header/ecommerce-actions.tsx +89 -0
- package/src/layouts/ecommerce-header/ecommerce-announcement.tsx +43 -0
- package/src/layouts/ecommerce-header/ecommerce-drawer.tsx +97 -0
- package/src/layouts/ecommerce-header/ecommerce-header-types.ts +59 -0
- package/src/layouts/ecommerce-header/ecommerce-header.tsx +47 -0
- package/src/layouts/ecommerce-header/ecommerce-navigation.tsx +98 -0
- package/src/layouts/ecommerce-header/ecommerce-search.tsx +86 -0
- package/src/layouts/ecommerce-header/index.ts +2 -0
- package/src/layouts/main-workspace/index.ts +28 -0
- package/src/layouts/mdi/index.ts +1 -0
- package/src/layouts/mdi/mdi.tsx +20 -0
- package/src/layouts/mdi-main/index.ts +27 -0
- package/src/layouts/mdi-main/mdi-app-catalog.generated.ts +159 -0
- package/src/layouts/mdi-main/mdi-app-catalog.ts +76 -0
- package/src/layouts/mdi-main/mdi-app-switcher.tsx +107 -0
- package/src/layouts/mdi-main/mdi-feature-settings.tsx +135 -0
- package/src/layouts/mdi-main/mdi-global-search.tsx +192 -0
- package/src/layouts/mdi-main/mdi-main.tsx +241 -0
- package/src/layouts/mdi-main/mdi-notifications-menu.tsx +197 -0
- package/src/layouts/mdi-main/mdi-profile-menu.tsx +79 -0
- package/src/layouts/mdi-main/mdi-sidebar-navigation.tsx +226 -0
- package/src/layouts/mdi-main/mdi-sidebar.tsx +110 -0
- package/src/layouts/mdi-main/mdi-status-bar.tsx +26 -0
- package/src/layouts/mdi-main/mdi-top-menu-control.ts +4 -0
- package/src/layouts/mdi-main/mdi-top-menu.tsx +117 -0
- package/src/layouts/mdi-main/mdi-topology.tsx +297 -0
- package/src/layouts/mdi-main/mdi-tweak-panel.tsx +104 -0
- package/src/layouts/mdi-main/mdi-types.ts +131 -0
- package/src/layouts/mdi-main/use-mdi-features.ts +36 -0
- package/src/layouts/mdi-main/use-mdi-sidebar-state.ts +59 -0
- package/src/layouts/site-header/announcement-bar.tsx +55 -0
- package/src/layouts/site-header/category-strip.tsx +41 -0
- package/src/layouts/site-header/index.ts +16 -0
- package/src/layouts/site-header/main-navbar.tsx +179 -0
- package/src/layouts/site-header/mobile-drawer.tsx +140 -0
- package/src/layouts/site-header/site-header-types.ts +63 -0
- package/src/layouts/site-header/site-header.tsx +58 -0
- package/src/lib/random-id.ts +19 -0
- package/src/lib/utils.ts +6 -0
- package/src/providers/pdf-provider.ts +137 -0
- package/src/styles/globals.css +374 -0
- package/src/templates/README.md +12 -0
- package/src/templates/dashboard-01/components/app-sidebar.tsx +177 -0
- package/src/templates/dashboard-01/components/chart-area-interactive.tsx +276 -0
- package/src/templates/dashboard-01/components/data-table-cell-viewer.tsx +213 -0
- package/src/templates/dashboard-01/components/data-table.tsx +577 -0
- package/src/templates/dashboard-01/components/nav-documents.tsx +79 -0
- package/src/templates/dashboard-01/components/nav-main.tsx +55 -0
- package/src/templates/dashboard-01/components/nav-secondary.tsx +39 -0
- package/src/templates/dashboard-01/components/nav-user.tsx +97 -0
- package/src/templates/dashboard-01/components/section-cards.tsx +99 -0
- package/src/templates/dashboard-01/components/site-header.tsx +14 -0
- package/src/templates/dashboard-01/data.json +614 -0
- package/src/templates/dashboard-01/index.ts +7 -0
- package/src/templates/documentation-sidebar/app-sidebar.tsx +95 -0
- package/src/templates/documentation-sidebar/index.ts +2 -0
- package/src/templates/sidebar-07/components/app-sidebar.tsx +175 -0
- package/src/templates/sidebar-07/components/nav-main.tsx +64 -0
- package/src/templates/sidebar-07/components/nav-projects.tsx +79 -0
- package/src/templates/sidebar-07/components/nav-user.tsx +105 -0
- package/src/templates/sidebar-07/components/team-switcher.tsx +94 -0
- package/src/templates/sidebar-07/index.ts +1 -0
- package/src/templates/ui-page/index.ts +7 -0
- package/src/templates/ui-page/ui-browser-frame.tsx +27 -0
- package/src/templates/ui-page/ui-template-code.tsx +48 -0
- package/src/templates/ui-page/ui-template-header.tsx +87 -0
- package/src/templates/ui-page/ui-template-navigation.tsx +60 -0
- package/src/templates/ui-page/ui-template-page.tsx +97 -0
- package/src/theme/index.ts +12 -0
- package/src/theme/theme-config.ts +30 -0
- package/src/theme/theme-provider.tsx +142 -0
- package/src/theme/theme-selector.tsx +75 -0
- package/src/tokens/index.ts +15 -0
- package/src/vite-env.d.ts +3 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Slider as SliderPrimitive } from '@base-ui/react/slider'
|
|
2
|
+
import { cn } from '../lib/utils'
|
|
3
|
+
|
|
4
|
+
function Slider({
|
|
5
|
+
className,
|
|
6
|
+
defaultValue,
|
|
7
|
+
value,
|
|
8
|
+
min = 0,
|
|
9
|
+
max = 100,
|
|
10
|
+
...props
|
|
11
|
+
}: SliderPrimitive.Root.Props) {
|
|
12
|
+
const _values = Array.isArray(value)
|
|
13
|
+
? value
|
|
14
|
+
: Array.isArray(defaultValue)
|
|
15
|
+
? defaultValue
|
|
16
|
+
: [min, max]
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<SliderPrimitive.Root
|
|
20
|
+
className={cn('data-horizontal:w-full data-vertical:h-full', className)}
|
|
21
|
+
data-slot="slider"
|
|
22
|
+
defaultValue={defaultValue}
|
|
23
|
+
value={value}
|
|
24
|
+
min={min}
|
|
25
|
+
max={max}
|
|
26
|
+
thumbAlignment="edge"
|
|
27
|
+
{...props}
|
|
28
|
+
>
|
|
29
|
+
<SliderPrimitive.Control className="relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col">
|
|
30
|
+
<SliderPrimitive.Track
|
|
31
|
+
data-slot="slider-track"
|
|
32
|
+
className="relative grow overflow-hidden rounded-full bg-muted select-none data-horizontal:h-1 data-horizontal:w-full data-vertical:h-full data-vertical:w-1"
|
|
33
|
+
>
|
|
34
|
+
<SliderPrimitive.Indicator
|
|
35
|
+
data-slot="slider-range"
|
|
36
|
+
className="bg-primary select-none data-horizontal:h-full data-vertical:w-full"
|
|
37
|
+
/>
|
|
38
|
+
</SliderPrimitive.Track>
|
|
39
|
+
{Array.from({ length: _values.length }, (_, index) => (
|
|
40
|
+
<SliderPrimitive.Thumb
|
|
41
|
+
data-slot="slider-thumb"
|
|
42
|
+
key={index}
|
|
43
|
+
className="relative block size-3 shrink-0 rounded-full border border-ring bg-white ring-ring/50 transition-[color,box-shadow] select-none after:absolute after:-inset-2 hover:ring-3 focus-visible:ring-3 focus-visible:outline-hidden active:ring-3 disabled:pointer-events-none disabled:opacity-50"
|
|
44
|
+
/>
|
|
45
|
+
))}
|
|
46
|
+
</SliderPrimitive.Control>
|
|
47
|
+
</SliderPrimitive.Root>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { Slider }
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Toaster as Sonner, type ToasterProps } from 'sonner'
|
|
4
|
+
import {
|
|
5
|
+
CircleCheckIcon,
|
|
6
|
+
InfoIcon,
|
|
7
|
+
TriangleAlertIcon,
|
|
8
|
+
OctagonXIcon,
|
|
9
|
+
Loader2Icon,
|
|
10
|
+
} from 'lucide-react'
|
|
11
|
+
import { useTheme } from '../theme/theme-provider'
|
|
12
|
+
|
|
13
|
+
const Toaster = ({ ...props }: ToasterProps) => {
|
|
14
|
+
const { theme = 'system' } = useTheme()
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Sonner
|
|
18
|
+
theme={theme as ToasterProps['theme']}
|
|
19
|
+
className="toaster group"
|
|
20
|
+
icons={{
|
|
21
|
+
success: <CircleCheckIcon className="size-4" />,
|
|
22
|
+
info: <InfoIcon className="size-4" />,
|
|
23
|
+
warning: <TriangleAlertIcon className="size-4" />,
|
|
24
|
+
error: <OctagonXIcon className="size-4" />,
|
|
25
|
+
loading: <Loader2Icon className="size-4 animate-spin" />,
|
|
26
|
+
}}
|
|
27
|
+
style={
|
|
28
|
+
{
|
|
29
|
+
'--normal-bg': 'var(--popover)',
|
|
30
|
+
'--normal-text': 'var(--popover-foreground)',
|
|
31
|
+
'--normal-border': 'var(--border)',
|
|
32
|
+
'--border-radius': 'var(--radius)',
|
|
33
|
+
} as React.CSSProperties
|
|
34
|
+
}
|
|
35
|
+
toastOptions={{
|
|
36
|
+
classNames: {
|
|
37
|
+
toast: 'cn-toast',
|
|
38
|
+
},
|
|
39
|
+
}}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { Toaster }
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { useId } from 'react'
|
|
2
|
+
import { cn } from '../lib/utils'
|
|
3
|
+
|
|
4
|
+
export type SparklineTone = 'primary' | 'positive' | 'negative' | 'neutral' | 'warning'
|
|
5
|
+
export type SparklineType = 'line' | 'area' | 'bar' | 'trend'
|
|
6
|
+
|
|
7
|
+
export interface SparklineDatum {
|
|
8
|
+
value: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface SparklineProps {
|
|
12
|
+
ariaLabel?: string
|
|
13
|
+
className?: string
|
|
14
|
+
data: readonly number[] | readonly SparklineDatum[]
|
|
15
|
+
height?: number
|
|
16
|
+
showDot?: boolean
|
|
17
|
+
showMinMax?: boolean
|
|
18
|
+
showTrendBadge?: boolean
|
|
19
|
+
strokeWidth?: number
|
|
20
|
+
tone?: SparklineTone
|
|
21
|
+
trendLabel?: string
|
|
22
|
+
type?: SparklineType
|
|
23
|
+
width?: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const toneStyles: Record<
|
|
27
|
+
SparklineTone,
|
|
28
|
+
{
|
|
29
|
+
badge: string
|
|
30
|
+
bar: string
|
|
31
|
+
dot: string
|
|
32
|
+
stopColor: string
|
|
33
|
+
stroke: string
|
|
34
|
+
}
|
|
35
|
+
> = {
|
|
36
|
+
primary: {
|
|
37
|
+
badge: 'bg-primary/10 text-primary',
|
|
38
|
+
bar: 'fill-primary',
|
|
39
|
+
dot: 'fill-primary stroke-background',
|
|
40
|
+
stopColor: 'currentColor',
|
|
41
|
+
stroke: 'stroke-primary text-primary',
|
|
42
|
+
},
|
|
43
|
+
positive: {
|
|
44
|
+
badge: 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400',
|
|
45
|
+
bar: 'fill-emerald-500',
|
|
46
|
+
dot: 'fill-emerald-500 stroke-background',
|
|
47
|
+
stopColor: '#10b981',
|
|
48
|
+
stroke: 'stroke-emerald-600 dark:stroke-emerald-400',
|
|
49
|
+
},
|
|
50
|
+
negative: {
|
|
51
|
+
badge: 'bg-destructive/10 text-destructive',
|
|
52
|
+
bar: 'fill-destructive',
|
|
53
|
+
dot: 'fill-destructive stroke-background',
|
|
54
|
+
stopColor: '#ef4444',
|
|
55
|
+
stroke: 'stroke-destructive',
|
|
56
|
+
},
|
|
57
|
+
neutral: {
|
|
58
|
+
badge: 'bg-muted text-muted-foreground',
|
|
59
|
+
bar: 'fill-muted-foreground/60',
|
|
60
|
+
dot: 'fill-muted-foreground stroke-background',
|
|
61
|
+
stopColor: '#9ca3af',
|
|
62
|
+
stroke: 'stroke-muted-foreground',
|
|
63
|
+
},
|
|
64
|
+
warning: {
|
|
65
|
+
badge: 'bg-amber-500/10 text-amber-600 dark:text-amber-400',
|
|
66
|
+
bar: 'fill-amber-500',
|
|
67
|
+
dot: 'fill-amber-500 stroke-background',
|
|
68
|
+
stopColor: '#f59e0b',
|
|
69
|
+
stroke: 'stroke-amber-600 dark:stroke-amber-400',
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function Sparkline({
|
|
74
|
+
ariaLabel = 'Data trend sparkline',
|
|
75
|
+
className,
|
|
76
|
+
data,
|
|
77
|
+
height = 32,
|
|
78
|
+
showDot = true,
|
|
79
|
+
showMinMax = false,
|
|
80
|
+
showTrendBadge = false,
|
|
81
|
+
strokeWidth = 2,
|
|
82
|
+
tone = 'primary',
|
|
83
|
+
trendLabel,
|
|
84
|
+
type = 'line',
|
|
85
|
+
width = 120,
|
|
86
|
+
}: SparklineProps) {
|
|
87
|
+
const gradientId = useId()
|
|
88
|
+
const rawValues = data.map((d) => (typeof d === 'number' ? d : d.value))
|
|
89
|
+
|
|
90
|
+
if (rawValues.length === 0) {
|
|
91
|
+
return (
|
|
92
|
+
<div
|
|
93
|
+
aria-label="Empty sparkline"
|
|
94
|
+
className={cn('inline-flex items-center text-xs text-muted-foreground', className)}
|
|
95
|
+
style={{ height, width }}
|
|
96
|
+
>
|
|
97
|
+
--
|
|
98
|
+
</div>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const min = Math.min(...rawValues)
|
|
103
|
+
const max = Math.max(...rawValues)
|
|
104
|
+
const range = max - min === 0 ? 1 : max - min
|
|
105
|
+
const padding = strokeWidth + 2
|
|
106
|
+
const innerWidth = Math.max(10, width - padding * 2)
|
|
107
|
+
const innerHeight = Math.max(10, height - padding * 2)
|
|
108
|
+
|
|
109
|
+
const points = rawValues.map((val, idx) => {
|
|
110
|
+
const x = padding + (idx / Math.max(1, rawValues.length - 1)) * innerWidth
|
|
111
|
+
const y = padding + innerHeight - ((val - min) / range) * innerHeight
|
|
112
|
+
return { val, x, y }
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
const firstVal = rawValues[0]
|
|
116
|
+
const lastVal = rawValues[rawValues.length - 1]
|
|
117
|
+
const calculatedDelta =
|
|
118
|
+
firstVal !== 0 ? (((lastVal - firstVal) / Math.abs(firstVal)) * 100).toFixed(1) : '0'
|
|
119
|
+
const isPositive = lastVal >= firstVal
|
|
120
|
+
const deltaBadgeText = trendLabel ?? (isPositive ? `+${calculatedDelta}%` : `${calculatedDelta}%`)
|
|
121
|
+
|
|
122
|
+
const pathD = points.reduce((acc, pt, idx) => {
|
|
123
|
+
if (idx === 0) return `M ${pt.x.toFixed(1)} ${pt.y.toFixed(1)}`
|
|
124
|
+
return `${acc} L ${pt.x.toFixed(1)} ${pt.y.toFixed(1)}`
|
|
125
|
+
}, '')
|
|
126
|
+
|
|
127
|
+
const lastPoint = points[points.length - 1]
|
|
128
|
+
const firstPoint = points[0]
|
|
129
|
+
const areaD = `${pathD} L ${lastPoint.x.toFixed(1)} ${(height - padding / 2).toFixed(1)} L ${firstPoint.x.toFixed(1)} ${(height - padding / 2).toFixed(1)} Z`
|
|
130
|
+
|
|
131
|
+
const selectedTone = toneStyles[tone]
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<div
|
|
135
|
+
aria-label={ariaLabel}
|
|
136
|
+
className={cn('inline-flex items-center gap-2', className)}
|
|
137
|
+
role="img"
|
|
138
|
+
>
|
|
139
|
+
<svg
|
|
140
|
+
className="overflow-visible"
|
|
141
|
+
height={height}
|
|
142
|
+
viewBox={`0 0 ${width} ${height}`}
|
|
143
|
+
width={width}
|
|
144
|
+
>
|
|
145
|
+
<defs>
|
|
146
|
+
<linearGradient id={gradientId} x1="0" x2="0" y1="0" y2="1">
|
|
147
|
+
<stop offset="0%" stopColor={selectedTone.stopColor} stopOpacity={0.35} />
|
|
148
|
+
<stop offset="100%" stopColor={selectedTone.stopColor} stopOpacity={0.0} />
|
|
149
|
+
</linearGradient>
|
|
150
|
+
</defs>
|
|
151
|
+
|
|
152
|
+
{type === 'bar' ? (
|
|
153
|
+
<g>
|
|
154
|
+
{points.map((pt, idx) => {
|
|
155
|
+
const barWidth = Math.max(2, (innerWidth / rawValues.length) * 0.7)
|
|
156
|
+
const barHeight = Math.max(2, innerHeight - (pt.y - padding))
|
|
157
|
+
const barX = pt.x - barWidth / 2
|
|
158
|
+
const barY = pt.y
|
|
159
|
+
return (
|
|
160
|
+
<rect
|
|
161
|
+
className={selectedTone.bar}
|
|
162
|
+
height={barHeight}
|
|
163
|
+
key={idx}
|
|
164
|
+
rx={1.5}
|
|
165
|
+
width={barWidth}
|
|
166
|
+
x={barX}
|
|
167
|
+
y={barY}
|
|
168
|
+
/>
|
|
169
|
+
)
|
|
170
|
+
})}
|
|
171
|
+
</g>
|
|
172
|
+
) : (
|
|
173
|
+
<g>
|
|
174
|
+
{(type === 'area' || type === 'trend') && (
|
|
175
|
+
<path d={areaD} fill={`url(#${gradientId})`} />
|
|
176
|
+
)}
|
|
177
|
+
<path
|
|
178
|
+
className={cn(selectedTone.stroke, 'fill-none')}
|
|
179
|
+
d={pathD}
|
|
180
|
+
strokeLinecap="round"
|
|
181
|
+
strokeLinejoin="round"
|
|
182
|
+
strokeWidth={strokeWidth}
|
|
183
|
+
/>
|
|
184
|
+
{showDot && (
|
|
185
|
+
<circle
|
|
186
|
+
className={selectedTone.dot}
|
|
187
|
+
cx={lastPoint.x}
|
|
188
|
+
cy={lastPoint.y}
|
|
189
|
+
r={strokeWidth + 1.5}
|
|
190
|
+
strokeWidth={1.5}
|
|
191
|
+
/>
|
|
192
|
+
)}
|
|
193
|
+
{showMinMax && (
|
|
194
|
+
<>
|
|
195
|
+
{points.find((p) => p.val === max) && (
|
|
196
|
+
<circle
|
|
197
|
+
className="fill-emerald-500 stroke-background"
|
|
198
|
+
cx={points.find((p) => p.val === max)!.x}
|
|
199
|
+
cy={points.find((p) => p.val === max)!.y}
|
|
200
|
+
r={2.5}
|
|
201
|
+
strokeWidth={1}
|
|
202
|
+
/>
|
|
203
|
+
)}
|
|
204
|
+
{points.find((p) => p.val === min) && (
|
|
205
|
+
<circle
|
|
206
|
+
className="fill-destructive stroke-background"
|
|
207
|
+
cx={points.find((p) => p.val === min)!.x}
|
|
208
|
+
cy={points.find((p) => p.val === min)!.y}
|
|
209
|
+
r={2.5}
|
|
210
|
+
strokeWidth={1}
|
|
211
|
+
/>
|
|
212
|
+
)}
|
|
213
|
+
</>
|
|
214
|
+
)}
|
|
215
|
+
</g>
|
|
216
|
+
)}
|
|
217
|
+
</svg>
|
|
218
|
+
|
|
219
|
+
{showTrendBadge && (
|
|
220
|
+
<span
|
|
221
|
+
className={cn(
|
|
222
|
+
'inline-flex items-center rounded-md px-1.5 py-0.5 text-[11px] font-semibold tracking-tight',
|
|
223
|
+
selectedTone.badge,
|
|
224
|
+
)}
|
|
225
|
+
>
|
|
226
|
+
{deltaBadgeText}
|
|
227
|
+
</span>
|
|
228
|
+
)}
|
|
229
|
+
</div>
|
|
230
|
+
)
|
|
231
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { cn } from '../lib/utils'
|
|
2
|
+
import { Loader2Icon } from 'lucide-react'
|
|
3
|
+
|
|
4
|
+
function Spinner({
|
|
5
|
+
className,
|
|
6
|
+
animated = true,
|
|
7
|
+
...props
|
|
8
|
+
}: React.ComponentProps<'svg'> & { animated?: boolean }) {
|
|
9
|
+
return (
|
|
10
|
+
<Loader2Icon
|
|
11
|
+
data-slot="spinner"
|
|
12
|
+
role="status"
|
|
13
|
+
aria-label="Loading"
|
|
14
|
+
className={cn('size-4', animated && 'motion-safe:animate-spin', className)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { Spinner }
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { CircleCheck } from 'lucide-react';
|
|
2
|
+
import { cn } from '../lib/utils';
|
|
3
|
+
|
|
4
|
+
export type StatusBadgeValue =
|
|
5
|
+
| 'active'
|
|
6
|
+
| 'in-review'
|
|
7
|
+
| 'complete'
|
|
8
|
+
| 'planned'
|
|
9
|
+
| 'draft'
|
|
10
|
+
| 'open'
|
|
11
|
+
| 'won'
|
|
12
|
+
| 'lost'
|
|
13
|
+
| 'pending'
|
|
14
|
+
| 'processing'
|
|
15
|
+
| 'failed'
|
|
16
|
+
| 'cancelled'
|
|
17
|
+
| 'archived'
|
|
18
|
+
| 'suspended'
|
|
19
|
+
| 'queued'
|
|
20
|
+
| 'attention'
|
|
21
|
+
| 'idle';
|
|
22
|
+
|
|
23
|
+
const statusStyles: Record<StatusBadgeValue, string> = {
|
|
24
|
+
active: 'border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-900 dark:bg-blue-950/40 dark:text-blue-300',
|
|
25
|
+
'in-review':
|
|
26
|
+
'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900 dark:bg-amber-950/40 dark:text-amber-300',
|
|
27
|
+
complete:
|
|
28
|
+
'border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900 dark:bg-emerald-950/40 dark:text-emerald-300',
|
|
29
|
+
planned:
|
|
30
|
+
'border-orange-200 bg-orange-50 text-orange-700 dark:border-orange-900 dark:bg-orange-950/40 dark:text-orange-300',
|
|
31
|
+
draft: 'border-slate-200 bg-slate-50 text-slate-700 dark:border-slate-800 dark:bg-slate-900/40 dark:text-slate-300',
|
|
32
|
+
open: 'border-sky-200 bg-sky-50 text-sky-700 dark:border-sky-900 dark:bg-sky-950/40 dark:text-sky-300',
|
|
33
|
+
won: 'border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900 dark:bg-emerald-950/40 dark:text-emerald-300',
|
|
34
|
+
lost: 'border-red-200 bg-red-50 text-red-700 dark:border-red-900 dark:bg-red-950/40 dark:text-red-300',
|
|
35
|
+
pending:
|
|
36
|
+
'border-yellow-200 bg-yellow-50 text-yellow-700 dark:border-yellow-900 dark:bg-yellow-950/40 dark:text-yellow-300',
|
|
37
|
+
processing:
|
|
38
|
+
'border-violet-200 bg-violet-50 text-violet-700 dark:border-violet-900 dark:bg-violet-950/40 dark:text-violet-300',
|
|
39
|
+
failed: 'border-red-200 bg-red-50 text-red-700 dark:border-red-900 dark:bg-red-950/40 dark:text-red-300',
|
|
40
|
+
cancelled: 'border-zinc-200 bg-zinc-50 text-zinc-700 dark:border-zinc-800 dark:bg-zinc-900/40 dark:text-zinc-300',
|
|
41
|
+
archived: 'border-gray-200 bg-gray-50 text-gray-700 dark:border-gray-800 dark:bg-gray-900/40 dark:text-gray-300',
|
|
42
|
+
suspended:
|
|
43
|
+
'border-purple-200 bg-purple-50 text-purple-700 dark:border-purple-900 dark:bg-purple-950/40 dark:text-purple-300',
|
|
44
|
+
queued:
|
|
45
|
+
'border-indigo-200 bg-indigo-50 text-indigo-700 dark:border-indigo-900 dark:bg-indigo-950/40 dark:text-indigo-300',
|
|
46
|
+
attention: 'border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-900 dark:bg-rose-950/40 dark:text-rose-300',
|
|
47
|
+
idle: 'border-neutral-200 bg-neutral-50 text-neutral-700 dark:border-neutral-800 dark:bg-neutral-900/40 dark:text-neutral-300',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const statusLabels: Record<StatusBadgeValue, string> = {
|
|
51
|
+
active: 'Active',
|
|
52
|
+
'in-review': 'In review',
|
|
53
|
+
complete: 'Complete',
|
|
54
|
+
planned: 'Planned',
|
|
55
|
+
draft: 'Draft',
|
|
56
|
+
open: 'Open',
|
|
57
|
+
won: 'Won',
|
|
58
|
+
lost: 'Lost',
|
|
59
|
+
pending: 'Pending',
|
|
60
|
+
processing: 'Processing',
|
|
61
|
+
failed: 'Failed',
|
|
62
|
+
cancelled: 'Cancelled',
|
|
63
|
+
archived: 'Archived',
|
|
64
|
+
suspended: 'Suspended',
|
|
65
|
+
queued: 'Queued',
|
|
66
|
+
attention: 'Attention',
|
|
67
|
+
idle: 'Idle',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export function getStatusBadgeValue(value: string): StatusBadgeValue {
|
|
71
|
+
const normalized = value.trim().toLowerCase();
|
|
72
|
+
if (['complete', 'completed', 'done'].includes(normalized)) return 'complete';
|
|
73
|
+
if (['in review', 'in-review', 'long hold', 'hold for approval'].includes(normalized)) return 'in-review';
|
|
74
|
+
if (['active'].includes(normalized)) return 'active';
|
|
75
|
+
if (['draft'].includes(normalized)) return 'draft';
|
|
76
|
+
if (['open'].includes(normalized)) return 'open';
|
|
77
|
+
if (['won'].includes(normalized)) return 'won';
|
|
78
|
+
if (['lost'].includes(normalized)) return 'lost';
|
|
79
|
+
if (['pending'].includes(normalized)) return 'pending';
|
|
80
|
+
if (['processing', 'in progress'].includes(normalized)) return 'processing';
|
|
81
|
+
if (['failed', 'error'].includes(normalized)) return 'failed';
|
|
82
|
+
if (['cancelled', 'canceled'].includes(normalized)) return 'cancelled';
|
|
83
|
+
if (['archived'].includes(normalized)) return 'archived';
|
|
84
|
+
if (['suspended', 'disabled'].includes(normalized)) return 'suspended';
|
|
85
|
+
if (['queued'].includes(normalized)) return 'queued';
|
|
86
|
+
if (['attention'].includes(normalized)) return 'attention';
|
|
87
|
+
if (['idle'].includes(normalized)) return 'idle';
|
|
88
|
+
return 'active';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function StatusBadge({
|
|
92
|
+
className,
|
|
93
|
+
label,
|
|
94
|
+
status,
|
|
95
|
+
}: {
|
|
96
|
+
status: StatusBadgeValue;
|
|
97
|
+
label?: string;
|
|
98
|
+
className?: string;
|
|
99
|
+
}) {
|
|
100
|
+
const displayLabel = label ?? statusLabels[status];
|
|
101
|
+
return (
|
|
102
|
+
<span
|
|
103
|
+
aria-label={`Status: ${displayLabel}`}
|
|
104
|
+
className={cn(
|
|
105
|
+
'inline-flex h-6 w-fit items-center gap-1 rounded-md border px-2 text-xs font-medium whitespace-nowrap',
|
|
106
|
+
statusStyles[status],
|
|
107
|
+
className,
|
|
108
|
+
)}
|
|
109
|
+
>
|
|
110
|
+
<CircleCheck aria-hidden="true" className="size-3.5" />
|
|
111
|
+
{displayLabel}
|
|
112
|
+
</span>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Switch as SwitchPrimitive } from '@base-ui/react/switch'
|
|
4
|
+
import { cn } from '../lib/utils'
|
|
5
|
+
|
|
6
|
+
function Switch({
|
|
7
|
+
className,
|
|
8
|
+
size = 'default',
|
|
9
|
+
...props
|
|
10
|
+
}: SwitchPrimitive.Root.Props & {
|
|
11
|
+
size?: 'sm' | 'default'
|
|
12
|
+
}) {
|
|
13
|
+
return (
|
|
14
|
+
<SwitchPrimitive.Root
|
|
15
|
+
data-slot="switch"
|
|
16
|
+
data-size={size}
|
|
17
|
+
className={cn(
|
|
18
|
+
'peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none group-has-[:focus-visible]/field-label:border-transparent group-has-[:focus-visible]/field-label:ring-0 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50',
|
|
19
|
+
className,
|
|
20
|
+
)}
|
|
21
|
+
{...props}
|
|
22
|
+
>
|
|
23
|
+
<SwitchPrimitive.Thumb
|
|
24
|
+
data-slot="switch-thumb"
|
|
25
|
+
className="pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
|
|
26
|
+
/>
|
|
27
|
+
</SwitchPrimitive.Root>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { Switch }
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { cn } from '../lib/utils'
|
|
5
|
+
|
|
6
|
+
function Table({ className, ...props }: React.ComponentProps<'table'>) {
|
|
7
|
+
return (
|
|
8
|
+
<div
|
|
9
|
+
data-slot="table-container"
|
|
10
|
+
className="relative w-full overflow-x-auto [scrollbar-color:var(--border)_transparent] [scrollbar-width:thin] [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-border [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar]:h-1"
|
|
11
|
+
>
|
|
12
|
+
<table
|
|
13
|
+
data-slot="table"
|
|
14
|
+
className={cn('w-full caption-bottom text-sm', className)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
</div>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) {
|
|
22
|
+
return <thead data-slot="table-header" className={cn('[&_tr]:border-b', className)} {...props} />
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) {
|
|
26
|
+
return (
|
|
27
|
+
<tbody
|
|
28
|
+
data-slot="table-body"
|
|
29
|
+
className={cn('[&_tr:last-child]:border-0', className)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) {
|
|
36
|
+
return (
|
|
37
|
+
<tfoot
|
|
38
|
+
data-slot="table-footer"
|
|
39
|
+
className={cn('border-t bg-muted/50 font-medium [&>tr]:last:border-b-0', className)}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function TableRow({ className, ...props }: React.ComponentProps<'tr'>) {
|
|
46
|
+
return (
|
|
47
|
+
<tr
|
|
48
|
+
data-slot="table-row"
|
|
49
|
+
className={cn(
|
|
50
|
+
'border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted',
|
|
51
|
+
className,
|
|
52
|
+
)}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function TableHead({ className, ...props }: React.ComponentProps<'th'>) {
|
|
59
|
+
return (
|
|
60
|
+
<th
|
|
61
|
+
data-slot="table-head"
|
|
62
|
+
className={cn(
|
|
63
|
+
'h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0',
|
|
64
|
+
className,
|
|
65
|
+
)}
|
|
66
|
+
{...props}
|
|
67
|
+
/>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function TableCell({ className, ...props }: React.ComponentProps<'td'>) {
|
|
72
|
+
return (
|
|
73
|
+
<td
|
|
74
|
+
data-slot="table-cell"
|
|
75
|
+
className={cn('p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0', className)}
|
|
76
|
+
{...props}
|
|
77
|
+
/>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function TableCaption({ className, ...props }: React.ComponentProps<'caption'>) {
|
|
82
|
+
return (
|
|
83
|
+
<caption
|
|
84
|
+
data-slot="table-caption"
|
|
85
|
+
className={cn('mt-4 text-sm text-muted-foreground', className)}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Tabs as TabsPrimitive } from '@base-ui/react/tabs'
|
|
2
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
3
|
+
import { cn } from '../lib/utils'
|
|
4
|
+
|
|
5
|
+
function Tabs({ className, orientation = 'horizontal', ...props }: TabsPrimitive.Root.Props) {
|
|
6
|
+
return (
|
|
7
|
+
<TabsPrimitive.Root
|
|
8
|
+
data-slot="tabs"
|
|
9
|
+
data-orientation={orientation}
|
|
10
|
+
className={cn('group/tabs flex gap-2 data-[orientation=horizontal]:flex-col', className)}
|
|
11
|
+
{...props}
|
|
12
|
+
/>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const tabsListVariants = cva(
|
|
17
|
+
'group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-8 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none',
|
|
18
|
+
{
|
|
19
|
+
variants: {
|
|
20
|
+
variant: {
|
|
21
|
+
default: 'bg-muted',
|
|
22
|
+
line: 'gap-1 bg-transparent',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
defaultVariants: {
|
|
26
|
+
variant: 'default',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
function TabsList({
|
|
32
|
+
className,
|
|
33
|
+
variant = 'default',
|
|
34
|
+
...props
|
|
35
|
+
}: TabsPrimitive.List.Props & VariantProps<typeof tabsListVariants>) {
|
|
36
|
+
return (
|
|
37
|
+
<TabsPrimitive.List
|
|
38
|
+
data-slot="tabs-list"
|
|
39
|
+
data-variant={variant}
|
|
40
|
+
className={cn(tabsListVariants({ variant }), className)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
|
|
47
|
+
return (
|
|
48
|
+
<TabsPrimitive.Tab
|
|
49
|
+
data-slot="tabs-trigger"
|
|
50
|
+
className={cn(
|
|
51
|
+
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-[color,background-color,box-shadow,transform] duration-200 ease-out hover:-translate-y-px hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
52
|
+
'group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent',
|
|
53
|
+
'data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground',
|
|
54
|
+
'after:absolute after:bg-foreground after:opacity-0 after:transition-[opacity,transform] after:duration-200 after:ease-out group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100',
|
|
55
|
+
className,
|
|
56
|
+
)}
|
|
57
|
+
{...props}
|
|
58
|
+
/>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {
|
|
63
|
+
return (
|
|
64
|
+
<TabsPrimitive.Panel
|
|
65
|
+
data-slot="tabs-content"
|
|
66
|
+
className={cn('flex-1 text-sm outline-none', className)}
|
|
67
|
+
{...props}
|
|
68
|
+
/>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { cn } from '../lib/utils'
|
|
3
|
+
|
|
4
|
+
function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {
|
|
5
|
+
return (
|
|
6
|
+
<textarea
|
|
7
|
+
data-slot="textarea"
|
|
8
|
+
className={cn(
|
|
9
|
+
'flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40',
|
|
10
|
+
className,
|
|
11
|
+
)}
|
|
12
|
+
{...props}
|
|
13
|
+
/>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { Textarea }
|