@iamfiro/design 0.0.0-stage → 0.3.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/AGENTS.md +82 -0
- package/README.md +15 -2
- package/agents/INDEX.md +157 -0
- package/agents/accordion/AGENTS.md +279 -0
- package/agents/action-bar/AGENTS.md +90 -0
- package/agents/alert/AGENTS.md +212 -0
- package/agents/alert-dialog/AGENTS.md +193 -0
- package/agents/app-shell/AGENTS.md +409 -0
- package/agents/area-chart/AGENTS.md +183 -0
- package/agents/aspect-ratio/AGENTS.md +143 -0
- package/agents/avatar/AGENTS.md +130 -0
- package/agents/badge/AGENTS.md +217 -0
- package/agents/bar-chart/AGENTS.md +386 -0
- package/agents/bar-list/AGENTS.md +223 -0
- package/agents/brand-icon/AGENTS.md +82 -0
- package/agents/breadcrumb/AGENTS.md +133 -0
- package/agents/button/AGENTS.md +195 -0
- package/agents/card/AGENTS.md +141 -0
- package/agents/card-frame/AGENTS.md +227 -0
- package/agents/chat-bubble/AGENTS.md +266 -0
- package/agents/chat-composer/AGENTS.md +251 -0
- package/agents/checkbox/AGENTS.md +113 -0
- package/agents/code-block/AGENTS.md +144 -0
- package/agents/color-dot/AGENTS.md +120 -0
- package/agents/color-picker/AGENTS.md +224 -0
- package/agents/colors/AGENTS.md +168 -0
- package/agents/combobox/AGENTS.md +218 -0
- package/agents/command-menu/AGENTS.md +169 -0
- package/agents/comment/AGENTS.md +206 -0
- package/agents/context-menu/AGENTS.md +303 -0
- package/agents/copy-button/AGENTS.md +84 -0
- package/agents/count-badge/AGENTS.md +115 -0
- package/agents/countdown/AGENTS.md +101 -0
- package/agents/data-grid/AGENTS.md +460 -0
- package/agents/date-picker/AGENTS.md +197 -0
- package/agents/delta/AGENTS.md +114 -0
- package/agents/description-list/AGENTS.md +251 -0
- package/agents/dialog/AGENTS.md +391 -0
- package/agents/drawer/AGENTS.md +471 -0
- package/agents/dropdown-menu/AGENTS.md +322 -0
- package/agents/dropzone/AGENTS.md +116 -0
- package/agents/elapsed/AGENTS.md +160 -0
- package/agents/empty-state/AGENTS.md +203 -0
- package/agents/file-chip/AGENTS.md +79 -0
- package/agents/file-icon/AGENTS.md +66 -0
- package/agents/file-upload/AGENTS.md +168 -0
- package/agents/filter-chips/AGENTS.md +167 -0
- package/agents/flag/AGENTS.md +67 -0
- package/agents/funnel-chart/AGENTS.md +131 -0
- package/agents/highlight/AGENTS.md +119 -0
- package/agents/hover-card/AGENTS.md +271 -0
- package/agents/icon-stack/AGENTS.md +120 -0
- package/agents/icon-tile/AGENTS.md +94 -0
- package/agents/inline-diff/AGENTS.md +211 -0
- package/agents/input/AGENTS.md +261 -0
- package/agents/kanban/AGENTS.md +352 -0
- package/agents/kbd/AGENTS.md +160 -0
- package/agents/layout/AGENTS.md +351 -0
- package/agents/line-chart/AGENTS.md +393 -0
- package/agents/link/AGENTS.md +53 -0
- package/agents/list/AGENTS.md +392 -0
- package/agents/marker/AGENTS.md +142 -0
- package/agents/masked-text/AGENTS.md +127 -0
- package/agents/menubar/AGENTS.md +276 -0
- package/agents/multi-select/AGENTS.md +159 -0
- package/agents/navigation-menu/AGENTS.md +315 -0
- package/agents/notice-stack/AGENTS.md +101 -0
- package/agents/number-stepper/AGENTS.md +96 -0
- package/agents/otp-input/AGENTS.md +162 -0
- package/agents/pagination/AGENTS.md +189 -0
- package/agents/phone-input/AGENTS.md +131 -0
- package/agents/pie-chart/AGENTS.md +243 -0
- package/agents/popover/AGENTS.md +190 -0
- package/agents/progress/AGENTS.md +273 -0
- package/agents/progress-tracker/AGENTS.md +305 -0
- package/agents/rating/AGENTS.md +165 -0
- package/agents/relative-time/AGENTS.md +103 -0
- package/agents/resizable/AGENTS.md +390 -0
- package/agents/scaled-preview/AGENTS.md +111 -0
- package/agents/scatter-chart/AGENTS.md +160 -0
- package/agents/scroll-area/AGENTS.md +271 -0
- package/agents/search-input/AGENTS.md +105 -0
- package/agents/segmented-control/AGENTS.md +153 -0
- package/agents/select/AGENTS.md +159 -0
- package/agents/selector-button/AGENTS.md +122 -0
- package/agents/sidebar/AGENTS.md +201 -0
- package/agents/skeleton/AGENTS.md +166 -0
- package/agents/slider/AGENTS.md +181 -0
- package/agents/social-login-button/AGENTS.md +156 -0
- package/agents/spark-chart/AGENTS.md +257 -0
- package/agents/sparkbar/AGENTS.md +92 -0
- package/agents/spinner/AGENTS.md +123 -0
- package/agents/split-view/AGENTS.md +290 -0
- package/agents/stat/AGENTS.md +138 -0
- package/agents/status-icon/AGENTS.md +243 -0
- package/agents/step-list/AGENTS.md +313 -0
- package/agents/table/AGENTS.md +286 -0
- package/agents/tabs/AGENTS.md +305 -0
- package/agents/tag-input/AGENTS.md +246 -0
- package/agents/time-picker/AGENTS.md +228 -0
- package/agents/timeline/AGENTS.md +1018 -0
- package/agents/toast/AGENTS.md +266 -0
- package/agents/toggle-group/AGENTS.md +216 -0
- package/agents/toolbar/AGENTS.md +277 -0
- package/agents/tooltip/AGENTS.md +154 -0
- package/agents/tracker/AGENTS.md +158 -0
- package/agents/tree-diagram/AGENTS.md +260 -0
- package/agents/tree-view/AGENTS.md +329 -0
- package/agents/typography/AGENTS.md +396 -0
- package/lib/components/Accordion/Accordion2.js +135 -0
- package/lib/components/ActionBar/ActionBar2.js +65 -0
- package/lib/components/Alert/Alert2.js +131 -0
- package/lib/components/AppShell/AppShell2.js +94 -0
- package/lib/components/AreaChart/AreaChart.js +59 -0
- package/lib/components/BarChart/BarChart2.js +243 -0
- package/lib/components/BarList/BarList2.js +172 -0
- package/lib/components/Breadcrumb/Breadcrumb2.js +87 -0
- package/lib/components/Calendar/Calendar2.js +309 -0
- package/lib/components/Calendar/DatePicker2.js +229 -0
- package/lib/components/Calendar/date.js +121 -0
- package/lib/components/Card/Card2.js +104 -0
- package/lib/components/CardFrame/CardFrame2.js +72 -0
- package/lib/components/Chart/ChartParts.js +150 -0
- package/lib/components/Chart/chart2.js +96 -0
- package/lib/components/Chart/useChartInteraction.js +94 -0
- package/lib/components/ChatBubble/ChatBubble2.js +174 -0
- package/lib/components/ChatBubble/formatChatTime.js +53 -0
- package/lib/components/ChatComposer/ChatComposer2.js +153 -0
- package/lib/components/CodeBlock/CodeBlock2.js +93 -0
- package/lib/components/CodeBlock/highlight.js +114 -0
- package/lib/components/ColorPicker/ColorPicker2.js +610 -0
- package/lib/components/ColorPicker/color.js +180 -0
- package/lib/components/ColorPicker/presets.js +75 -0
- package/lib/components/Combobox/Combobox2.js +229 -0
- package/lib/components/CommandMenu/CommandMenu.js +267 -0
- package/lib/components/Comment/Comment2.js +134 -0
- package/lib/components/ContextMenu/ContextMenu2.js +168 -0
- package/lib/components/DataGrid/DataGrid2.js +1996 -0
- package/lib/components/DataGrid/GridPopover.js +154 -0
- package/lib/components/DataGrid/anchor.js +50 -0
- package/lib/components/DataGrid/model.js +558 -0
- package/lib/components/DataGrid/panels.js +703 -0
- package/lib/components/DataGrid/typeMeta.js +76 -0
- package/lib/components/DataGrid/types.js +54 -0
- package/lib/components/DescriptionList/DescriptionList2.js +180 -0
- package/lib/components/Dialog/AlertDialog.js +118 -0
- package/lib/components/Dialog/Dialog2.js +109 -0
- package/lib/components/Dialog/modal.js +92 -0
- package/lib/components/Drawer/Drawer2.js +244 -0
- package/lib/components/DropdownMenu/DropdownMenu2.js +394 -0
- package/lib/components/DropdownMenu/menuCore.js +63 -0
- package/lib/components/Dropzone/Dropzone2.js +144 -0
- package/lib/components/EmptyState/EmptyState2.js +72 -0
- package/lib/components/FileChip/FileChip2.js +118 -0
- package/lib/components/FileUpload/FileUpload.js +258 -0
- package/lib/components/FilterChips/FilterChips2.js +101 -0
- package/lib/components/FunnelChart/FunnelChart2.js +283 -0
- package/lib/components/FunnelChart/funnel.js +69 -0
- package/lib/components/Highlight/Highlight2.js +100 -0
- package/lib/components/HoverCard/HoverCard.js +134 -0
- package/lib/components/InlineDiff/InlineDiff2.js +215 -0
- package/lib/components/InlineDiff/diff.js +126 -0
- package/lib/components/InlineDiff/useDiffReview.js +109 -0
- package/lib/components/Kanban/Kanban2.js +691 -0
- package/lib/components/Kanban/KanbanCard.js +159 -0
- package/lib/components/Kanban/move.js +69 -0
- package/lib/components/LineChart/LineChart2.js +342 -0
- package/lib/components/List/List2.js +133 -0
- package/lib/components/Listbox/Listbox2.js +163 -0
- package/lib/components/Marker/Marker2.js +82 -0
- package/lib/components/Menubar/Menubar2.js +223 -0
- package/lib/components/MultiSelect/MultiSelect2.js +204 -0
- package/lib/components/NavigationMenu/NavigationMenu2.js +481 -0
- package/lib/components/NoticeStack/NoticeStack2.js +178 -0
- package/lib/components/NumberStepper/NumberStepper2.js +129 -0
- package/lib/components/OtpInput/OtpInput2.js +122 -0
- package/lib/components/Pagination/Pagination2.js +210 -0
- package/lib/components/Pagination/paginationRange.js +53 -0
- package/lib/components/PhoneInput/PhoneInput.js +175 -0
- package/lib/components/PieChart/PieChart2.js +246 -0
- package/lib/components/PieChart/arc.js +66 -0
- package/lib/components/PieChart/useArcTween.js +94 -0
- package/lib/components/Popover/Popover2.js +112 -0
- package/lib/components/ProgressTracker/ProgressTracker2.js +226 -0
- package/lib/components/Rating/Rating2.js +186 -0
- package/lib/components/Resizable/Resizable2.js +236 -0
- package/lib/components/Resizable/layout.js +115 -0
- package/lib/components/ScaledPreview/ScaledPreview2.js +100 -0
- package/lib/components/ScatterChart/ScatterChart2.js +356 -0
- package/lib/components/ScatterChart/scatter.js +83 -0
- package/lib/components/ScrollArea/ScrollArea2.js +218 -0
- package/lib/components/Select/Select2.js +153 -0
- package/lib/components/Sidebar/Sidebar2.js +127 -0
- package/lib/components/Skeleton/Skeleton2.js +91 -0
- package/lib/components/Slider/Slider2.js +142 -0
- package/lib/components/SocialLoginButton/SocialLoginButton.js +85 -0
- package/lib/components/SparkChart/SparkChart2.js +217 -0
- package/lib/components/SparkChart/spark.js +76 -0
- package/lib/components/Sparkbar/Sparkbar2.js +111 -0
- package/lib/components/SplitView/SplitView2.js +95 -0
- package/lib/components/Stat/Stat2.js +164 -0
- package/lib/components/StepList/StepList2.js +131 -0
- package/lib/components/Table/Table2.js +104 -0
- package/lib/components/Tabs/Tabs2.js +131 -0
- package/lib/components/TagInput/TagInput2.js +360 -0
- package/lib/components/TimePicker/TimePicker2.js +388 -0
- package/lib/components/TimePicker/time.js +83 -0
- package/lib/components/Timeline/Timeline2.js +369 -0
- package/lib/components/Timeline/scale.js +83 -0
- package/lib/components/Toast/Toaster.js +212 -0
- package/lib/components/Toast/store.js +129 -0
- package/lib/components/ToggleGroup/ToggleGroup2.js +125 -0
- package/lib/components/Toolbar/Toolbar2.js +281 -0
- package/lib/components/Tracker/Tracker2.js +172 -0
- package/lib/components/TreeDiagram/TreeDiagram2.js +223 -0
- package/lib/components/TreeView/TreeView2.js +284 -0
- package/lib/hooks/timing.js +55 -0
- package/lib/hooks/useControllableState.js +49 -0
- package/lib/hooks/useCountdown.js +80 -0
- package/lib/hooks/useDigitInput.js +105 -0
- package/lib/hooks/useDismiss.js +71 -0
- package/lib/hooks/useElapsed.js +83 -0
- package/lib/hooks/useFloating.js +70 -0
- package/lib/hooks/useFocusTrap.js +61 -0
- package/lib/hooks/usePresence.js +60 -0
- package/lib/hooks/usePressToOpen.js +69 -0
- package/lib/hooks/useScrollLock.js +70 -0
- package/lib/hooks/useSelection.js +68 -0
- package/lib/index.js +178 -0
- package/lib/primitives/AspectRatio2.js +80 -0
- package/lib/primitives/BrandIcon.js +245 -0
- package/lib/primitives/ColorDot2.js +70 -0
- package/lib/primitives/CopyButton2.js +85 -0
- package/lib/primitives/CountBadge2.js +78 -0
- package/lib/primitives/Countdown2.js +65 -0
- package/lib/primitives/Delta2.js +67 -0
- package/lib/primitives/Elapsed.js +62 -0
- package/lib/primitives/FileIcon2.js +125 -0
- package/lib/primitives/Flag2.js +263 -0
- package/lib/primitives/IconStack2.js +85 -0
- package/lib/primitives/IconTile2.js +68 -0
- package/lib/primitives/Kbd2.js +88 -0
- package/lib/primitives/Layout2.js +118 -0
- package/lib/primitives/Link2.js +53 -0
- package/lib/primitives/MaskedText2.js +120 -0
- package/lib/primitives/Progress2.js +137 -0
- package/lib/primitives/RelativeTime.js +84 -0
- package/lib/primitives/SearchInput2.js +84 -0
- package/lib/primitives/SelectorButton2.js +97 -0
- package/lib/primitives/Spinner2.js +98 -0
- package/lib/primitives/StatusIcon2.js +126 -0
- package/lib/primitives/Swatch2.js +79 -0
- package/lib/primitives/Tooltip2.js +132 -0
- package/lib/primitives/Typography2.js +97 -0
- package/lib/primitives/formatCountdown.js +51 -0
- package/lib/primitives/formatElapsed.js +66 -0
- package/lib/primitives/formatRelativeTime.js +64 -0
- package/lib/primitives/hotkey.js +94 -0
- package/lib/primitives/index.js +352 -0
- package/lib/primitives/maskValue.js +65 -0
- package/lib/style.css +2 -0
- package/lib/types/components/Accordion/Accordion.d.ts +57 -0
- package/lib/types/components/ActionBar/ActionBar.d.ts +24 -0
- package/lib/types/components/Alert/Alert.d.ts +36 -0
- package/lib/types/components/AppShell/AppShell.d.ts +38 -0
- package/lib/types/components/AreaChart/AreaChart.d.ts +17 -0
- package/lib/types/components/BarChart/BarChart.d.ts +57 -0
- package/lib/types/components/BarList/BarList.d.ts +56 -0
- package/lib/types/components/Breadcrumb/Breadcrumb.d.ts +15 -0
- package/lib/types/components/Calendar/Calendar.d.ts +34 -0
- package/lib/types/components/Calendar/DatePicker.d.ts +45 -0
- package/lib/types/components/Calendar/date.d.ts +32 -0
- package/lib/types/components/Card/Card.d.ts +64 -0
- package/lib/types/components/CardFrame/CardFrame.d.ts +15 -0
- package/lib/types/components/Chart/ChartParts.d.ts +38 -0
- package/lib/types/components/Chart/chart.d.ts +46 -0
- package/lib/types/components/Chart/useChartInteraction.d.ts +39 -0
- package/lib/types/components/ChatBubble/ChatBubble.d.ts +61 -0
- package/lib/types/components/ChatBubble/formatChatTime.d.ts +2 -0
- package/lib/types/components/ChatComposer/ChatComposer.d.ts +42 -0
- package/lib/types/components/CodeBlock/CodeBlock.d.ts +18 -0
- package/lib/types/components/CodeBlock/highlight.d.ts +10 -0
- package/lib/types/components/ColorPicker/ColorPicker.d.ts +61 -0
- package/lib/types/components/ColorPicker/color.d.ts +48 -0
- package/lib/types/components/ColorPicker/presets.d.ts +7 -0
- package/lib/types/components/Combobox/Combobox.d.ts +48 -0
- package/lib/types/components/CommandMenu/CommandMenu.d.ts +51 -0
- package/lib/types/components/Comment/Comment.d.ts +45 -0
- package/lib/types/components/ContextMenu/ContextMenu.d.ts +40 -0
- package/lib/types/components/DataGrid/DataGrid.d.ts +43 -0
- package/lib/types/components/DataGrid/GridPopover.d.ts +43 -0
- package/lib/types/components/DataGrid/anchor.d.ts +5 -0
- package/lib/types/components/DataGrid/model.d.ts +61 -0
- package/lib/types/components/DataGrid/panels.d.ts +63 -0
- package/lib/types/components/DataGrid/typeMeta.d.ts +6 -0
- package/lib/types/components/DataGrid/types.d.ts +77 -0
- package/lib/types/components/DescriptionList/DescriptionList.d.ts +77 -0
- package/lib/types/components/Dialog/AlertDialog.d.ts +26 -0
- package/lib/types/components/Dialog/Dialog.d.ts +42 -0
- package/lib/types/components/Dialog/modal.d.ts +38 -0
- package/lib/types/components/Drawer/Drawer.d.ts +62 -0
- package/lib/types/components/DropdownMenu/DropdownMenu.d.ts +97 -0
- package/lib/types/components/DropdownMenu/menuCore.d.ts +23 -0
- package/lib/types/components/Dropzone/Dropzone.d.ts +30 -0
- package/lib/types/components/EmptyState/EmptyState.d.ts +26 -0
- package/lib/types/components/FileChip/FileChip.d.ts +32 -0
- package/lib/types/components/FileUpload/FileUpload.d.ts +52 -0
- package/lib/types/components/FilterChips/FilterChips.d.ts +37 -0
- package/lib/types/components/FunnelChart/FunnelChart.d.ts +38 -0
- package/lib/types/components/FunnelChart/funnel.d.ts +25 -0
- package/lib/types/components/Highlight/Highlight.d.ts +35 -0
- package/lib/types/components/HoverCard/HoverCard.d.ts +47 -0
- package/lib/types/components/InlineDiff/InlineDiff.d.ts +36 -0
- package/lib/types/components/InlineDiff/diff.d.ts +42 -0
- package/lib/types/components/InlineDiff/useDiffReview.d.ts +57 -0
- package/lib/types/components/Kanban/Kanban.d.ts +64 -0
- package/lib/types/components/Kanban/KanbanCard.d.ts +35 -0
- package/lib/types/components/Kanban/move.d.ts +25 -0
- package/lib/types/components/LineChart/LineChart.d.ts +85 -0
- package/lib/types/components/List/List.d.ts +62 -0
- package/lib/types/components/Listbox/Listbox.d.ts +60 -0
- package/lib/types/components/Marker/Marker.d.ts +28 -0
- package/lib/types/components/Menubar/Menubar.d.ts +50 -0
- package/lib/types/components/MultiSelect/MultiSelect.d.ts +38 -0
- package/lib/types/components/NavigationMenu/NavigationMenu.d.ts +101 -0
- package/lib/types/components/NoticeStack/NoticeStack.d.ts +41 -0
- package/lib/types/components/NumberStepper/NumberStepper.d.ts +32 -0
- package/lib/types/components/OtpInput/OtpInput.d.ts +31 -0
- package/lib/types/components/Pagination/Pagination.d.ts +54 -0
- package/lib/types/components/Pagination/paginationRange.d.ts +7 -0
- package/lib/types/components/PhoneInput/PhoneInput.d.ts +47 -0
- package/lib/types/components/PieChart/PieChart.d.ts +67 -0
- package/lib/types/components/PieChart/arc.d.ts +26 -0
- package/lib/types/components/PieChart/useArcTween.d.ts +15 -0
- package/lib/types/components/Popover/Popover.d.ts +28 -0
- package/lib/types/components/ProgressTracker/ProgressTracker.d.ts +64 -0
- package/lib/types/components/Rating/Rating.d.ts +42 -0
- package/lib/types/components/Resizable/Resizable.d.ts +50 -0
- package/lib/types/components/Resizable/layout.d.ts +31 -0
- package/lib/types/components/ScaledPreview/ScaledPreview.d.ts +38 -0
- package/lib/types/components/ScatterChart/ScatterChart.d.ts +81 -0
- package/lib/types/components/ScatterChart/scatter.d.ts +14 -0
- package/lib/types/components/ScrollArea/ScrollArea.d.ts +34 -0
- package/lib/types/components/Select/Select.d.ts +39 -0
- package/lib/types/components/Sidebar/Sidebar.d.ts +39 -0
- package/lib/types/components/Skeleton/Skeleton.d.ts +47 -0
- package/lib/types/components/Slider/Slider.d.ts +36 -0
- package/lib/types/components/SocialLoginButton/SocialLoginButton.d.ts +10 -0
- package/lib/types/components/SparkChart/SparkChart.d.ts +55 -0
- package/lib/types/components/SparkChart/spark.d.ts +16 -0
- package/lib/types/components/Sparkbar/Sparkbar.d.ts +39 -0
- package/lib/types/components/SplitView/SplitView.d.ts +31 -0
- package/lib/types/components/Stat/Stat.d.ts +58 -0
- package/lib/types/components/StepList/StepList.d.ts +61 -0
- package/lib/types/components/Table/Table.d.ts +39 -0
- package/lib/types/components/Tabs/Tabs.d.ts +47 -0
- package/lib/types/components/TagInput/TagInput.d.ts +46 -0
- package/lib/types/components/TimePicker/TimePicker.d.ts +43 -0
- package/lib/types/components/TimePicker/time.d.ts +15 -0
- package/lib/types/components/Timeline/Timeline.d.ts +154 -0
- package/lib/types/components/Timeline/scale.d.ts +22 -0
- package/lib/types/components/Toast/Toaster.d.ts +19 -0
- package/lib/types/components/Toast/index.d.ts +4 -0
- package/lib/types/components/Toast/store.d.ts +75 -0
- package/lib/types/components/ToggleGroup/ToggleGroup.d.ts +28 -0
- package/lib/types/components/Toolbar/Toolbar.d.ts +70 -0
- package/lib/types/components/Tracker/Tracker.d.ts +43 -0
- package/lib/types/components/TreeDiagram/TreeDiagram.d.ts +68 -0
- package/lib/types/components/TreeView/TreeView.d.ts +107 -0
- package/lib/types/components/index.d.ts +188 -0
- package/lib/types/hooks/index.d.ts +17 -0
- package/lib/types/hooks/timing.d.ts +29 -0
- package/lib/types/hooks/useControllableState.d.ts +5 -0
- package/lib/types/hooks/useCountdown.d.ts +19 -0
- package/lib/types/hooks/useDigitInput.d.ts +24 -0
- package/lib/types/hooks/useDismiss.d.ts +3 -0
- package/lib/types/hooks/useElapsed.d.ts +28 -0
- package/lib/types/hooks/useFloating.d.ts +21 -0
- package/lib/types/hooks/useFocusTrap.d.ts +7 -0
- package/lib/types/hooks/usePresence.d.ts +8 -0
- package/lib/types/hooks/usePressToOpen.d.ts +20 -0
- package/lib/types/hooks/useScrollLock.d.ts +2 -0
- package/lib/types/hooks/useSelection.d.ts +29 -0
- package/lib/types/index.d.ts +5 -0
- package/lib/types/primitives/AspectRatio.d.ts +14 -0
- package/lib/types/primitives/BrandIcon.d.ts +12 -0
- package/lib/types/primitives/ColorDot.d.ts +20 -0
- package/lib/types/primitives/CopyButton.d.ts +12 -0
- package/lib/types/primitives/CountBadge.d.ts +28 -0
- package/lib/types/primitives/Countdown.d.ts +21 -0
- package/lib/types/primitives/Delta.d.ts +33 -0
- package/lib/types/primitives/Elapsed.d.ts +23 -0
- package/lib/types/primitives/FileIcon.d.ts +17 -0
- package/lib/types/primitives/Flag.d.ts +15 -0
- package/lib/types/primitives/IconStack.d.ts +27 -0
- package/lib/types/primitives/IconTile.d.ts +24 -0
- package/lib/types/primitives/Kbd.d.ts +19 -0
- package/lib/types/primitives/Layout.d.ts +64 -0
- package/lib/types/primitives/Link.d.ts +12 -0
- package/lib/types/primitives/MaskedText.d.ts +28 -0
- package/lib/types/primitives/Progress.d.ts +52 -0
- package/lib/types/primitives/RelativeTime.d.ts +22 -0
- package/lib/types/primitives/SearchInput.d.ts +8 -0
- package/lib/types/primitives/SelectorButton.d.ts +23 -0
- package/lib/types/primitives/Spinner.d.ts +15 -0
- package/lib/types/primitives/StatusIcon.d.ts +40 -0
- package/lib/types/primitives/Swatch.d.ts +11 -0
- package/lib/types/primitives/Tooltip.d.ts +17 -0
- package/lib/types/primitives/Typography.d.ts +48 -0
- package/lib/types/primitives/formatCountdown.d.ts +2 -0
- package/lib/types/primitives/formatElapsed.d.ts +8 -0
- package/lib/types/primitives/formatRelativeTime.d.ts +16 -0
- package/lib/types/primitives/hotkey.d.ts +10 -0
- package/lib/types/primitives/index.d.ts +188 -0
- package/lib/types/primitives/maskValue.d.ts +24 -0
- package/package.json +55 -4
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
# DataGrid
|
|
2
|
+
|
|
3
|
+
> 데이터 표시 · `@iamfiro/design` · 직접 만들지 말고 이 컴포넌트를 쓴다.
|
|
4
|
+
|
|
5
|
+
Notion 데이터베이스처럼 타입이 있는 속성과 보기(정렬·필터·그룹·검색)를, Google Sheets·Numbers처럼 범위 선택·키보드 편집·복사/붙여넣기·채우기 핸들을 갖춘 표. 열은 끌어서 순서·너비를 바꾸고 고정·숨기기·타입 변경까지 머리글 메뉴에서 할 수 있어요. 모든 데이터 변경은 실행 취소(⌘/Ctrl+Z)할 수 있고, 행이 많으면 보이는 줄만 그려요. 단순히 보여 주기만 하는 표는 Table을 쓰세요.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { DataGrid, type GridColumn, type GridRow } from '@iamfiro/design'
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 디자인 규칙
|
|
12
|
+
|
|
13
|
+
- 표: 보여 주기만 하면 `Table`, 편집·정렬·필터·범위 선택이 필요하면 `DataGrid`. 셀 편집·키 입력·클립보드·한글 IME는 셀 위에 놓인 textarea 하나가 모두 받는다(조합 중에 포커스를 옮기면 한글이 깨진다).
|
|
14
|
+
|
|
15
|
+
## API
|
|
16
|
+
|
|
17
|
+
### DataGrid
|
|
18
|
+
|
|
19
|
+
| prop | 타입 | 기본값 | 설명 |
|
|
20
|
+
| --- | --- | --- | --- |
|
|
21
|
+
| `rows / defaultRows / onRowsChange` | `R[] / R[] / (rows) => void` | | 행 데이터(제어/비제어). 행마다 고유한 id가 있어야 해요. |
|
|
22
|
+
| `columns / defaultColumns / onColumnsChange` | `GridColumn<R>[]` | | 열 정의. 너비·순서·고정·숨김·타입 변경이 여기에 반영돼요. |
|
|
23
|
+
| `view / defaultView / onViewChange` | `GridView / Partial<GridView>` | | 정렬·필터·그룹·검색·행 높이 같은 보기 상태. |
|
|
24
|
+
| `selectedRowIds / onSelectedRowIdsChange` | `string[]` | | 체크된 행(일괄 작업 대상). |
|
|
25
|
+
| `createRow` | `(values) => R` | | 새 행을 만들 때. values에는 현재 필터·그룹에 맞는 값이 들어 있어요. |
|
|
26
|
+
| `onCellEdit` | `({ row, column, value, previous }) => void` | | 셀이 확정될 때마다(입력·붙여넣기·채우기·지우기). |
|
|
27
|
+
| `readOnly` | `boolean` | `false` | 데이터 변경 금지. 보기 조작과 복사는 돼요. |
|
|
28
|
+
| `columnEditing` | `boolean` | `true` | 사용자가 속성을 추가·이름 변경·타입 변경·삭제할 수 있는지. |
|
|
29
|
+
| `toolbar` | `boolean` | `true` | 검색·필터·정렬·그룹·속성·보기 설정·실행 취소·새로 만들기 막대. |
|
|
30
|
+
| `title / actions` | `ReactNode` | | 툴바 왼쪽 제목 / 오른쪽 끝에 붙일 버튼. |
|
|
31
|
+
| `footer` | `boolean` | `true` | 열마다 계산을 고르는 아래 줄. |
|
|
32
|
+
| `height` | `number \| string` | `520` | 스크롤 영역 최대 높이. |
|
|
33
|
+
| `emptyState` | `ReactNode` | | 보일 행이 없을 때 대신 보여 줄 내용. |
|
|
34
|
+
|
|
35
|
+
### GridColumn
|
|
36
|
+
|
|
37
|
+
| prop | 타입 | 기본값 | 설명 |
|
|
38
|
+
| --- | --- | --- | --- |
|
|
39
|
+
| `key / name` (필수) | `string` | | 값을 읽고 쓰는 필드 / 머리글 이름. |
|
|
40
|
+
| `type` | `'text' \| 'number' \| 'select' \| 'multiSelect' \| 'checkbox' \| 'date' \| 'url'` | `'text'` | 표시·편집·정렬·필터·붙여넣기 해석이 타입을 따라요. |
|
|
41
|
+
| `width / minWidth` | `number` | `180 / 64` | 열 너비(px). 경계를 더블클릭하면 내용에 맞춰요. |
|
|
42
|
+
| `pinned / hidden / wrap` | `boolean` | | 왼쪽 고정 / 숨김 / 줄바꿈(켜면 행 가상화가 꺼져요). |
|
|
43
|
+
| `options / creatable` | `GridOption[] / boolean` | | 선택지({ label, tone }). 없는 값을 입력하면 새로 만들어요(creatable: false면 거절). |
|
|
44
|
+
| `format / decimals` | `'plain' \| 'comma' \| 'percent' \| 'won' \| 'usd'` | `'comma'` | 숫자 표시 형식. |
|
|
45
|
+
| `aggregate` | `GridAggregate` | | 아래 줄 계산('sum', 'avg', 'percentChecked', 'earliest' …). |
|
|
46
|
+
| `readOnly / locked` | `boolean` | | 셀 편집 금지 / 속성 삭제·타입 변경 금지. |
|
|
47
|
+
| `align` | `'start' \| 'center' \| 'end'` | | 정렬. 숫자는 기본이 오른쪽. |
|
|
48
|
+
| `render` | `(value, row) => ReactNode` | | 셀 모양을 직접 그릴 때. |
|
|
49
|
+
| `validate` | `(value, row) => string \| null` | | 메시지를 돌려주면 그 편집을 거절해요. |
|
|
50
|
+
|
|
51
|
+
### 키보드
|
|
52
|
+
|
|
53
|
+
| prop | 타입 | 기본값 | 설명 |
|
|
54
|
+
| --- | --- | --- | --- |
|
|
55
|
+
| `↑ ↓ ← → · Tab` | `이동` | | Shift를 같이 누르면 범위 확장, ⌘/Ctrl은 끝까지. |
|
|
56
|
+
| `Enter · F2 · 더블클릭` | `편집` | | 바로 글자를 쳐도 편집이 시작돼요(한글 입력 포함). Enter 확정 후 아래로, Tab은 오른쪽, Esc 취소. |
|
|
57
|
+
| `Shift+Enter · Alt+Enter` | `줄바꿈` | | 텍스트 셀 안에서 새 줄. |
|
|
58
|
+
| `Delete · Backspace` | `지우기` | | 선택 범위의 값을 지워요. |
|
|
59
|
+
| `Space` | `체크` | | 체크박스 열에서 범위를 한꺼번에 토글. |
|
|
60
|
+
| `⌘/Ctrl + C · X · V` | `클립보드` | | TSV로 주고받아 Sheets·Excel·Numbers와 호환돼요. |
|
|
61
|
+
| `⌘/Ctrl + D` | `아래로 채우기` | | 범위의 첫 행 값을 아래로 복사(숫자는 이어서). |
|
|
62
|
+
| `⌘/Ctrl + A · Z · Shift+Z` | `전체 선택 · 실행 취소 · 다시 실행` | | |
|
|
63
|
+
| `Esc (끄는 중)` | `취소` | | 열 너비·순서, 행 순서, 채우기를 끄는 도중 Esc면 원래대로. |
|
|
64
|
+
|
|
65
|
+
## 예제
|
|
66
|
+
|
|
67
|
+
예제를 감싼 `style={{ ... }}` div는 미리보기 배치용이다. 실제 화면 배치는 `Stack` · `Inline` · `Grid`로 한다.
|
|
68
|
+
|
|
69
|
+
### 데이터베이스 (Notion)
|
|
70
|
+
|
|
71
|
+
텍스트·숫자·선택·다중 선택·날짜·체크박스·URL 속성. 머리글의 ⌄ 메뉴(또는 우클릭)에서 이름·타입·정렬·필터·그룹·고정·줄바꿈·삽입·삭제를 하고, 맨 오른쪽 +로 속성을 추가해요. 행 왼쪽 손잡이로 순서를 바꾸고, 체크하면 아래에 일괄 작업 막대가 떠요. 맨 아래 칸을 누르면 합계·평균·체크 비율 같은 계산을 고를 수 있어요.
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
import { DataGrid, type GridColumn, type GridRow } from '@iamfiro/design'
|
|
75
|
+
|
|
76
|
+
interface Task extends GridRow {
|
|
77
|
+
title: string
|
|
78
|
+
status: string | null
|
|
79
|
+
owner: string | null
|
|
80
|
+
tags: string[]
|
|
81
|
+
due: string | null
|
|
82
|
+
budget: number | null
|
|
83
|
+
progress: number | null
|
|
84
|
+
done: boolean
|
|
85
|
+
link: string | null
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const COLUMNS: GridColumn<Task>[] = [
|
|
89
|
+
{ key: 'title', name: '작업', type: 'text', width: 240, pinned: true, aggregate: 'count', locked: true },
|
|
90
|
+
{
|
|
91
|
+
key: 'status',
|
|
92
|
+
name: '상태',
|
|
93
|
+
type: 'select',
|
|
94
|
+
width: 130,
|
|
95
|
+
options: [
|
|
96
|
+
{ label: '시작 전', tone: 'neutral' },
|
|
97
|
+
{ label: '진행 중', tone: 'info' },
|
|
98
|
+
{ label: '검토', tone: 'warning' },
|
|
99
|
+
{ label: '완료', tone: 'success' },
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
key: 'owner',
|
|
104
|
+
name: '담당자',
|
|
105
|
+
type: 'select',
|
|
106
|
+
width: 120,
|
|
107
|
+
options: [{ label: '김민지' }, { label: '이도현' }, { label: '박서연' }, { label: '최준호' }],
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
key: 'tags',
|
|
111
|
+
name: '태그',
|
|
112
|
+
type: 'multiSelect',
|
|
113
|
+
width: 200,
|
|
114
|
+
options: [
|
|
115
|
+
{ label: '디자인', tone: 'accent' },
|
|
116
|
+
{ label: '개발', tone: 'info' },
|
|
117
|
+
{ label: '마케팅', tone: 'warning' },
|
|
118
|
+
{ label: '긴급', tone: 'danger' },
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
{ key: 'due', name: '마감일', type: 'date', width: 130, aggregate: 'earliest' },
|
|
122
|
+
{ key: 'budget', name: '예산', type: 'number', format: 'won', width: 140, aggregate: 'sum' },
|
|
123
|
+
{ key: 'progress', name: '진행률', type: 'number', format: 'percent', width: 110, aggregate: 'avg' },
|
|
124
|
+
{ key: 'done', name: '완료', type: 'checkbox', width: 90, aggregate: 'percentChecked' },
|
|
125
|
+
{ key: 'link', name: '문서', type: 'url', width: 200 },
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
const ROWS: Task[] = [
|
|
129
|
+
{ id: 't1', title: '랜딩 페이지 리뉴얼', status: '진행 중', owner: '김민지', tags: ['디자인', '마케팅'], due: '2026-11-03', budget: 4200000, progress: 0.6, done: false, link: 'figma.com/lumen-landing' },
|
|
130
|
+
{ id: 't2', title: '결제 모듈 교체', status: '검토', owner: '이도현', tags: ['개발', '긴급'], due: '2026-10-28', budget: 8800000, progress: 0.9, done: false, link: 'github.com/lumen/pay' },
|
|
131
|
+
{ id: 't3', title: '10월 뉴스레터', status: '완료', owner: '박서연', tags: ['마케팅'], due: '2026-10-20', budget: 600000, progress: 1, done: true, link: null },
|
|
132
|
+
{ id: 't4', title: '디자인 토큰 정리', status: '진행 중', owner: '김민지', tags: ['디자인', '개발'], due: '2026-11-10', budget: 1500000, progress: 0.35, done: false, link: 'lumen.app/tokens' },
|
|
133
|
+
{ id: 't5', title: '온보딩 인터뷰 5건', status: '시작 전', owner: '최준호', tags: ['디자인'], due: '2026-11-14', budget: 500000, progress: 0, done: false, link: null },
|
|
134
|
+
{ id: 't6', title: '모바일 앱 2.0 QA', status: '진행 중', owner: '이도현', tags: ['개발'], due: '2026-11-07', budget: 3200000, progress: 0.45, done: false, link: null },
|
|
135
|
+
{ id: 't7', title: '파트너 웨비나', status: '시작 전', owner: '박서연', tags: ['마케팅'], due: '2026-11-21', budget: 2000000, progress: 0.1, done: false, link: null },
|
|
136
|
+
{ id: 't8', title: '접근성 감사', status: '완료', owner: '최준호', tags: ['디자인', '개발'], due: '2026-10-15', budget: 1200000, progress: 1, done: true, link: 'lumen.app/a11y' },
|
|
137
|
+
{ id: 't9', title: '요금제 페이지 A/B 테스트', status: '검토', owner: '김민지', tags: ['마케팅', '긴급'], due: '2026-10-30', budget: 900000, progress: 0.8, done: false, link: null },
|
|
138
|
+
{ id: 't10', title: '고객 지원 매크로 정비', status: '시작 전', owner: null, tags: [], due: null, budget: null, progress: null, done: false, link: null },
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
export default function Example() {
|
|
142
|
+
return <DataGrid title="4분기 로드맵" defaultColumns={COLUMNS} defaultRows={ROWS} height={460} style={{ width: '100%' }} />
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 스프레드시트 (Sheets · Numbers)
|
|
147
|
+
|
|
148
|
+
셀을 끌어 범위를 고르고, 선택 오른쪽 아래 점(채우기 핸들)을 끌면 1, 2 → 3, 4처럼 이어서 채워요. Sheets·Excel에서 복사한 범위를 그대로 붙여 넣을 수 있고, 범위를 넘으면 행이 새로 생겨요. 합계 열은 readOnly로 두고 onRowsChange에서 다시 계산해요.
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
import { useState } from 'react'
|
|
152
|
+
import { DataGrid, type GridColumn, type GridRow } from '@iamfiro/design'
|
|
153
|
+
|
|
154
|
+
interface Line extends GridRow {
|
|
155
|
+
item: string
|
|
156
|
+
oct: number | null
|
|
157
|
+
nov: number | null
|
|
158
|
+
dec: number | null
|
|
159
|
+
total: number
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const MONTHS = ['oct', 'nov', 'dec'] as const
|
|
163
|
+
|
|
164
|
+
const COLUMNS: GridColumn<Line>[] = [
|
|
165
|
+
{ key: 'item', name: '항목', width: 180, pinned: true },
|
|
166
|
+
{ key: 'oct', name: '10월', type: 'number', format: 'comma', width: 120, aggregate: 'sum' },
|
|
167
|
+
{ key: 'nov', name: '11월', type: 'number', format: 'comma', width: 120, aggregate: 'sum' },
|
|
168
|
+
{ key: 'dec', name: '12월', type: 'number', format: 'comma', width: 120, aggregate: 'sum' },
|
|
169
|
+
{ key: 'total', name: '합계', type: 'number', format: 'comma', width: 140, aggregate: 'sum', readOnly: true, locked: true },
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
// 합계 열은 직접 고칠 수 없고, 월별 값이 바뀔 때마다 다시 계산해요.
|
|
173
|
+
const withTotal = (l: Line): Line => ({ ...l, total: MONTHS.reduce((s, m) => s + (l[m] ?? 0), 0) })
|
|
174
|
+
|
|
175
|
+
const START: Line[] = [
|
|
176
|
+
{ id: 'l1', item: '클라우드 서버', oct: 1820000, nov: 1910000, dec: 2000000, total: 0 },
|
|
177
|
+
{ id: 'l2', item: '디자인 툴 구독', oct: 360000, nov: 360000, dec: 360000, total: 0 },
|
|
178
|
+
{ id: 'l3', item: '광고', oct: 2500000, nov: 3000000, dec: 3500000, total: 0 },
|
|
179
|
+
{ id: 'l4', item: '외주 번역', oct: 800000, nov: null, dec: 450000, total: 0 },
|
|
180
|
+
{ id: 'l5', item: '사무용품', oct: 120000, nov: 95000, dec: 130000, total: 0 },
|
|
181
|
+
].map(withTotal)
|
|
182
|
+
|
|
183
|
+
export default function Example() {
|
|
184
|
+
const [rows, setRows] = useState(START)
|
|
185
|
+
return (
|
|
186
|
+
<DataGrid
|
|
187
|
+
title="4분기 비용"
|
|
188
|
+
rows={rows}
|
|
189
|
+
onRowsChange={(next) => setRows(next.map(withTotal))}
|
|
190
|
+
defaultColumns={COLUMNS}
|
|
191
|
+
defaultView={{ gridlines: true, density: 'compact' }}
|
|
192
|
+
createRow={(values) => withTotal({ id: crypto.randomUUID(), item: '', oct: null, nov: null, dec: null, total: 0, ...values })}
|
|
193
|
+
height={360}
|
|
194
|
+
style={{ width: '100%' }}
|
|
195
|
+
/>
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### 저장된 보기
|
|
201
|
+
|
|
202
|
+
view / onViewChange로 정렬·필터·그룹·접힘 상태를 밖에서 들고 있어요. 탭마다 다른 보기를 저장하고, 표에서 바꾼 내용도 그 탭에 남아요. 그룹 머리의 +로 그 그룹 값이 채워진 행을 만들어요.
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
import { useState } from 'react'
|
|
206
|
+
import { SegmentedControl, DEFAULT_GRID_VIEW, DataGrid, type GridColumn, type GridRow, type GridView } from '@iamfiro/design'
|
|
207
|
+
|
|
208
|
+
interface Ticket extends GridRow {
|
|
209
|
+
title: string
|
|
210
|
+
status: string
|
|
211
|
+
priority: string
|
|
212
|
+
owner: string
|
|
213
|
+
due: string
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const COLUMNS: GridColumn<Ticket>[] = [
|
|
217
|
+
{ key: 'title', name: '제목', width: 260 },
|
|
218
|
+
{
|
|
219
|
+
key: 'status',
|
|
220
|
+
name: '상태',
|
|
221
|
+
type: 'select',
|
|
222
|
+
width: 120,
|
|
223
|
+
options: [
|
|
224
|
+
{ label: '접수', tone: 'neutral' },
|
|
225
|
+
{ label: '처리 중', tone: 'info' },
|
|
226
|
+
{ label: '해결', tone: 'success' },
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
key: 'priority',
|
|
231
|
+
name: '우선순위',
|
|
232
|
+
type: 'select',
|
|
233
|
+
width: 110,
|
|
234
|
+
options: [
|
|
235
|
+
{ label: '높음', tone: 'danger' },
|
|
236
|
+
{ label: '보통', tone: 'warning' },
|
|
237
|
+
{ label: '낮음', tone: 'neutral' },
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
{ key: 'owner', name: '담당자', type: 'select', width: 110, options: [{ label: '김민지' }, { label: '이도현' }, { label: '박서연' }] },
|
|
241
|
+
{ key: 'due', name: '기한', type: 'date', width: 120 },
|
|
242
|
+
]
|
|
243
|
+
|
|
244
|
+
const ROWS: Ticket[] = [
|
|
245
|
+
{ id: 'k1', title: '로그인 후 빈 화면이 떠요', status: '처리 중', priority: '높음', owner: '이도현', due: '2026-10-26' },
|
|
246
|
+
{ id: 'k2', title: '영수증 이메일이 늦게 와요', status: '접수', priority: '보통', owner: '박서연', due: '2026-10-29' },
|
|
247
|
+
{ id: 'k3', title: '다크 모드 대비가 낮아요', status: '해결', priority: '낮음', owner: '김민지', due: '2026-10-21' },
|
|
248
|
+
{ id: 'k4', title: 'CSV 내보내기 한글 깨짐', status: '처리 중', priority: '높음', owner: '김민지', due: '2026-10-27' },
|
|
249
|
+
{ id: 'k5', title: '팀 초대 링크 만료 기간', status: '접수', priority: '낮음', owner: '이도현', due: '2026-11-04' },
|
|
250
|
+
{ id: 'k6', title: '알림 설정이 저장되지 않아요', status: '처리 중', priority: '보통', owner: '박서연', due: '2026-10-31' },
|
|
251
|
+
]
|
|
252
|
+
|
|
253
|
+
type ViewId = 'all' | 'open' | 'byOwner' | 'urgent'
|
|
254
|
+
|
|
255
|
+
// 탭마다 보기(정렬·필터·그룹)를 따로 저장해요. 표에서 바꾼 내용도 그 탭에 남아요.
|
|
256
|
+
const PRESETS: Record<ViewId, GridView> = {
|
|
257
|
+
all: { ...DEFAULT_GRID_VIEW },
|
|
258
|
+
open: { ...DEFAULT_GRID_VIEW, filters: [{ id: 'f1', key: 'status', op: 'isNot', value: '해결' }], sorts: [{ key: 'due', dir: 'asc' }] },
|
|
259
|
+
byOwner: { ...DEFAULT_GRID_VIEW, groupBy: 'owner' },
|
|
260
|
+
urgent: { ...DEFAULT_GRID_VIEW, groupBy: 'priority', collapsed: ['낮음'], sorts: [{ key: 'due', dir: 'asc' }] },
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export default function Example() {
|
|
264
|
+
const [tab, setTab] = useState<ViewId>('open')
|
|
265
|
+
const [views, setViews] = useState(PRESETS)
|
|
266
|
+
|
|
267
|
+
return (
|
|
268
|
+
<div style={{ display: 'grid', gap: 12, width: '100%' }}>
|
|
269
|
+
<SegmentedControl
|
|
270
|
+
variant="underline"
|
|
271
|
+
value={tab}
|
|
272
|
+
onChange={setTab}
|
|
273
|
+
ariaLabel="저장된 보기"
|
|
274
|
+
options={[
|
|
275
|
+
{ value: 'all', label: '전체' },
|
|
276
|
+
{ value: 'open', label: '미해결' },
|
|
277
|
+
{ value: 'byOwner', label: '담당자별' },
|
|
278
|
+
{ value: 'urgent', label: '우선순위별' },
|
|
279
|
+
]}
|
|
280
|
+
/>
|
|
281
|
+
<DataGrid
|
|
282
|
+
toolbar
|
|
283
|
+
defaultColumns={COLUMNS}
|
|
284
|
+
defaultRows={ROWS}
|
|
285
|
+
view={views[tab]}
|
|
286
|
+
onViewChange={(view) => setViews((v) => ({ ...v, [tab]: view }))}
|
|
287
|
+
height={420}
|
|
288
|
+
aria-label="지원 티켓"
|
|
289
|
+
/>
|
|
290
|
+
</div>
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### 1만 행
|
|
296
|
+
|
|
297
|
+
보이는 줄만 그려서 1만 행도 부드럽게 스크롤돼요. 검색·정렬·필터·하단 계산은 전체 행 기준이에요.
|
|
298
|
+
|
|
299
|
+
```tsx
|
|
300
|
+
import { useMemo } from 'react'
|
|
301
|
+
import { DataGrid, type GridColumn, type GridRow } from '@iamfiro/design'
|
|
302
|
+
|
|
303
|
+
interface Order extends GridRow {
|
|
304
|
+
no: number
|
|
305
|
+
customer: string
|
|
306
|
+
region: string
|
|
307
|
+
amount: number
|
|
308
|
+
ordered: string
|
|
309
|
+
paid: boolean
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const NAMES = ['김민지', '이도현', '박서연', '최준호', '정하은', '강지우', '윤서준', '한예린']
|
|
313
|
+
const REGIONS = ['서울', '부산', '대구', '인천', '광주', '대전']
|
|
314
|
+
|
|
315
|
+
const COLUMNS: GridColumn<Order>[] = [
|
|
316
|
+
{ key: 'no', name: '주문 번호', type: 'number', format: 'plain', width: 120, readOnly: true, aggregate: 'count' },
|
|
317
|
+
{ key: 'customer', name: '고객', width: 140 },
|
|
318
|
+
{ key: 'region', name: '지역', type: 'select', width: 110, options: REGIONS.map((label) => ({ label })) },
|
|
319
|
+
{ key: 'amount', name: '금액', type: 'number', format: 'won', width: 140, aggregate: 'sum' },
|
|
320
|
+
{ key: 'ordered', name: '주문일', type: 'date', width: 130, aggregate: 'latest' },
|
|
321
|
+
{ key: 'paid', name: '결제', type: 'checkbox', width: 90, aggregate: 'percentChecked' },
|
|
322
|
+
]
|
|
323
|
+
|
|
324
|
+
/** 1만 행을 만들어요(같은 결과가 나오도록 간단한 의사 난수). */
|
|
325
|
+
function makeOrders(count: number): Order[] {
|
|
326
|
+
let seed = 7
|
|
327
|
+
const rand = () => (seed = (seed * 16807) % 2147483647) / 2147483647
|
|
328
|
+
return Array.from({ length: count }, (_, i) => {
|
|
329
|
+
const day = new Date(2026, 0, 1 + Math.floor(rand() * 298))
|
|
330
|
+
return {
|
|
331
|
+
id: `o${i}`,
|
|
332
|
+
no: 100001 + i,
|
|
333
|
+
customer: NAMES[Math.floor(rand() * NAMES.length)],
|
|
334
|
+
region: REGIONS[Math.floor(rand() * REGIONS.length)],
|
|
335
|
+
amount: Math.round(rand() * 400) * 1000 + 9000,
|
|
336
|
+
ordered: `${day.getFullYear()}-${String(day.getMonth() + 1).padStart(2, '0')}-${String(day.getDate()).padStart(2, '0')}`,
|
|
337
|
+
paid: rand() > 0.2,
|
|
338
|
+
}
|
|
339
|
+
})
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export default function Example() {
|
|
343
|
+
const rows = useMemo(() => makeOrders(10000), [])
|
|
344
|
+
return <DataGrid title="주문 10,000건" defaultRows={rows} defaultColumns={COLUMNS} defaultView={{ density: 'compact' }} height={440} style={{ width: '100%' }} />
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### 커스텀 셀 · 검증
|
|
349
|
+
|
|
350
|
+
render로 셀 모양을 바꾸고(편집은 type을 따라요), validate로 잘못된 값을 막아요(칸이 흔들리고 편집이 유지돼요). creatable: false인 선택 열은 새 선택지를 만들 수 없고, readOnly 열은 고칠 수 없어요. onCellEdit으로 모든 변경을 받아요.
|
|
351
|
+
|
|
352
|
+
```tsx
|
|
353
|
+
import { Avatar, Text, DataGrid, toast, type GridColumn, type GridRow } from '@iamfiro/design'
|
|
354
|
+
|
|
355
|
+
interface Member extends GridRow {
|
|
356
|
+
name: string
|
|
357
|
+
email: string
|
|
358
|
+
role: string
|
|
359
|
+
seats: number
|
|
360
|
+
joined: string
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const COLUMNS: GridColumn<Member>[] = [
|
|
364
|
+
{
|
|
365
|
+
key: 'name',
|
|
366
|
+
name: '이름',
|
|
367
|
+
width: 180,
|
|
368
|
+
// render는 보이는 모습만 바꿔요. 편집·복사·정렬은 여전히 type(텍스트)을 따라요.
|
|
369
|
+
render: (value) => (
|
|
370
|
+
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
|
|
371
|
+
<Avatar name={String(value ?? '')} size={22} />
|
|
372
|
+
<Text variant="label-md">{String(value ?? '')}</Text>
|
|
373
|
+
</span>
|
|
374
|
+
),
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
key: 'email',
|
|
378
|
+
name: '이메일',
|
|
379
|
+
width: 220,
|
|
380
|
+
validate: (v) => (v && !/^\S+@\S+\.\S+$/.test(String(v)) ? '이메일 형식이 아니에요' : null),
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
key: 'role',
|
|
384
|
+
name: '역할',
|
|
385
|
+
type: 'select',
|
|
386
|
+
width: 120,
|
|
387
|
+
creatable: false,
|
|
388
|
+
options: [
|
|
389
|
+
{ label: '관리자', tone: 'accent' },
|
|
390
|
+
{ label: '편집자', tone: 'success' },
|
|
391
|
+
{ label: '뷰어', tone: 'neutral' },
|
|
392
|
+
],
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
key: 'seats',
|
|
396
|
+
name: '좌석',
|
|
397
|
+
type: 'number',
|
|
398
|
+
format: 'plain',
|
|
399
|
+
width: 90,
|
|
400
|
+
validate: (v) => (typeof v === 'number' && (v < 1 || v > 50 || !Number.isInteger(v)) ? '1–50 사이 정수만 돼요' : null),
|
|
401
|
+
},
|
|
402
|
+
{ key: 'joined', name: '가입일', type: 'date', width: 130, readOnly: true },
|
|
403
|
+
]
|
|
404
|
+
|
|
405
|
+
const ROWS: Member[] = [
|
|
406
|
+
{ id: 'm1', name: '김민지', email: 'minji@lumen.app', role: '관리자', seats: 12, joined: '2025-03-02' },
|
|
407
|
+
{ id: 'm2', name: '이도현', email: 'dohyun@lumen.app', role: '편집자', seats: 4, joined: '2025-07-18' },
|
|
408
|
+
{ id: 'm3', name: '박서연', email: 'seoyeon@lumen.app', role: '편집자', seats: 2, joined: '2026-01-09' },
|
|
409
|
+
{ id: 'm4', name: '최준호', email: 'junho@lumen.app', role: '뷰어', seats: 1, joined: '2026-09-30' },
|
|
410
|
+
]
|
|
411
|
+
|
|
412
|
+
export default function Example() {
|
|
413
|
+
return (
|
|
414
|
+
<DataGrid
|
|
415
|
+
title="멤버"
|
|
416
|
+
defaultColumns={COLUMNS}
|
|
417
|
+
defaultRows={ROWS}
|
|
418
|
+
columnEditing={false}
|
|
419
|
+
footer={false}
|
|
420
|
+
defaultView={{ rowNumbers: false }}
|
|
421
|
+
onCellEdit={({ row, column, value }) => toast.success(`${row.name} · ${column.name} → ${String(value ?? '비움')}`)}
|
|
422
|
+
height={320}
|
|
423
|
+
style={{ width: '100%' }}
|
|
424
|
+
/>
|
|
425
|
+
)
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### 읽기 전용
|
|
430
|
+
|
|
431
|
+
readOnly면 데이터는 바뀌지 않지만 선택·복사·정렬·너비 조절은 그대로 돼요. toolbar·footer를 끄면 작은 표로도 써요.
|
|
432
|
+
|
|
433
|
+
```tsx
|
|
434
|
+
import { DataGrid, type GridColumn, type GridRow } from '@iamfiro/design'
|
|
435
|
+
|
|
436
|
+
interface Release extends GridRow {
|
|
437
|
+
version: string
|
|
438
|
+
date: string
|
|
439
|
+
changes: number
|
|
440
|
+
stable: boolean
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const COLUMNS: GridColumn<Release>[] = [
|
|
444
|
+
{ key: 'version', name: '버전', width: 120 },
|
|
445
|
+
{ key: 'date', name: '배포일', type: 'date', width: 130 },
|
|
446
|
+
{ key: 'changes', name: '변경', type: 'number', width: 100 },
|
|
447
|
+
{ key: 'stable', name: '안정판', type: 'checkbox', width: 90 },
|
|
448
|
+
]
|
|
449
|
+
|
|
450
|
+
const ROWS: Release[] = [
|
|
451
|
+
{ id: 'v5', version: 'v2.4.0', date: '2026-10-21', changes: 38, stable: true },
|
|
452
|
+
{ id: 'v4', version: 'v2.4.0-rc.2', date: '2026-10-14', changes: 12, stable: false },
|
|
453
|
+
{ id: 'v3', version: 'v2.3.2', date: '2026-09-30', changes: 7, stable: true },
|
|
454
|
+
{ id: 'v2', version: 'v2.3.1', date: '2026-09-18', changes: 4, stable: true },
|
|
455
|
+
]
|
|
456
|
+
|
|
457
|
+
export default function Example() {
|
|
458
|
+
return <DataGrid readOnly toolbar={false} footer={false} defaultColumns={COLUMNS} defaultRows={ROWS} defaultView={{ rowNumbers: false }} style={{ width: 460 }} />
|
|
459
|
+
}
|
|
460
|
+
```
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# DatePicker · Calendar
|
|
2
|
+
|
|
3
|
+
> 선택 컨트롤 · `@iamfiro/design` · 직접 만들지 말고 이 컴포넌트를 쓴다.
|
|
4
|
+
|
|
5
|
+
날짜 입력. 입력창처럼 생긴 필드를 누르면 Popover 안에 달력이 열리고, 고르면 닫혀요. 화살표로 하루씩, PageUp/PageDown으로 한 달씩(Shift는 1년) 움직이고, 달을 넘기면 그 방향으로 미끄러져요. 위의 "2026년 10월"을 누르면 한 화면에서 연도(왼쪽 스크롤 목록)와 월(오른쪽)을 고를 수 있고, 월을 누르면 바로 그 달로 돌아와요. Esc는 날짜 화면으로 돌아가요. 기간 선택은 마우스를 올린 날까지 범위를 미리 보여줘요.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Calendar, DatePicker, DateRangePicker, type DateRange } from '@iamfiro/design'
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## API
|
|
12
|
+
|
|
13
|
+
### DatePicker
|
|
14
|
+
|
|
15
|
+
| prop | 타입 | 기본값 | 설명 |
|
|
16
|
+
| --- | --- | --- | --- |
|
|
17
|
+
| `value` | `Date \| null` | | 선택한 날짜(제어). |
|
|
18
|
+
| `defaultValue` | `Date \| null` | `null` | 처음 값(비제어). |
|
|
19
|
+
| `onValueChange` | `(date: Date \| null) => void` | | 고르거나 지울 때. |
|
|
20
|
+
| `formatValue` | `(date: Date) => string` | `"10월 25일"` | 필드에 쓰는 형식. |
|
|
21
|
+
| `placeholder` | `string` | `'날짜 입력 또는 선택'` | 비었을 때 문구. |
|
|
22
|
+
| `clearable` | `boolean` | `true` | × 지우기 버튼. |
|
|
23
|
+
| `min / max` | `Date` | | 고를 수 있는 처음·마지막 날. |
|
|
24
|
+
| `isDateDisabled` | `(date: Date) => boolean` | | 추가로 막을 날(공휴일, 주말…). |
|
|
25
|
+
| `weekStartsOn` | `0 \| 1` | `0` | 0 = 일요일 시작, 1 = 월요일 시작. |
|
|
26
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 필드 높이 36 / 44 / 52px. |
|
|
27
|
+
| `invalid / disabled` | `boolean` | | 오류 · 비활성. |
|
|
28
|
+
| `align` | `'start' \| 'end'` | `'start'` | 달력이 필드 어느 쪽에 맞춰 열릴지. |
|
|
29
|
+
|
|
30
|
+
### DateRangePicker
|
|
31
|
+
|
|
32
|
+
DatePicker와 같은 props에 아래가 다르거나 추가돼요.
|
|
33
|
+
|
|
34
|
+
| prop | 타입 | 기본값 | 설명 |
|
|
35
|
+
| --- | --- | --- | --- |
|
|
36
|
+
| `value / defaultValue` | `DateRange` | | { from: Date \| null, to: Date \| null } |
|
|
37
|
+
| `onValueChange` | `(range: DateRange) => void` | | 시작·끝을 고를 때마다. 두 날짜가 다 고르면 닫혀요. |
|
|
38
|
+
| `presets` | `DatePreset[]` | `DEFAULT_DATE_PRESETS` | { label, range: () => DateRange }[]. []면 숨김. |
|
|
39
|
+
|
|
40
|
+
### Calendar
|
|
41
|
+
|
|
42
|
+
| prop | 타입 | 기본값 | 설명 |
|
|
43
|
+
| --- | --- | --- | --- |
|
|
44
|
+
| `mode` | `'single' \| 'range'` | `'single'` | 하루 또는 기간. |
|
|
45
|
+
| `value / onValueChange` | `Date \| null · DateRange` | | mode에 맞는 값. |
|
|
46
|
+
| `defaultMonth` | `Date` | | 처음 보여줄 달. 생략하면 선택한 날 또는 오늘. |
|
|
47
|
+
| `min / max / isDateDisabled / weekStartsOn` | `…` | | DatePicker와 같아요. |
|
|
48
|
+
| `autoFocus` | `boolean` | | 처음에 선택한 날(또는 오늘)로 포커스. 팝오버 안에서 써요. |
|
|
49
|
+
| `showFooter` | `boolean` | `true` | 아래 줄("오늘" 버튼, 기간 선택 안내). |
|
|
50
|
+
|
|
51
|
+
## 예제
|
|
52
|
+
|
|
53
|
+
예제를 감싼 `style={{ ... }}` div는 미리보기 배치용이다. 실제 화면 배치는 `Stack` · `Inline` · `Grid`로 한다.
|
|
54
|
+
|
|
55
|
+
### 날짜 선택
|
|
56
|
+
|
|
57
|
+
값이 올해면 연도는 생략해요("10월 25일"). ×로 지울 수 있어요.
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { useState } from 'react'
|
|
61
|
+
import { Stack, Text, DatePicker, formatDate } from '@iamfiro/design'
|
|
62
|
+
|
|
63
|
+
export default function Example() {
|
|
64
|
+
const [date, setDate] = useState<Date | null>(() => new Date())
|
|
65
|
+
return (
|
|
66
|
+
<Stack gap={2} style={{ width: 260 }}>
|
|
67
|
+
<DatePicker value={date} onValueChange={setDate} aria-label="마감일" />
|
|
68
|
+
<Text variant="caption" tone="tertiary">
|
|
69
|
+
선택: {date ? formatDate(date, true) : '없음'}
|
|
70
|
+
</Text>
|
|
71
|
+
</Stack>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 직접 입력
|
|
77
|
+
|
|
78
|
+
"10/25", "2026.10.25", "10월 25일", "내일"처럼 쳐도 돼요. 완전한 날짜가 되면 달력이 따라오고, Enter로 확정, ↓로 달력에 들어가요. 못 알아들으면 빨갛게 표시하고, 칸을 벗어나면 원래 값으로 돌아가요.
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
import { useState } from 'react'
|
|
82
|
+
import { Field, Stack, Text, DatePicker, formatDate } from '@iamfiro/design'
|
|
83
|
+
|
|
84
|
+
export default function Example() {
|
|
85
|
+
const [date, setDate] = useState<Date | null>(null)
|
|
86
|
+
return (
|
|
87
|
+
<Stack gap={2} style={{ width: 280 }}>
|
|
88
|
+
<Field label="출발일" htmlFor="depart" hint="예) 10/25 · 2026.10.25 · 10월 25일 · 내일">
|
|
89
|
+
<DatePicker id="depart" value={date} onValueChange={setDate} />
|
|
90
|
+
</Field>
|
|
91
|
+
<Text variant="caption" tone="tertiary">
|
|
92
|
+
값: {date ? formatDate(date, true) : '없음'}
|
|
93
|
+
</Text>
|
|
94
|
+
</Stack>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 기간 선택
|
|
100
|
+
|
|
101
|
+
두 날짜를 차례로 누르거나, 왼쪽의 빠른 선택(오늘·최근 7일…)을 써요. presets로 바꾸거나 []로 숨겨요.
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
import { useState } from 'react'
|
|
105
|
+
import { Stack, Text, DateRangePicker, formatDate, type DateRange } from '@iamfiro/design'
|
|
106
|
+
|
|
107
|
+
export default function Example() {
|
|
108
|
+
const [range, setRange] = useState<DateRange>({ from: null, to: null })
|
|
109
|
+
const days = range.from && range.to ? Math.round((range.to.getTime() - range.from.getTime()) / 86400000) + 1 : 0
|
|
110
|
+
return (
|
|
111
|
+
<Stack gap={2} style={{ width: 300 }}>
|
|
112
|
+
<DateRangePicker value={range} onValueChange={setRange} aria-label="조회 기간" />
|
|
113
|
+
<Text variant="caption" tone="tertiary">
|
|
114
|
+
{range.from && range.to ? `${formatDate(range.from)} ~ ${formatDate(range.to)} · ${days}일` : '두 날짜를 고르거나 빠른 선택을 눌러 보세요'}
|
|
115
|
+
</Text>
|
|
116
|
+
</Stack>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 선택 가능한 날짜 제한
|
|
122
|
+
|
|
123
|
+
min · max와 isDateDisabled로 지난 날짜와 주말을 막아요. 막힌 날은 취소선으로 보여요.
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
import { Field, DatePicker } from '@iamfiro/design'
|
|
127
|
+
|
|
128
|
+
const today = new Date()
|
|
129
|
+
const inThreeMonths = new Date(today.getFullYear(), today.getMonth() + 3, today.getDate())
|
|
130
|
+
const isWeekend = (d: Date) => d.getDay() === 0 || d.getDay() === 6
|
|
131
|
+
|
|
132
|
+
export default function Example() {
|
|
133
|
+
return (
|
|
134
|
+
<Field label="미팅 날짜 · 평일, 오늘부터 3개월 안" htmlFor="meeting-date">
|
|
135
|
+
<div style={{ width: 280 }}>
|
|
136
|
+
<DatePicker id="meeting-date" min={today} max={inThreeMonths} isDateDisabled={isWeekend} placeholder="평일을 고르세요" />
|
|
137
|
+
</div>
|
|
138
|
+
</Field>
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Calendar 단독
|
|
144
|
+
|
|
145
|
+
팝오버 없이 화면에 바로 두는 달력. 일요일은 빨강, 토요일은 파랑 톤. 아래 "오늘"로 바로 이동하고, 월요일 시작(weekStartsOn={1})도 돼요.
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
import { useState } from 'react'
|
|
149
|
+
import { Card, CardBody, Calendar, Inline } from '@iamfiro/design'
|
|
150
|
+
|
|
151
|
+
export default function Example() {
|
|
152
|
+
const [date, setDate] = useState<Date | null>(() => new Date())
|
|
153
|
+
return (
|
|
154
|
+
<Inline gap={4} align="start">
|
|
155
|
+
<Card>
|
|
156
|
+
<CardBody>
|
|
157
|
+
<Calendar value={date} onValueChange={setDate} />
|
|
158
|
+
</CardBody>
|
|
159
|
+
</Card>
|
|
160
|
+
<Card>
|
|
161
|
+
<CardBody>
|
|
162
|
+
<Calendar value={date} onValueChange={setDate} weekStartsOn={1} />
|
|
163
|
+
</CardBody>
|
|
164
|
+
</Card>
|
|
165
|
+
</Inline>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### 크기와 상태
|
|
171
|
+
|
|
172
|
+
Field와 함께 쓰면 라벨이 연결돼요. size · invalid · disabled.
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
import { Field, Grid, DatePicker } from '@iamfiro/design'
|
|
176
|
+
|
|
177
|
+
const TODAY = new Date()
|
|
178
|
+
|
|
179
|
+
export default function Example() {
|
|
180
|
+
return (
|
|
181
|
+
<Grid columns={2} gap={4} style={{ width: 480 }}>
|
|
182
|
+
<Field label="작게 (sm)" htmlFor="dp-sm">
|
|
183
|
+
<DatePicker id="dp-sm" size="sm" defaultValue={TODAY} />
|
|
184
|
+
</Field>
|
|
185
|
+
<Field label="크게 (lg)" htmlFor="dp-lg">
|
|
186
|
+
<DatePicker id="dp-lg" size="lg" />
|
|
187
|
+
</Field>
|
|
188
|
+
<Field label="시작일" invalid hint="시작일을 골라 주세요." htmlFor="dp-err">
|
|
189
|
+
<DatePicker id="dp-err" invalid />
|
|
190
|
+
</Field>
|
|
191
|
+
<Field label="계약일" htmlFor="dp-off">
|
|
192
|
+
<DatePicker id="dp-off" defaultValue={new Date(2026, 0, 2)} disabled />
|
|
193
|
+
</Field>
|
|
194
|
+
</Grid>
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
```
|