xertica-ui 2.1.2 → 2.1.3
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/CHANGELOG.md +31 -0
- package/README.md +1 -1
- package/assets/xertica-logo.svg +37 -37
- package/assets/xertica-x-logo.svg +20 -20
- package/bin/cli.ts +1 -1
- package/bin/generate-tokens.ts +13 -7
- package/components/assistant/index.ts +6 -6
- package/components/assistant/xertica-assistant/index.ts +2 -0
- package/components/assistant/xertica-assistant/use-assistant.ts +564 -0
- package/components/assistant/xertica-assistant/xertica-assistant.stories.tsx +200 -0
- package/components/assistant/xertica-assistant/xertica-assistant.tsx +183 -474
- package/components/blocks/card-patterns/ActivityCard.tsx +72 -72
- package/components/blocks/card-patterns/FeatureCard.tsx +100 -100
- package/components/blocks/card-patterns/NotificationCard.tsx +127 -127
- package/components/blocks/card-patterns/ProfileCard.tsx +84 -84
- package/components/blocks/card-patterns/ProjectCard.tsx +89 -89
- package/components/blocks/card-patterns/QuickActionCard.tsx +62 -62
- package/components/blocks/card-patterns/card-patterns.stories.tsx +445 -445
- package/components/blocks/card-patterns/index.ts +17 -17
- package/components/blocks/index.ts +1 -1
- package/components/brand/index.ts +6 -6
- package/components/brand/xertica-provider/XerticaProvider.tsx +48 -48
- package/components/brand/xertica-provider/xertica-provider.mdx +61 -61
- package/components/brand/xertica-provider/xertica-provider.test.tsx +52 -52
- package/components/examples/MapExamples.tsx +282 -282
- package/components/examples/SimpleFilterableMap.tsx +191 -191
- package/components/examples/index.ts +51 -51
- package/components/figma/ImageWithFallback.tsx +27 -27
- package/components/hooks/index.ts +7 -7
- package/components/index.ts +5 -5
- package/components/layout/header/header.test.tsx +8 -8
- package/components/layout/header/header.tsx +4 -4
- package/components/layout/index.ts +2 -2
- package/components/layout/sidebar/index.ts +2 -0
- package/components/layout/sidebar/sidebar.mdx +1 -1
- package/components/layout/sidebar/sidebar.stories.tsx +160 -8
- package/components/layout/sidebar/sidebar.test.tsx +11 -11
- package/components/layout/sidebar/sidebar.tsx +610 -471
- package/components/layout/sidebar/use-sidebar.ts +113 -0
- package/components/media/FloatingMediaWrapper.tsx +11 -11
- package/components/media/audio-player/AudioPlayer.tsx +22 -22
- package/components/media/index.ts +3 -3
- package/components/pages/home-content/HomeContent.tsx +7 -7
- package/components/pages/home-content/home-content.mdx +62 -62
- package/components/pages/home-page/HomePage.stories.tsx +39 -39
- package/components/pages/home-page/HomePage.tsx +1 -1
- package/components/pages/home-page/home-page.mdx +53 -53
- package/components/pages/index.ts +8 -8
- package/components/pages/template-content/TemplateContent.tsx +5 -5
- package/components/pages/template-content/template-content.mdx +61 -61
- package/components/pages/template-page/TemplatePage.stories.tsx +39 -39
- package/components/pages/template-page/TemplatePage.tsx +5 -5
- package/components/pages/template-page/template-page.mdx +53 -53
- package/components/public-api-smoke.test.tsx +52 -52
- package/components/shared/assistant-utils.ts +43 -43
- package/components/shared/layout-constants.ts +1 -1
- package/components/ui/accordion/accordion.mdx +8 -8
- package/components/ui/accordion/accordion.stories.tsx +53 -53
- package/components/ui/alert/alert.mdx +8 -8
- package/components/ui/alert-dialog/alert-dialog.mdx +8 -8
- package/components/ui/aspect-ratio/aspect-ratio.mdx +8 -8
- package/components/ui/assistant-chart/assistant-chart.mdx +8 -8
- package/components/ui/avatar/avatar.mdx +8 -8
- package/components/ui/badge/badge.mdx +8 -8
- package/components/ui/badge/badge.tsx +9 -9
- package/components/ui/breadcrumb/breadcrumb.mdx +8 -8
- package/components/ui/button/button.mdx +8 -8
- package/components/ui/calendar/calendar.mdx +8 -8
- package/components/ui/calendar/calendar.tsx +258 -258
- package/components/ui/card/card.mdx +8 -8
- package/components/ui/card/card.stories.tsx +245 -245
- package/components/ui/carousel/carousel.mdx +8 -8
- package/components/ui/chart/chart.mdx +8 -8
- package/components/ui/chart/chart.stories.tsx +1303 -344
- package/components/ui/chart/chart.test.tsx +154 -154
- package/components/ui/chart/chart.tsx +2367 -1494
- package/components/ui/checkbox/checkbox.mdx +8 -8
- package/components/ui/checkbox/checkbox.stories.tsx +20 -20
- package/components/ui/collapsible/collapsible.mdx +8 -8
- package/components/ui/command/command.mdx +8 -8
- package/components/ui/context-menu/context-menu.mdx +8 -8
- package/components/ui/dialog/dialog.mdx +8 -8
- package/components/ui/drawer/drawer.mdx +8 -8
- package/components/ui/dropdown-menu/dropdown-menu.mdx +8 -8
- package/components/ui/empty/empty.mdx +8 -8
- package/components/ui/file-upload/file-upload.mdx +8 -8
- package/components/ui/file-upload/file-upload.stories.tsx +100 -0
- package/components/ui/file-upload/file-upload.tsx +14 -74
- package/components/ui/file-upload/index.ts +1 -0
- package/components/ui/file-upload/use-file-upload.ts +181 -0
- package/components/ui/google-maps-loader/google-maps-loader.tsx +2 -2
- package/components/ui/hover-card/hover-card.mdx +8 -8
- package/components/ui/input/input.mdx +8 -8
- package/components/ui/input-otp/input-otp.mdx +8 -8
- package/components/ui/input-otp/input-otp.stories.tsx +6 -6
- package/components/ui/label/label.mdx +8 -8
- package/components/ui/map/map.mdx +8 -8
- package/components/ui/map/map.stories.tsx +51 -51
- package/components/ui/map/map.tsx +2 -2
- package/components/ui/menubar/menubar.mdx +8 -8
- package/components/ui/navigation-menu/navigation-menu.mdx +8 -8
- package/components/ui/notification-badge/notification-badge.mdx +8 -8
- package/components/ui/pagination/index.ts +2 -0
- package/components/ui/pagination/pagination.mdx +8 -8
- package/components/ui/pagination/pagination.stories.tsx +94 -0
- package/components/ui/pagination/use-pagination.ts +194 -0
- package/components/ui/popover/popover.mdx +8 -8
- package/components/ui/progress/progress.mdx +8 -8
- package/components/ui/progress/progress.tsx +68 -68
- package/components/ui/radio-group/radio-group.mdx +8 -8
- package/components/ui/rating/rating.mdx +8 -8
- package/components/ui/resizable/resizable.mdx +8 -8
- package/components/ui/rich-text-editor/index.ts +2 -0
- package/components/ui/rich-text-editor/rich-text-editor.stories.tsx +129 -1
- package/components/ui/rich-text-editor/rich-text-editor.tsx +86 -305
- package/components/ui/rich-text-editor/use-rich-text-editor.ts +439 -0
- package/components/ui/route-map/route-map.mdx +8 -8
- package/components/ui/route-map/route-map.stories.tsx +54 -54
- package/components/ui/route-map/route-map.tsx +2 -2
- package/components/ui/scroll-area/scroll-area.mdx +8 -8
- package/components/ui/search/search.mdx +8 -8
- package/components/ui/select/select.mdx +8 -8
- package/components/ui/select/select.stories.tsx +9 -9
- package/components/ui/separator/separator.mdx +8 -8
- package/components/ui/sheet/sheet.mdx +8 -8
- package/components/ui/sheet/sheet.stories.tsx +95 -95
- package/components/ui/simple-map/simple-map.mdx +8 -8
- package/components/ui/simple-map/simple-map.stories.tsx +48 -48
- package/components/ui/skeleton/skeleton.mdx +8 -8
- package/components/ui/slider/slider.mdx +8 -8
- package/components/ui/sonner/sonner.mdx +8 -8
- package/components/ui/stats-card/stats-card.mdx +8 -8
- package/components/ui/stepper/index.ts +3 -1
- package/components/ui/stepper/stepper.mdx +8 -8
- package/components/ui/stepper/stepper.stories.tsx +116 -0
- package/components/ui/stepper/stepper.tsx +4 -4
- package/components/ui/stepper/use-stepper.ts +137 -0
- package/components/ui/switch/switch.mdx +8 -8
- package/components/ui/switch/switch.stories.tsx +20 -20
- package/components/ui/table/table.mdx +8 -8
- package/components/ui/tabs/tabs.mdx +8 -8
- package/components/ui/tabs/tabs.stories.tsx +26 -26
- package/components/ui/textarea/textarea.mdx +8 -8
- package/components/ui/timeline/timeline.mdx +8 -8
- package/components/ui/toggle/toggle.mdx +8 -8
- package/components/ui/toggle-group/toggle-group.mdx +8 -8
- package/components/ui/tooltip/tooltip.mdx +8 -8
- package/components/ui/tree-view/index.ts +4 -1
- package/components/ui/tree-view/tree-view.mdx +8 -8
- package/components/ui/tree-view/tree-view.stories.tsx +110 -4
- package/components/ui/tree-view/tree-view.tsx +17 -125
- package/components/ui/tree-view/use-tree-view.ts +229 -0
- package/components.json +1512 -1512
- package/contexts/ApiKeyContext.tsx +72 -72
- package/contexts/BrandColorsContext.tsx +26 -26
- package/contexts/LanguageContext.tsx +13 -13
- package/contexts/LayoutContext.test.tsx +11 -11
- package/contexts/LayoutContext.tsx +29 -29
- package/contexts/ThemeContext.tsx +26 -26
- package/contexts/theme-data.ts +4 -4
- package/dist/{AssistantChart-CVzmmhx4.js → AssistantChart-C_hwFRRr.js} +4 -4
- package/dist/{AssistantChart-BAx9VQvb.cjs → AssistantChart-CldVCVDe.cjs} +311 -94
- package/dist/{AssistantChart-CVko2A1W.js → AssistantChart-Cu3m7RBo.js} +314 -97
- package/dist/{AssistantChart-9w31gdAb.cjs → AssistantChart-DoZCyS5r.cjs} +4 -4
- package/dist/AudioPlayer-B1lt5cPl.cjs +989 -0
- package/dist/AudioPlayer-BZ7bibzU.cjs +982 -0
- package/dist/AudioPlayer-C12BjQBV.cjs +997 -0
- package/dist/AudioPlayer-DMcG_c7L.js +990 -0
- package/dist/AudioPlayer-DcFKRJE_.js +998 -0
- package/dist/AudioPlayer-e8LfNoqO.js +983 -0
- package/dist/{xertica-assistant-Qp3ydksa.cjs → CodeBlock-7TTgmdGG.cjs} +263 -51
- package/dist/{xertica-assistant-gnCJdcZY.js → CodeBlock-BeSt1h5P.js} +219 -7
- package/dist/CodeBlock-BgfYL_rD.cjs +2094 -0
- package/dist/CodeBlock-BlcqlA9M.cjs +2094 -0
- package/dist/CodeBlock-Bnmeu5ez.cjs +2094 -0
- package/dist/CodeBlock-BtfPlbAI.js +2078 -0
- package/dist/CodeBlock-CIySIuYr.js +2078 -0
- package/dist/CodeBlock-CuPtUM-7.cjs +2094 -0
- package/dist/CodeBlock-D6ffWXgc.js +2078 -0
- package/dist/CodeBlock-D8dcwbit.cjs +2094 -0
- package/dist/CodeBlock-DMZrFnlw.cjs +2094 -0
- package/dist/CodeBlock-DlBehYN8.js +2078 -0
- package/dist/CodeBlock-DnYNI8rQ.js +2078 -0
- package/dist/CodeBlock-DvKWbSnE.cjs +2094 -0
- package/dist/CodeBlock-DwMCfkFY.js +2078 -0
- package/dist/CodeBlock-Dy6CNYyj.js +2078 -0
- package/dist/CodeBlock-U1pPOQI7.cjs +2094 -0
- package/dist/CodeBlock-f_GpNhEB.js +2078 -0
- package/dist/CodeBlock-oB6u8nI1.js +2078 -0
- package/dist/CodeBlock-tZC31B73.cjs +2094 -0
- package/dist/ImageWithFallback-CGtidP6B.cjs +4542 -0
- package/dist/ImageWithFallback-lsg3pdFg.js +4508 -0
- package/dist/LanguageSelector-B5YfbHra.js +231 -0
- package/dist/LanguageSelector-D6uacAIM.cjs +230 -0
- package/dist/LayoutContext-BAql6ZRY.js +97 -0
- package/dist/LayoutContext-BvK-ggDa.cjs +96 -0
- package/dist/ThemeContext-BoH4NLfN.js +734 -0
- package/dist/{ThemeContext-BbBNoFTG.js → ThemeContext-C2EwAPDt.js} +2 -2
- package/dist/ThemeContext-r69W20Xg.cjs +733 -0
- package/dist/{ThemeContext-Cmr8Ex8H.cjs → ThemeContext-vTjumZeM.cjs} +2 -2
- package/dist/{VerifyEmailPage-BE-L9mB7.js → VerifyEmailPage-C0c2e5n0.js} +7 -7
- package/dist/{VerifyEmailPage-DMBh4NM9.cjs → VerifyEmailPage-CYXtbKi3.cjs} +1 -1
- package/dist/{VerifyEmailPage-CR7kb5df.cjs → VerifyEmailPage-Cwi3kbol.cjs} +7 -7
- package/dist/{VerifyEmailPage-CdYPSJoO.js → VerifyEmailPage-DvMLZgFt.js} +1 -1
- package/dist/XerticaOrbe-KL1RBHzw.cjs +1354 -0
- package/dist/XerticaOrbe-zwS1p2a8.js +1355 -0
- package/dist/XerticaProvider-6btlAlzc.js +17 -0
- package/dist/XerticaProvider-BNoNOxQ5.cjs +16 -0
- package/dist/XerticaProvider-BlY2limY.cjs +38 -0
- package/dist/{XerticaProvider-BITjgC5p.js → XerticaProvider-CEoWMTxu.js} +2 -2
- package/dist/{XerticaProvider-By8q3Roe.cjs → XerticaProvider-CllrbMEJ.cjs} +2 -2
- package/dist/XerticaProvider-DDuiIcKo.js +39 -0
- package/dist/XerticaProvider-cI9hSs27.cjs +38 -0
- package/dist/XerticaProvider-hSwhNQex.js +39 -0
- package/dist/{alert-dialog-yckpaOpy.cjs → alert-dialog-DSKByiKZ.cjs} +3 -3
- package/dist/{alert-dialog-iDe5VE5o.js → alert-dialog-s-vmNkJ_.js} +3 -3
- package/dist/assistant.cjs.js +2 -1
- package/dist/assistant.es.js +3 -2
- package/dist/brand.cjs.js +1 -1
- package/dist/brand.es.js +1 -1
- package/dist/breadcrumb-CqJ7bHY5.js +161 -0
- package/dist/breadcrumb-m9Hb2_XN.cjs +177 -0
- package/dist/cli.js +14 -8
- package/dist/components/assistant/xertica-assistant/index.d.ts +2 -0
- package/dist/components/assistant/xertica-assistant/use-assistant.d.ts +126 -0
- package/dist/components/assistant/xertica-assistant/xertica-assistant.d.ts +7 -2
- package/dist/components/blocks/audio-player/AudioPlayer.d.ts +35 -0
- package/dist/components/blocks/audio-player/index.d.ts +1 -0
- package/dist/components/blocks/document-editor/DocumentEditor.d.ts +26 -0
- package/dist/components/blocks/document-editor/index.d.ts +1 -0
- package/dist/components/blocks/podcast-player/PodcastPlayer.d.ts +41 -0
- package/dist/components/blocks/podcast-player/index.d.ts +1 -0
- package/dist/components/layout/sidebar/index.d.ts +2 -0
- package/dist/components/layout/sidebar/sidebar.d.ts +80 -0
- package/dist/components/layout/sidebar/use-sidebar.d.ts +22 -0
- package/dist/components/shared/layout-constants.d.ts +1 -1
- package/dist/components/ui/button/button.d.ts +1 -1
- package/dist/components/ui/chart/chart.d.ts +162 -5
- package/dist/components/ui/file-upload/file-upload.d.ts +2 -0
- package/dist/components/ui/file-upload/index.d.ts +1 -0
- package/dist/components/ui/file-upload/use-file-upload.d.ts +49 -0
- package/dist/components/ui/pagination/index.d.ts +2 -0
- package/dist/components/ui/pagination/use-pagination.d.ts +78 -0
- package/dist/components/ui/rich-text-editor/index.d.ts +2 -0
- package/dist/components/ui/rich-text-editor/use-rich-text-editor.d.ts +107 -0
- package/dist/components/ui/stepper/index.d.ts +3 -1
- package/dist/components/ui/stepper/stepper.d.ts +2 -2
- package/dist/components/ui/stepper/use-stepper.d.ts +60 -0
- package/dist/components/ui/tree-view/index.d.ts +4 -1
- package/dist/components/ui/tree-view/tree-view.d.ts +4 -6
- package/dist/components/ui/tree-view/use-tree-view.d.ts +60 -0
- package/dist/dropdown-menu-BDB5CmQs.cjs +247 -0
- package/dist/dropdown-menu-DQidbKBD.js +231 -0
- package/dist/google-maps-loader-BFWp6VPd.js +287 -0
- package/dist/{google-maps-loader-t2IlYBzw.js → google-maps-loader-CTYySAun.js} +4 -0
- package/dist/{google-maps-loader-BqsYL48U.cjs → google-maps-loader-Y-QkD-Li.cjs} +5 -0
- package/dist/google-maps-loader-eS3uQ5TA.cjs +287 -0
- package/dist/header-Cgy6vYPk.cjs +731 -0
- package/dist/header-DRlT4jgI.js +715 -0
- package/dist/header-Dux00SI4.cjs +731 -0
- package/dist/header-EkGKXPsD.js +715 -0
- package/dist/header-WfEywpyc.cjs +731 -0
- package/dist/header-tifNQn2U.js +715 -0
- package/dist/hooks.cjs.js +1 -1
- package/dist/hooks.es.js +1 -1
- package/dist/index-BhapVLVj.js +8 -0
- package/dist/{index-D3RLKRAs.cjs → index-COtD8bRW.cjs} +1 -1
- package/dist/index-D6fxYEY8.cjs +7 -0
- package/dist/index-DW5tYe26.js +8 -0
- package/dist/index.cjs.js +19 -8
- package/dist/index.es.js +39 -28
- package/dist/index.umd.js +1043 -470
- package/dist/input-2R4loU86.js +127 -0
- package/dist/input-DWANSKGb.cjs +145 -0
- package/dist/layout.cjs.js +89 -1
- package/dist/layout.es.js +90 -2
- package/dist/pages.cjs.js +1 -1
- package/dist/pages.es.js +1 -1
- package/dist/rich-text-editor-0mraWT5y.cjs +2376 -0
- package/dist/rich-text-editor-B6jMRLzk.cjs +1939 -0
- package/dist/rich-text-editor-B8_oYcIR.js +1730 -0
- package/dist/rich-text-editor-B9UbSXNb.js +1203 -0
- package/dist/rich-text-editor-BYuRBNBU.js +2373 -0
- package/dist/rich-text-editor-Bb9pySTs.cjs +2374 -0
- package/dist/rich-text-editor-BcL6L3cm.cjs +2374 -0
- package/dist/rich-text-editor-BoVZYtTs.cjs +2391 -0
- package/dist/rich-text-editor-CPV1lEPH.cjs +1748 -0
- package/dist/rich-text-editor-CoKqbCtu.cjs +1799 -0
- package/dist/rich-text-editor-Cw56T_mB.js +2356 -0
- package/dist/rich-text-editor-Cyt8qs2b.js +1921 -0
- package/dist/rich-text-editor-D6H84OcX.cjs +1220 -0
- package/dist/rich-text-editor-D76gD-QI.js +2328 -0
- package/dist/rich-text-editor-DKkokOnA.js +1781 -0
- package/dist/rich-text-editor-DNsdpN64.cjs +2359 -0
- package/dist/rich-text-editor-DfG8bCyY.js +2358 -0
- package/dist/rich-text-editor-DgF8s7xW.js +2949 -0
- package/dist/rich-text-editor-Dxjw31Z4.js +2341 -0
- package/dist/rich-text-editor-DzP0Epmb.js +2356 -0
- package/dist/rich-text-editor-mWoaSCE4.cjs +2966 -0
- package/dist/rich-text-editor-skplNlBM.cjs +2345 -0
- package/dist/select-Bkbr0f-Z.cjs +162 -0
- package/dist/select-CvIVdX2n.js +145 -0
- package/dist/sidebar-CK_0ZQHj.cjs +803 -0
- package/dist/sidebar-CUuOvYhK.js +787 -0
- package/dist/slider-Bc5Hd0y1.js +56 -0
- package/dist/slider-N7hFFj6X.cjs +73 -0
- package/dist/tooltip-Ded96neP.cjs +137 -0
- package/dist/tooltip-HDOoD2-0.js +120 -0
- package/dist/ui.cjs.js +14 -4
- package/dist/ui.es.js +26 -16
- package/dist/use-mobile-B0hNy_Y6.cjs +4303 -0
- package/dist/use-mobile-BXuYROXM.js +4202 -0
- package/dist/use-mobile-Bbd51ASU.cjs +4392 -0
- package/dist/use-mobile-Bk6CX-TC.js +4359 -0
- package/dist/use-mobile-BvYdisLP.js +4202 -0
- package/dist/use-mobile-BzuxjzNX.cjs +4392 -0
- package/dist/use-mobile-CG2-SdXV.cjs +4235 -0
- package/dist/use-mobile-CKb5pqTs.js +4269 -0
- package/dist/use-mobile-CYuAuGDl.js +4202 -0
- package/dist/use-mobile-CaENcqm-.js +4508 -0
- package/dist/use-mobile-CbrYgJGJ.js +4203 -0
- package/dist/use-mobile-DMOvImGQ.cjs +4542 -0
- package/dist/use-mobile-DRB3BQgD.cjs +4235 -0
- package/dist/use-mobile-DZvv7QMR.js +4359 -0
- package/dist/use-mobile-DdI_TXam.cjs +4235 -0
- package/dist/use-mobile-DlceKf8a.js +4359 -0
- package/dist/use-mobile-DsOnow1o.cjs +4236 -0
- package/dist/use-mobile-Kcj6jSnK.cjs +4392 -0
- package/dist/use-mobile-bnKcua_i.js +4202 -0
- package/dist/use-mobile-ncXBeE2z.cjs +4235 -0
- package/dist/{xertica-assistant-B1IaHXnB.cjs → xertica-assistant-dyP7KHM5.cjs} +533 -392
- package/dist/{xertica-assistant-DPsESB6t.js → xertica-assistant-yX1CFBBo.js} +535 -394
- package/dist/xertica-ui.css +2 -2
- package/docs/ai-usage.md +28 -10
- package/docs/architecture.md +76 -0
- package/docs/components/assistant.md +159 -0
- package/docs/components/calendar.md +154 -154
- package/docs/components/card-patterns.md +337 -337
- package/docs/components/card.md +235 -235
- package/docs/components/chart.md +354 -39
- package/docs/components/file-upload.md +119 -2
- package/docs/components/map.md +84 -84
- package/docs/components/pagination.md +187 -0
- package/docs/components/rich-text-editor.md +164 -0
- package/docs/components/sidebar.md +153 -4
- package/docs/components/stepper.md +157 -12
- package/docs/components/tree-view.md +164 -6
- package/docs/components/xertica-provider.md +24 -24
- package/docs/decision-tree.md +287 -287
- package/docs/getting-started.md +1 -1
- package/docs/guidelines.md +14 -8
- package/docs/layout.md +2 -2
- package/docs/llms.md +4 -4
- package/guidelines/Guidelines.md +252 -250
- package/hooks/useTheme.ts +3 -3
- package/imports/Podcast.tsx +388 -388
- package/imports/XerticaAi.tsx +45 -45
- package/imports/XerticaX.tsx +19 -19
- package/imports/svg-aueiaqngck.ts +11 -11
- package/imports/svg-v9krss1ozd.ts +16 -16
- package/imports/svg-vhrdofe3qe.ts +5 -5
- package/llms-compact.txt +327 -327
- package/llms.txt +160 -160
- package/package.json +203 -203
- package/styles/xertica/app-overrides/chat.css +61 -61
- package/styles/xertica/app-overrides/scrollbar.css +33 -33
- package/styles/xertica/integrations/google-maps.css +76 -76
- package/styles/xertica/integrations/sonner.css +73 -73
- package/styles/xertica/tokens.css +41 -12
- package/templates/CLAUDE.md +182 -172
- package/templates/guidelines/Guidelines.md +325 -313
- package/templates/package.json +3 -3
- package/templates/src/features/auth/index.ts +4 -4
- package/templates/src/features/auth/ui/AuthPageShell.tsx +34 -34
- package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +70 -70
- package/templates/src/features/auth/ui/LoginContent.tsx +90 -90
- package/templates/src/features/auth/ui/ResetPasswordContent.tsx +151 -151
- package/templates/src/features/auth/ui/SocialLoginButtons.tsx +59 -59
- package/templates/src/features/auth/ui/VerifyEmailContent.tsx +82 -82
- package/templates/src/features/home/index.ts +1 -1
- package/templates/src/features/home/ui/HomeContent.tsx +100 -100
- package/templates/src/features/template/index.ts +5 -5
- package/templates/src/features/template/ui/CrudTemplate.tsx +3 -3
- package/templates/src/features/template/ui/DashboardTemplate.tsx +3 -3
- package/templates/src/features/template/ui/FormTemplate.tsx +120 -120
- package/templates/src/features/template/ui/LoginTemplate.tsx +3 -3
- package/templates/src/pages/AssistantPage.tsx +328 -328
- package/templates/src/pages/ForgotPasswordPage.tsx +6 -6
- package/templates/src/pages/HomePage.tsx +57 -57
- package/templates/src/pages/LoginPage.tsx +10 -10
- package/templates/src/pages/ResetPasswordPage.tsx +6 -6
- package/templates/src/pages/TemplatePage.tsx +30 -30
- package/templates/src/pages/VerifyEmailPage.tsx +6 -6
- package/templates/src/shared/config/navigation.ts +20 -20
- package/templates/src/shared/lib/auth.ts +20 -20
- package/templates/src/shared/types/auth.ts +3 -3
- package/templates/src/styles/xertica/tokens.css +39 -10
- package/templates/tsconfig.json +5 -5
- package/utils/gemini.ts +140 -140
- package/dist/VerifyEmailPage-Bae2cBXT.cjs +0 -2827
- package/dist/VerifyEmailPage-CbgjOF0v.js +0 -2828
- package/dist/index-CkTUgOwX.js +0 -8
|
@@ -6,6 +6,14 @@ The `Sidebar` is the primary vertical navigation panel of the application shell.
|
|
|
6
6
|
|
|
7
7
|
It is a responsive component that can be expanded or collapsed as an icon-only strip, fully integrated with the `LayoutContext` for state management.
|
|
8
8
|
|
|
9
|
+
The component ships with **three usage patterns** to cover different levels of customization:
|
|
10
|
+
|
|
11
|
+
| Pattern | When to use |
|
|
12
|
+
|---|---|
|
|
13
|
+
| **Monolithic API** (`<Sidebar />`) | Standard usage — pass props, get a fully-featured sidebar |
|
|
14
|
+
| **Compound Component API** (`<Sidebar.Root>` + sub-components) | Custom layouts — compose only the parts you need |
|
|
15
|
+
| **Headless Hook** (`useSidebar()`) | Full control — manage state yourself, render anything |
|
|
16
|
+
|
|
9
17
|
---
|
|
10
18
|
|
|
11
19
|
## Variants
|
|
@@ -36,6 +44,8 @@ Designed for complex tools, agents, or multi-context navigation.
|
|
|
36
44
|
|
|
37
45
|
## Props
|
|
38
46
|
|
|
47
|
+
### `<Sidebar />` (Monolithic API)
|
|
48
|
+
|
|
39
49
|
| Prop | Type | Default | Description |
|
|
40
50
|
|---|---|---|---|
|
|
41
51
|
| `expanded` | `boolean` | `auto` | Controlled state. Defaults to `LayoutContext` or local state. |
|
|
@@ -47,8 +57,45 @@ Designed for complex tools, agents, or multi-context navigation.
|
|
|
47
57
|
| `search` | `SidebarSearchConfig` | — | Search bar configuration (assistant variant). |
|
|
48
58
|
| `logo` | `ReactNode` | — | Logo shown when expanded. |
|
|
49
59
|
| `logoCollapsed` | `ReactNode` | — | Logo shown when collapsed. |
|
|
50
|
-
| `width` | `number` | `
|
|
60
|
+
| `width` | `number` | `280` | Expanded sidebar width in pixels. |
|
|
51
61
|
| `footer` | `SidebarFooterConfig` | — | Footer visibility flags (`showUser`, `showSettings`, `showLogout`). |
|
|
62
|
+
| `showFooter` | `boolean` | `true` for default, `false` for assistant | Whether to render the footer section. |
|
|
63
|
+
|
|
64
|
+
### `<Sidebar.Root />` (Compound Component)
|
|
65
|
+
|
|
66
|
+
| Prop | Type | Default | Description |
|
|
67
|
+
|---|---|---|---|
|
|
68
|
+
| `expanded` | `boolean` | `auto` | Controlled expansion state. |
|
|
69
|
+
| `onToggle` | `() => void` | `auto` | Toggle callback. |
|
|
70
|
+
| `navigate` | `(path: string) => void` | `window.location.href` | Navigation handler. |
|
|
71
|
+
| `location` | `{ pathname: string }` | `window.location` | Current location for active state. |
|
|
72
|
+
| `width` | `number` | `280` | Expanded width in pixels. |
|
|
73
|
+
| `children` | `ReactNode` | — | Sub-components: `Sidebar.Header`, `Sidebar.Nav`, `Sidebar.Footer`, `Sidebar.Search`. |
|
|
74
|
+
|
|
75
|
+
### `useSidebar()` (Headless Hook)
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { useSidebar } from 'xertica-ui/layout';
|
|
79
|
+
|
|
80
|
+
const {
|
|
81
|
+
expanded, // boolean — current expansion state
|
|
82
|
+
setExpanded, // (value: boolean | fn) => void
|
|
83
|
+
toggleExpanded, // () => void
|
|
84
|
+
isMobileViewport, // boolean
|
|
85
|
+
hasOverflow, // boolean — nav items overflow the container
|
|
86
|
+
visibleItems, // RouteGroup[] — items that fit in the nav
|
|
87
|
+
overflowItems, // RouteGroup[] — items that overflow
|
|
88
|
+
openSubmenu, // string | null
|
|
89
|
+
setOpenSubmenu, // (id: string | null) => void
|
|
90
|
+
isFilterOpen, // boolean
|
|
91
|
+
setIsFilterOpen, // (open: boolean) => void
|
|
92
|
+
navRef, // RefObject<HTMLElement> — attach to nav container
|
|
93
|
+
navigationGroups, // RouteGroup[] — the resolved groups passed in
|
|
94
|
+
} = useSidebar({
|
|
95
|
+
defaultExpanded: true,
|
|
96
|
+
navigationGroups: myGroups,
|
|
97
|
+
});
|
|
98
|
+
```
|
|
52
99
|
|
|
53
100
|
### RouteConfig
|
|
54
101
|
|
|
@@ -61,11 +108,21 @@ Designed for complex tools, agents, or multi-context navigation.
|
|
|
61
108
|
| `actions` | `ActionMenuItem[]` | Contextual action menu items (ellipsis button — assistant variant) |
|
|
62
109
|
| `description` | `ReactNode` | Auxiliary content shown when the route is active (assistant variant) |
|
|
63
110
|
|
|
111
|
+
### RouteGroup
|
|
112
|
+
|
|
113
|
+
| Field | Type | Description |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| `id` | `string` | Unique identifier |
|
|
116
|
+
| `label` | `string` | Group title |
|
|
117
|
+
| `icon` | `ComponentType` | Group icon |
|
|
118
|
+
| `items` | `RouteConfig[]` | Navigation items in the group |
|
|
119
|
+
| `actions` | `ActionMenuItem[]` | Context menu for the entire group |
|
|
120
|
+
|
|
64
121
|
---
|
|
65
122
|
|
|
66
123
|
## Examples
|
|
67
124
|
|
|
68
|
-
###
|
|
125
|
+
### 1. Monolithic API — Flat Routes
|
|
69
126
|
|
|
70
127
|
```tsx
|
|
71
128
|
import { Sidebar } from 'xertica-ui/layout';
|
|
@@ -94,7 +151,7 @@ export function MySidebar() {
|
|
|
94
151
|
}
|
|
95
152
|
```
|
|
96
153
|
|
|
97
|
-
###
|
|
154
|
+
### 2. Monolithic API — Groups with Children
|
|
98
155
|
|
|
99
156
|
```tsx
|
|
100
157
|
import { BarChart, Users, Settings, Home } from 'lucide-react';
|
|
@@ -129,15 +186,107 @@ import { BarChart, Users, Settings, Home } from 'lucide-react';
|
|
|
129
186
|
/>
|
|
130
187
|
```
|
|
131
188
|
|
|
189
|
+
### 3. Compound Component API
|
|
190
|
+
|
|
191
|
+
Use this when you need to inject custom content between sections or reorder the layout:
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
import { Sidebar } from 'xertica-ui/layout';
|
|
195
|
+
import { Home, BarChart } from 'lucide-react';
|
|
196
|
+
|
|
197
|
+
export function CustomSidebar({ expanded, onToggle }) {
|
|
198
|
+
return (
|
|
199
|
+
<Sidebar.Root
|
|
200
|
+
expanded={expanded}
|
|
201
|
+
onToggle={onToggle}
|
|
202
|
+
width={280}
|
|
203
|
+
location={{ pathname: window.location.pathname }}
|
|
204
|
+
navigate={(path) => window.location.href = path}
|
|
205
|
+
>
|
|
206
|
+
{/* Toggle button + logo */}
|
|
207
|
+
<Sidebar.Header logo={<MyLogo />} logoCollapsed={<MyIcon />} />
|
|
208
|
+
|
|
209
|
+
{/* Custom content injected between header and nav */}
|
|
210
|
+
<div className="px-4 py-2">
|
|
211
|
+
<span className="text-xs text-sidebar-foreground/60">v2.0.0</span>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
{/* Navigation */}
|
|
215
|
+
<Sidebar.Nav
|
|
216
|
+
navigationGroups={[
|
|
217
|
+
{
|
|
218
|
+
id: 'main',
|
|
219
|
+
items: [
|
|
220
|
+
{ path: '/home', label: 'Home', icon: Home },
|
|
221
|
+
{ path: '/dashboard', label: 'Dashboard', icon: BarChart },
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
]}
|
|
225
|
+
/>
|
|
226
|
+
|
|
227
|
+
{/* Footer */}
|
|
228
|
+
<Sidebar.Footer
|
|
229
|
+
user={{ name: 'Admin', email: 'admin@example.com' }}
|
|
230
|
+
onLogout={() => signOut()}
|
|
231
|
+
showUser
|
|
232
|
+
showSettings
|
|
233
|
+
showLogout
|
|
234
|
+
/>
|
|
235
|
+
</Sidebar.Root>
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### 4. Headless Hook — Fully Custom UI
|
|
241
|
+
|
|
242
|
+
Use `useSidebar` when you need complete control over the rendered output:
|
|
243
|
+
|
|
244
|
+
```tsx
|
|
245
|
+
import { useSidebar } from 'xertica-ui/layout';
|
|
246
|
+
import { Home, BarChart } from 'lucide-react';
|
|
247
|
+
|
|
248
|
+
const groups = [
|
|
249
|
+
{
|
|
250
|
+
id: 'main',
|
|
251
|
+
items: [
|
|
252
|
+
{ path: '/home', label: 'Home', icon: Home },
|
|
253
|
+
{ path: '/dashboard', label: 'Dashboard', icon: BarChart },
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
];
|
|
257
|
+
|
|
258
|
+
export function MyCustomSidebar() {
|
|
259
|
+
const { expanded, toggleExpanded, navigationGroups } = useSidebar({
|
|
260
|
+
defaultExpanded: true,
|
|
261
|
+
navigationGroups: groups,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<aside style={{ width: expanded ? 280 : 80 }}>
|
|
266
|
+
<button onClick={toggleExpanded}>Toggle</button>
|
|
267
|
+
<nav>
|
|
268
|
+
{navigationGroups.flatMap(g => g.items).map(item => (
|
|
269
|
+
<a key={item.path} href={item.path}>
|
|
270
|
+
{expanded ? item.label : null}
|
|
271
|
+
</a>
|
|
272
|
+
))}
|
|
273
|
+
</nav>
|
|
274
|
+
</aside>
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
132
279
|
---
|
|
133
280
|
|
|
134
281
|
## AI Rules
|
|
135
282
|
|
|
136
|
-
- **State Management** — Always use `useLayout()` to keep the Sidebar synchronized with the main content padding. Avoid managing `expanded` locally.
|
|
283
|
+
- **State Management** — Always use `useLayout()` to keep the Sidebar synchronized with the main content padding. Avoid managing `expanded` locally unless using the Headless Hook pattern.
|
|
137
284
|
- **Icons** — Pass the icon **component source** (e.g., `HomeIcon`), not a rendered element.
|
|
138
285
|
- **Groups** — Use `navigationGroups` in `default` variant for structured navigation with labels. Use `routes` for flat lists.
|
|
139
286
|
- **Children** — Add `children` to a `RouteConfig` to expose sub-routes via a ChevronRight button at the end of the item. Do not nest more than 2 levels.
|
|
140
287
|
- **Assistant Mode** — Use `fixedArea` to place high-priority "Create" buttons so they remain visible regardless of scrolling.
|
|
288
|
+
- **Compound Components** — Use `<Sidebar.Root>` + sub-components when you need to inject custom content between sections or reorder the layout.
|
|
289
|
+
- **Headless Hook** — Use `useSidebar()` only when you need a completely custom sidebar UI that shares none of the default rendering.
|
|
141
290
|
|
|
142
291
|
---
|
|
143
292
|
|
|
@@ -4,6 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
A visual multi-step progress indicator that shows the user's current position within a sequential process. Uses a composable primitive pattern: `<Stepper>` wraps one or more `<Step>` children, automatically inferring the total number of steps.
|
|
6
6
|
|
|
7
|
+
The package also exports a **headless `useStepper` hook** for building fully custom step-navigation UIs — including async validation guards before advancing — while reusing all the step-state logic.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Exports
|
|
12
|
+
|
|
13
|
+
| Export | Description |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `Stepper` | Visual step indicator component |
|
|
16
|
+
| `Step` | Individual step item |
|
|
17
|
+
| `useStepper` | Headless hook — all step navigation logic, no UI |
|
|
18
|
+
| `UseStepperProps` | TypeScript props type for the hook |
|
|
19
|
+
| `UseStepperReturn` | TypeScript return type for the hook |
|
|
20
|
+
|
|
7
21
|
---
|
|
8
22
|
|
|
9
23
|
## When to Use
|
|
@@ -43,6 +57,7 @@ A visual multi-step progress indicator that shows the user's current position wi
|
|
|
43
57
|
| Prop | Type | Required | Description |
|
|
44
58
|
|---|---|---|---|
|
|
45
59
|
| `currentStep` | `number` | **Yes** | The currently active step (1-indexed) |
|
|
60
|
+
| `orientation` | `'horizontal' \| 'vertical'` | No | Layout direction (default: `'horizontal'`) |
|
|
46
61
|
| `className` | `string` | No | Additional CSS classes |
|
|
47
62
|
|
|
48
63
|
### `Step`
|
|
@@ -52,16 +67,9 @@ A visual multi-step progress indicator that shows the user's current position wi
|
|
|
52
67
|
| `step` | `number` | **Yes** | Position of this step (1-indexed, must match order) |
|
|
53
68
|
| `label` | `string` | **Yes** | Step label text |
|
|
54
69
|
| `description` | `string` | No | Optional supplementary text below the label |
|
|
70
|
+
| `error` | `boolean` | No | When `true`, renders the step in error state (red) |
|
|
55
71
|
| `className` | `string` | No | Additional CSS classes |
|
|
56
72
|
|
|
57
|
-
### `useStepper()` (hook)
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
const { currentStep, totalSteps } = useStepper();
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
Access the stepper context from within a custom `Step` child component.
|
|
64
|
-
|
|
65
73
|
---
|
|
66
74
|
|
|
67
75
|
## Examples
|
|
@@ -97,7 +105,7 @@ const [currentStep, setCurrentStep] = useState(1);
|
|
|
97
105
|
onClick={() => setCurrentStep(s => Math.min(3, s + 1))}
|
|
98
106
|
disabled={currentStep === 3}
|
|
99
107
|
>
|
|
100
|
-
{currentStep ===
|
|
108
|
+
{currentStep === 3 ? 'Finish' : 'Next'}
|
|
101
109
|
</Button>
|
|
102
110
|
</div>
|
|
103
111
|
</div>
|
|
@@ -105,14 +113,151 @@ const [currentStep, setCurrentStep] = useState(1);
|
|
|
105
113
|
|
|
106
114
|
---
|
|
107
115
|
|
|
116
|
+
## `useStepper` Hook
|
|
117
|
+
|
|
118
|
+
A headless hook that manages step navigation state. Supports both **uncontrolled** (internal state) and **controlled** (external `step` prop) modes, plus an optional async `onBeforeNext` guard for validating the current step before advancing.
|
|
119
|
+
|
|
120
|
+
### Props
|
|
121
|
+
|
|
122
|
+
| Prop | Type | Default | Description |
|
|
123
|
+
|---|---|---|---|
|
|
124
|
+
| `totalSteps` | `number` | — | **Required.** Total number of steps in the flow |
|
|
125
|
+
| `initialStep` | `number` | `1` | Starting step (uncontrolled mode only) |
|
|
126
|
+
| `step` | `number` | — | Controlled current step — when provided, the hook uses this value instead of internal state |
|
|
127
|
+
| `onStepChange` | `(step: number) => void` | — | Called whenever the step changes |
|
|
128
|
+
| `onBeforeNext` | `(currentStep: number) => boolean \| Promise<boolean>` | — | Optional async guard — return `false` (or resolve to `false`) to block advancing to the next step |
|
|
129
|
+
|
|
130
|
+
### Return Value
|
|
131
|
+
|
|
132
|
+
| Property | Type | Description |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| `currentStep` | `number` | The currently active step (1-indexed) |
|
|
135
|
+
| `totalSteps` | `number` | Total number of steps |
|
|
136
|
+
| `isFirstStep` | `boolean` | Whether the current step is the first step |
|
|
137
|
+
| `isLastStep` | `boolean` | Whether the current step is the last step |
|
|
138
|
+
| `canGoPrev` | `boolean` | Whether navigating to the previous step is possible |
|
|
139
|
+
| `canGoNext` | `boolean` | Whether navigating to the next step is possible |
|
|
140
|
+
| `next` | `() => Promise<void>` | Advance to the next step (runs `onBeforeNext` guard if provided) |
|
|
141
|
+
| `prev` | `() => void` | Go back to the previous step |
|
|
142
|
+
| `goTo` | `(step: number) => void` | Jump to a specific step number |
|
|
143
|
+
| `reset` | `() => void` | Reset to the initial step |
|
|
144
|
+
|
|
145
|
+
### Uncontrolled Example
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
import { useStepper, Stepper, Step, Button } from 'xertica-ui/ui';
|
|
149
|
+
|
|
150
|
+
function OnboardingWizard() {
|
|
151
|
+
const { currentStep, isFirstStep, isLastStep, next, prev } = useStepper({
|
|
152
|
+
totalSteps: 3,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<div className="space-y-8">
|
|
157
|
+
<Stepper currentStep={currentStep}>
|
|
158
|
+
<Step step={1} label="Account" />
|
|
159
|
+
<Step step={2} label="Plan" />
|
|
160
|
+
<Step step={3} label="Confirm" />
|
|
161
|
+
</Stepper>
|
|
162
|
+
|
|
163
|
+
<div className="min-h-[200px]">
|
|
164
|
+
{currentStep === 1 && <AccountForm />}
|
|
165
|
+
{currentStep === 2 && <PlanSelector />}
|
|
166
|
+
{currentStep === 3 && <ConfirmationSummary />}
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<div className="flex justify-between">
|
|
170
|
+
<Button variant="outline" onClick={prev} disabled={isFirstStep}>
|
|
171
|
+
Previous
|
|
172
|
+
</Button>
|
|
173
|
+
<Button onClick={next} disabled={isLastStep}>
|
|
174
|
+
{isLastStep ? 'Finish' : 'Next'}
|
|
175
|
+
</Button>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### With Async Validation Guard
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
import { useStepper, Stepper, Step, Button } from 'xertica-ui/ui';
|
|
186
|
+
import { useForm } from 'react-hook-form';
|
|
187
|
+
|
|
188
|
+
function ValidatedWizard() {
|
|
189
|
+
const form = useForm();
|
|
190
|
+
|
|
191
|
+
const { currentStep, isFirstStep, isLastStep, next, prev } = useStepper({
|
|
192
|
+
totalSteps: 3,
|
|
193
|
+
onBeforeNext: async (step) => {
|
|
194
|
+
// Validate only the fields relevant to the current step
|
|
195
|
+
const fieldsPerStep: Record<number, string[]> = {
|
|
196
|
+
1: ['email', 'password'],
|
|
197
|
+
2: ['plan'],
|
|
198
|
+
};
|
|
199
|
+
const fields = fieldsPerStep[step];
|
|
200
|
+
if (!fields) return true;
|
|
201
|
+
return form.trigger(fields as never[]);
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
<div className="space-y-8">
|
|
207
|
+
<Stepper currentStep={currentStep}>
|
|
208
|
+
<Step step={1} label="Account" />
|
|
209
|
+
<Step step={2} label="Plan" />
|
|
210
|
+
<Step step={3} label="Confirm" />
|
|
211
|
+
</Stepper>
|
|
212
|
+
|
|
213
|
+
<form>{/* step content */}</form>
|
|
214
|
+
|
|
215
|
+
<div className="flex justify-between">
|
|
216
|
+
<Button variant="outline" onClick={prev} disabled={isFirstStep}>
|
|
217
|
+
Previous
|
|
218
|
+
</Button>
|
|
219
|
+
<Button onClick={next}>
|
|
220
|
+
{isLastStep ? 'Submit' : 'Next'}
|
|
221
|
+
</Button>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Controlled Example
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
import { useStepper } from 'xertica-ui/ui';
|
|
232
|
+
import { useState } from 'react';
|
|
233
|
+
|
|
234
|
+
function ControlledWizard() {
|
|
235
|
+
const [step, setStep] = useState(1);
|
|
236
|
+
|
|
237
|
+
const { currentStep, next, prev, isFirstStep, isLastStep } = useStepper({
|
|
238
|
+
totalSteps: 4,
|
|
239
|
+
step, // controlled
|
|
240
|
+
onStepChange: setStep,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// currentStep === step at all times
|
|
244
|
+
return (/* ... */);
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
108
250
|
## AI Rules
|
|
109
251
|
|
|
110
|
-
- `step` props are **1-indexed** — the first step is `step={1}`, not `step={0}`.
|
|
252
|
+
- `step` props on `<Step>` are **1-indexed** — the first step is `step={1}`, not `step={0}`.
|
|
111
253
|
- Each `<Step>` `step` prop must match its position sequentially: `1, 2, 3, ...`.
|
|
112
254
|
- `currentStep` on `<Stepper>` must be a number from `1` to the total number of steps.
|
|
113
255
|
- Always render the navigation buttons (Previous/Next) below the Stepper.
|
|
114
|
-
- Disable "Previous" on step `1` and "Next" on the final step.
|
|
115
|
-
-
|
|
256
|
+
- Disable "Previous" on step `1` (`isFirstStep`) and "Next" on the final step (`isLastStep`).
|
|
257
|
+
- Use `onBeforeNext` for async form validation — return `false` to block advancing.
|
|
258
|
+
- `next()` is **async** — always `await` it or use it as an `onClick` handler directly (React handles the Promise).
|
|
259
|
+
- In controlled mode, pass both `step` and `onStepChange` — omitting `onStepChange` makes the hook read-only.
|
|
260
|
+
- Use `reset()` to return to step 1 after a successful submission.
|
|
116
261
|
|
|
117
262
|
---
|
|
118
263
|
|
|
@@ -2,7 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
An interactive hierarchical tree component for displaying and navigating nested data structures — file systems, organizational charts, category trees, and recursive navigation menus.
|
|
5
|
+
An interactive hierarchical tree component for displaying and navigating nested data structures — file systems, organizational charts, category trees, and recursive navigation menus. Supports keyboard navigation (Arrow keys, Home, End, Space), expand/collapse, and single-node selection.
|
|
6
|
+
|
|
7
|
+
The package also exports a **headless `useTreeView` hook** for building fully custom tree UIs while reusing all the expand/collapse, selection, keyboard navigation, and focus management logic.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Exports
|
|
12
|
+
|
|
13
|
+
| Export | Description |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `TreeView` | Ready-to-use hierarchical tree component |
|
|
16
|
+
| `useTreeView` | Headless hook — all logic, no UI |
|
|
17
|
+
| `TreeNode` | TypeScript type for a single tree node |
|
|
18
|
+
| `UseTreeViewProps` | TypeScript props type for the hook |
|
|
19
|
+
| `UseTreeViewReturn` | TypeScript return type for the hook |
|
|
6
20
|
|
|
7
21
|
---
|
|
8
22
|
|
|
@@ -17,13 +31,18 @@ An interactive hierarchical tree component for displaying and navigating nested
|
|
|
17
31
|
|
|
18
32
|
## Props
|
|
19
33
|
|
|
34
|
+
### `TreeView`
|
|
35
|
+
|
|
20
36
|
| Prop | Type | Required | Description |
|
|
21
37
|
|---|---|---|---|
|
|
22
38
|
| `data` | `TreeNode[]` | **Yes** | The tree data structure |
|
|
23
|
-
| `
|
|
39
|
+
| `defaultExpanded` | `string[]` | No | Node IDs to expand on initial render |
|
|
40
|
+
| `selectedNodeId` | `string` | No | Controlled selected node ID |
|
|
41
|
+
| `onNodeClick` | `(node: TreeNode) => void` | No | Called when any node is clicked |
|
|
42
|
+
| `onNodeSelect` | `(node: TreeNode) => void` | No | Called when a node is selected (leaf or branch) |
|
|
24
43
|
| `className` | `string` | No | Additional CSS classes |
|
|
25
44
|
|
|
26
|
-
### TreeNode
|
|
45
|
+
### `TreeNode`
|
|
27
46
|
|
|
28
47
|
```typescript
|
|
29
48
|
interface TreeNode {
|
|
@@ -31,7 +50,6 @@ interface TreeNode {
|
|
|
31
50
|
label: string;
|
|
32
51
|
icon?: ReactNode;
|
|
33
52
|
children?: TreeNode[];
|
|
34
|
-
disabled?: boolean;
|
|
35
53
|
}
|
|
36
54
|
```
|
|
37
55
|
|
|
@@ -39,6 +57,8 @@ interface TreeNode {
|
|
|
39
57
|
|
|
40
58
|
## Examples
|
|
41
59
|
|
|
60
|
+
### File System Browser
|
|
61
|
+
|
|
42
62
|
```tsx
|
|
43
63
|
import { TreeView } from 'xertica-ui/ui';
|
|
44
64
|
import { Folder, File } from 'lucide-react';
|
|
@@ -65,14 +85,152 @@ const tree = [
|
|
|
65
85
|
|
|
66
86
|
<TreeView
|
|
67
87
|
data={tree}
|
|
68
|
-
|
|
88
|
+
onNodeSelect={(node) => console.log('Selected:', node.id)}
|
|
89
|
+
/>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Pre-expanded Nodes
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
<TreeView
|
|
96
|
+
data={tree}
|
|
97
|
+
defaultExpanded={['src', 'components']}
|
|
98
|
+
onNodeSelect={(node) => setSelectedFile(node.id)}
|
|
69
99
|
/>
|
|
70
100
|
```
|
|
71
101
|
|
|
72
102
|
---
|
|
73
103
|
|
|
104
|
+
## `useTreeView` Hook
|
|
105
|
+
|
|
106
|
+
A headless hook that manages expand/collapse state, selection, keyboard navigation, and focus refs for a tree structure. Use it when the `<TreeView>` component's visual design doesn't fit your needs.
|
|
107
|
+
|
|
108
|
+
### Props
|
|
109
|
+
|
|
110
|
+
| Prop | Type | Default | Description |
|
|
111
|
+
|---|---|---|---|
|
|
112
|
+
| `data` | `TreeNode[]` | — | **Required.** The tree data structure |
|
|
113
|
+
| `defaultExpanded` | `string[]` | `[]` | Node IDs to expand on initial render |
|
|
114
|
+
| `selectedNodeId` | `string` | — | Controlled selected node ID — when provided, the hook uses this value instead of internal state |
|
|
115
|
+
| `onNodeClick` | `(node: TreeNode) => void` | — | Called when any node is clicked |
|
|
116
|
+
| `onNodeSelect` | `(node: TreeNode) => void` | — | Called when a node is selected |
|
|
117
|
+
|
|
118
|
+
### Return Value
|
|
119
|
+
|
|
120
|
+
| Property | Type | Description |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `expanded` | `Set<string>` | Set of currently expanded node IDs |
|
|
123
|
+
| `effectiveSelectedId` | `string \| undefined` | The currently selected node ID (controlled or internal) |
|
|
124
|
+
| `nodeRefs` | `Map<string, HTMLButtonElement>` | Map of node ID → DOM button element refs |
|
|
125
|
+
| `getNodeRef` | `(nodeId: string) => (el: HTMLButtonElement \| null) => void` | Ref callback factory — attach to each node's button element |
|
|
126
|
+
| `toggleExpand` | `(nodeId: string) => void` | Toggle the expand/collapse state of a node |
|
|
127
|
+
| `handleSelect` | `(node: TreeNode) => void` | Handle node selection (updates internal state and calls callbacks) |
|
|
128
|
+
| `handleKeyDown` | `(e: KeyboardEvent, node: TreeNode) => void` | Full keyboard navigation handler — attach to each node's `onKeyDown` |
|
|
129
|
+
| `getVisibleNodes` | `() => TreeNode[]` | Returns the flat list of currently visible (non-collapsed) nodes in DOM order |
|
|
130
|
+
|
|
131
|
+
### Keyboard Navigation
|
|
132
|
+
|
|
133
|
+
The `handleKeyDown` handler implements the WAI-ARIA tree pattern:
|
|
134
|
+
|
|
135
|
+
| Key | Action |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `ArrowDown` | Move focus to the next visible node |
|
|
138
|
+
| `ArrowUp` | Move focus to the previous visible node |
|
|
139
|
+
| `ArrowRight` | Expand a collapsed branch node; or move to first child if already expanded |
|
|
140
|
+
| `ArrowLeft` | Collapse an expanded branch node; or move to parent if already collapsed |
|
|
141
|
+
| `Home` | Move focus to the first node in the tree |
|
|
142
|
+
| `End` | Move focus to the last visible node in the tree |
|
|
143
|
+
| `Space` | Select the focused node |
|
|
144
|
+
|
|
145
|
+
### Custom Tree Example
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
import { useTreeView, TreeNode } from 'xertica-ui/ui';
|
|
149
|
+
import { ChevronRight, ChevronDown, Folder, File } from 'lucide-react';
|
|
150
|
+
import { cn } from '@/shared/utils';
|
|
151
|
+
|
|
152
|
+
function CustomTree({ data }: { data: TreeNode[] }) {
|
|
153
|
+
const {
|
|
154
|
+
expanded,
|
|
155
|
+
effectiveSelectedId,
|
|
156
|
+
getNodeRef,
|
|
157
|
+
toggleExpand,
|
|
158
|
+
handleSelect,
|
|
159
|
+
handleKeyDown,
|
|
160
|
+
} = useTreeView({
|
|
161
|
+
data,
|
|
162
|
+
defaultExpanded: [],
|
|
163
|
+
onNodeSelect: (node) => console.log('Selected:', node.id),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const renderNode = (node: TreeNode, level: number): React.ReactNode => {
|
|
167
|
+
const hasChildren = !!node.children?.length;
|
|
168
|
+
const isExpanded = expanded.has(node.id);
|
|
169
|
+
const isSelected = effectiveSelectedId === node.id;
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<div key={node.id}>
|
|
173
|
+
<button
|
|
174
|
+
ref={getNodeRef(node.id)}
|
|
175
|
+
role="treeitem"
|
|
176
|
+
aria-expanded={hasChildren ? isExpanded : undefined}
|
|
177
|
+
aria-selected={isSelected}
|
|
178
|
+
onClick={() => {
|
|
179
|
+
if (hasChildren) toggleExpand(node.id);
|
|
180
|
+
handleSelect(node);
|
|
181
|
+
}}
|
|
182
|
+
onKeyDown={(e) => handleKeyDown(e, node)}
|
|
183
|
+
style={{ paddingLeft: `${level * 16}px` }}
|
|
184
|
+
className={cn(
|
|
185
|
+
"flex w-full items-center gap-2 rounded px-2 py-1 text-sm",
|
|
186
|
+
isSelected
|
|
187
|
+
? "bg-primary text-primary-foreground"
|
|
188
|
+
: "hover:bg-accent"
|
|
189
|
+
)}
|
|
190
|
+
>
|
|
191
|
+
{hasChildren ? (
|
|
192
|
+
isExpanded
|
|
193
|
+
? <ChevronDown className="size-4 shrink-0" />
|
|
194
|
+
: <ChevronRight className="size-4 shrink-0" />
|
|
195
|
+
) : (
|
|
196
|
+
<span className="size-4 shrink-0" />
|
|
197
|
+
)}
|
|
198
|
+
{node.icon}
|
|
199
|
+
<span>{node.label}</span>
|
|
200
|
+
</button>
|
|
201
|
+
|
|
202
|
+
{hasChildren && isExpanded && (
|
|
203
|
+
<div role="group">
|
|
204
|
+
{node.children!.map(child => renderNode(child, level + 1))}
|
|
205
|
+
</div>
|
|
206
|
+
)}
|
|
207
|
+
</div>
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
<div role="tree" className="w-full">
|
|
213
|
+
{data.map(node => renderNode(node, 0))}
|
|
214
|
+
</div>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
74
221
|
## AI Rules
|
|
75
222
|
|
|
76
|
-
- Each `TreeNode` must have a unique `id` — the component uses it to track expand/collapse state.
|
|
223
|
+
- Each `TreeNode` must have a unique `id` — the component uses it to track expand/collapse state and selection.
|
|
77
224
|
- Leaf nodes (no `children`) do not show expand arrows.
|
|
78
225
|
- Use `lucide-react` icons with `className="size-4"` consistently across all tree nodes.
|
|
226
|
+
- When using `useTreeView`, attach `getNodeRef(node.id)` as the `ref` on each node's button — keyboard navigation (`handleKeyDown`) requires DOM refs to move focus.
|
|
227
|
+
- Attach `handleKeyDown` to `onKeyDown` on every node button — omitting it breaks keyboard accessibility.
|
|
228
|
+
- `getVisibleNodes()` returns only nodes that are currently visible (not inside a collapsed branch) — use it to compute `ArrowDown`/`ArrowUp` targets.
|
|
229
|
+
- For controlled selection, pass `selectedNodeId` — the hook will use it as `effectiveSelectedId` instead of internal state.
|
|
230
|
+
- `expanded` is a `Set<string>` — use `expanded.has(nodeId)` to check if a node is expanded.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Related Components
|
|
235
|
+
|
|
236
|
+
- [`Sidebar`](./sidebar.md) — Uses TreeView-style navigation for nested menu items
|