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
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { useState, useCallback, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
// Types
|
|
5
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
/** A page item rendered in the pagination bar. */
|
|
8
|
+
export type PaginationPageItem =
|
|
9
|
+
| { type: 'page'; page: number }
|
|
10
|
+
| { type: 'ellipsis'; key: string };
|
|
11
|
+
|
|
12
|
+
export interface UsePaginationProps {
|
|
13
|
+
/** Total number of items in the data set. */
|
|
14
|
+
totalItems: number;
|
|
15
|
+
/** Number of items per page. @default 10 */
|
|
16
|
+
pageSize?: number;
|
|
17
|
+
/** Initial page (1-indexed). @default 1 */
|
|
18
|
+
initialPage?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Controlled current page (1-indexed).
|
|
21
|
+
* When provided, page state is managed externally via `onPageChange`.
|
|
22
|
+
*/
|
|
23
|
+
page?: number;
|
|
24
|
+
/** Called whenever the page changes. */
|
|
25
|
+
onPageChange?: (page: number) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Number of page buttons to show on each side of the current page.
|
|
28
|
+
* @default 1
|
|
29
|
+
*/
|
|
30
|
+
siblingCount?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface UsePaginationReturn {
|
|
34
|
+
// ── State ─────────────────────────────────────────────────────────────────
|
|
35
|
+
/** The currently active page (1-indexed). */
|
|
36
|
+
currentPage: number;
|
|
37
|
+
/** Total number of pages. */
|
|
38
|
+
totalPages: number;
|
|
39
|
+
/** Index of the first item on the current page (0-indexed, useful for slicing). */
|
|
40
|
+
startIndex: number;
|
|
41
|
+
/** Index of the last item on the current page (exclusive, useful for slicing). */
|
|
42
|
+
endIndex: number;
|
|
43
|
+
|
|
44
|
+
// ── Flags ─────────────────────────────────────────────────────────────────
|
|
45
|
+
/** Whether there is a previous page available. */
|
|
46
|
+
canGoPrev: boolean;
|
|
47
|
+
/** Whether there is a next page available. */
|
|
48
|
+
canGoNext: boolean;
|
|
49
|
+
/** Whether the current page is the first page. */
|
|
50
|
+
isFirstPage: boolean;
|
|
51
|
+
/** Whether the current page is the last page. */
|
|
52
|
+
isLastPage: boolean;
|
|
53
|
+
|
|
54
|
+
// ── Computed ──────────────────────────────────────────────────────────────
|
|
55
|
+
/**
|
|
56
|
+
* Ordered list of items to render in the pagination bar.
|
|
57
|
+
* Each item is either a `{ type: 'page', page: number }` or
|
|
58
|
+
* `{ type: 'ellipsis', key: string }`.
|
|
59
|
+
*/
|
|
60
|
+
items: PaginationPageItem[];
|
|
61
|
+
|
|
62
|
+
// ── Handlers ──────────────────────────────────────────────────────────────
|
|
63
|
+
/** Navigate to a specific page (1-indexed, clamped to valid range). */
|
|
64
|
+
goTo: (page: number) => void;
|
|
65
|
+
/** Navigate to the next page (no-op if already on the last page). */
|
|
66
|
+
next: () => void;
|
|
67
|
+
/** Navigate to the previous page (no-op if already on the first page). */
|
|
68
|
+
prev: () => void;
|
|
69
|
+
/** Navigate to the first page. */
|
|
70
|
+
first: () => void;
|
|
71
|
+
/** Navigate to the last page. */
|
|
72
|
+
last: () => void;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
76
|
+
// Hook
|
|
77
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Headless hook for pagination logic.
|
|
81
|
+
*
|
|
82
|
+
* @description
|
|
83
|
+
* Computes the current page, total pages, slice indices, and the ordered list
|
|
84
|
+
* of page items (numbers + ellipses) to render. Supports both controlled and
|
|
85
|
+
* uncontrolled modes. Pair with any custom pagination UI or with the
|
|
86
|
+
* `<Pagination>` visual components.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```tsx
|
|
90
|
+
* const { currentPage, totalPages, items, next, prev, goTo, canGoPrev, canGoNext } =
|
|
91
|
+
* usePagination({ totalItems: 200, pageSize: 20 });
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export function usePagination({
|
|
95
|
+
totalItems,
|
|
96
|
+
pageSize = 10,
|
|
97
|
+
initialPage = 1,
|
|
98
|
+
page: controlledPage,
|
|
99
|
+
onPageChange,
|
|
100
|
+
siblingCount = 1,
|
|
101
|
+
}: UsePaginationProps): UsePaginationReturn {
|
|
102
|
+
const [internalPage, setInternalPage] = useState(initialPage);
|
|
103
|
+
|
|
104
|
+
const isControlled = controlledPage !== undefined;
|
|
105
|
+
const currentPage = isControlled ? controlledPage! : internalPage;
|
|
106
|
+
|
|
107
|
+
const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
|
|
108
|
+
|
|
109
|
+
const setPage = useCallback(
|
|
110
|
+
(p: number) => {
|
|
111
|
+
const clamped = Math.min(Math.max(1, p), totalPages);
|
|
112
|
+
if (!isControlled) setInternalPage(clamped);
|
|
113
|
+
onPageChange?.(clamped);
|
|
114
|
+
},
|
|
115
|
+
[isControlled, onPageChange, totalPages]
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// ── Derived ───────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
const startIndex = (currentPage - 1) * pageSize;
|
|
121
|
+
const endIndex = Math.min(startIndex + pageSize, totalItems);
|
|
122
|
+
const canGoPrev = currentPage > 1;
|
|
123
|
+
const canGoNext = currentPage < totalPages;
|
|
124
|
+
const isFirstPage = currentPage === 1;
|
|
125
|
+
const isLastPage = currentPage === totalPages;
|
|
126
|
+
|
|
127
|
+
// ── Page items (numbers + ellipses) ───────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
const items = useMemo((): PaginationPageItem[] => {
|
|
130
|
+
// Always show first, last, current ± siblingCount
|
|
131
|
+
const range = (start: number, end: number): number[] =>
|
|
132
|
+
Array.from({ length: end - start + 1 }, (_, i) => start + i);
|
|
133
|
+
|
|
134
|
+
const leftSibling = Math.max(currentPage - siblingCount, 1);
|
|
135
|
+
const rightSibling = Math.min(currentPage + siblingCount, totalPages);
|
|
136
|
+
|
|
137
|
+
const showLeftEllipsis = leftSibling > 2;
|
|
138
|
+
const showRightEllipsis = rightSibling < totalPages - 1;
|
|
139
|
+
|
|
140
|
+
const result: PaginationPageItem[] = [];
|
|
141
|
+
|
|
142
|
+
// First page
|
|
143
|
+
result.push({ type: 'page', page: 1 });
|
|
144
|
+
|
|
145
|
+
if (showLeftEllipsis) {
|
|
146
|
+
result.push({ type: 'ellipsis', key: 'ellipsis-left' });
|
|
147
|
+
} else if (leftSibling === 2) {
|
|
148
|
+
result.push({ type: 'page', page: 2 });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Sibling range (excluding first and last)
|
|
152
|
+
for (const p of range(Math.max(leftSibling, 2), Math.min(rightSibling, totalPages - 1))) {
|
|
153
|
+
result.push({ type: 'page', page: p });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (showRightEllipsis) {
|
|
157
|
+
result.push({ type: 'ellipsis', key: 'ellipsis-right' });
|
|
158
|
+
} else if (rightSibling === totalPages - 1) {
|
|
159
|
+
result.push({ type: 'page', page: totalPages - 1 });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Last page (only if more than 1 page)
|
|
163
|
+
if (totalPages > 1) {
|
|
164
|
+
result.push({ type: 'page', page: totalPages });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return result;
|
|
168
|
+
}, [currentPage, siblingCount, totalPages]);
|
|
169
|
+
|
|
170
|
+
// ── Handlers ──────────────────────────────────────────────────────────────
|
|
171
|
+
|
|
172
|
+
const goTo = useCallback((p: number) => setPage(p), [setPage]);
|
|
173
|
+
const next = useCallback(() => setPage(currentPage + 1), [currentPage, setPage]);
|
|
174
|
+
const prev = useCallback(() => setPage(currentPage - 1), [currentPage, setPage]);
|
|
175
|
+
const first = useCallback(() => setPage(1), [setPage]);
|
|
176
|
+
const last = useCallback(() => setPage(totalPages), [setPage, totalPages]);
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
currentPage,
|
|
180
|
+
totalPages,
|
|
181
|
+
startIndex,
|
|
182
|
+
endIndex,
|
|
183
|
+
canGoPrev,
|
|
184
|
+
canGoNext,
|
|
185
|
+
isFirstPage,
|
|
186
|
+
isLastPage,
|
|
187
|
+
items,
|
|
188
|
+
goTo,
|
|
189
|
+
next,
|
|
190
|
+
prev,
|
|
191
|
+
first,
|
|
192
|
+
last,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
@@ -10,14 +10,14 @@ import * as PopoverStories from './popover.stories';
|
|
|
10
10
|
|
|
11
11
|
<Primary />
|
|
12
12
|
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Variations
|
|
16
|
-
|
|
17
|
-
<Stories />
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Variations
|
|
16
|
+
|
|
17
|
+
<Stories />
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
21
|
## AI Best Practices
|
|
22
22
|
|
|
23
23
|
> [!IMPORTANT]
|
|
@@ -10,14 +10,14 @@ import * as ProgressStories from './progress.stories';
|
|
|
10
10
|
|
|
11
11
|
<Primary />
|
|
12
12
|
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Variations
|
|
16
|
-
|
|
17
|
-
<Stories />
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Variations
|
|
16
|
+
|
|
17
|
+
<Stories />
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
21
|
## AI Best Practices
|
|
22
22
|
|
|
23
23
|
> [!IMPORTANT]
|
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import * as ProgressPrimitive from "@radix-ui/react-progress";
|
|
5
|
-
|
|
6
|
-
import { cn } from "../../shared/utils";
|
|
7
|
-
|
|
8
|
-
type ProgressVariant = "default" | "success" | "info" | "warning" | "destructive";
|
|
9
|
-
|
|
10
|
-
const trackVariants: Record<ProgressVariant, string> = {
|
|
11
|
-
default: "bg-primary/20",
|
|
12
|
-
success: "bg-success/20",
|
|
13
|
-
info: "bg-info/20",
|
|
14
|
-
warning: "bg-warning/20",
|
|
15
|
-
destructive: "bg-destructive/20",
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const indicatorVariants: Record<ProgressVariant, string> = {
|
|
19
|
-
default: "bg-primary",
|
|
20
|
-
success: "bg-success",
|
|
21
|
-
info: "bg-info",
|
|
22
|
-
warning: "bg-warning",
|
|
23
|
-
destructive: "bg-destructive",
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Numeric progress bar from 0 to 100.
|
|
28
|
-
*
|
|
29
|
-
* @description
|
|
30
|
-
* Indicates deterministic task progress (file upload %, form completion steps, etc.).
|
|
31
|
-
* Built on Radix UI Progress with smooth CSS transition.
|
|
32
|
-
*
|
|
33
|
-
* @ai-rules
|
|
34
|
-
* 1. Control progress via the `value` prop (e.g., `value={66}`).
|
|
35
|
-
* 2. Use `variant` to communicate semantic state: `success`, `info`, `warning`, `destructive`.
|
|
36
|
-
* 3. For indeterminate / infinite loading, use `<Loader2 className="animate-spin" />` — not `<Progress>`.
|
|
37
|
-
* 4. Available variants: `default`, `success`, `info`, `warning`, `destructive`.
|
|
38
|
-
*/
|
|
39
|
-
function Progress({
|
|
40
|
-
className,
|
|
41
|
-
value,
|
|
42
|
-
variant = "default",
|
|
43
|
-
...props
|
|
44
|
-
}: React.ComponentProps<typeof ProgressPrimitive.Root> & {
|
|
45
|
-
variant?: ProgressVariant;
|
|
46
|
-
}) {
|
|
47
|
-
return (
|
|
48
|
-
<ProgressPrimitive.Root
|
|
49
|
-
data-slot="progress"
|
|
50
|
-
aria-label={props["aria-label"] || "Progresso"}
|
|
51
|
-
className={cn(
|
|
52
|
-
"relative h-2 w-full overflow-hidden rounded-full",
|
|
53
|
-
trackVariants[variant],
|
|
54
|
-
className,
|
|
55
|
-
)}
|
|
56
|
-
{...props}
|
|
57
|
-
>
|
|
58
|
-
<ProgressPrimitive.Indicator
|
|
59
|
-
data-slot="progress-indicator"
|
|
60
|
-
className={cn("h-full w-full flex-1 transition-all", indicatorVariants[variant])}
|
|
61
|
-
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
|
62
|
-
/>
|
|
63
|
-
</ProgressPrimitive.Root>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export { Progress };
|
|
68
|
-
export type { ProgressVariant };
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as ProgressPrimitive from "@radix-ui/react-progress";
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../shared/utils";
|
|
7
|
+
|
|
8
|
+
type ProgressVariant = "default" | "success" | "info" | "warning" | "destructive";
|
|
9
|
+
|
|
10
|
+
const trackVariants: Record<ProgressVariant, string> = {
|
|
11
|
+
default: "bg-primary/20",
|
|
12
|
+
success: "bg-success/20",
|
|
13
|
+
info: "bg-info/20",
|
|
14
|
+
warning: "bg-warning/20",
|
|
15
|
+
destructive: "bg-destructive/20",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const indicatorVariants: Record<ProgressVariant, string> = {
|
|
19
|
+
default: "bg-primary",
|
|
20
|
+
success: "bg-success",
|
|
21
|
+
info: "bg-info",
|
|
22
|
+
warning: "bg-warning",
|
|
23
|
+
destructive: "bg-destructive",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Numeric progress bar from 0 to 100.
|
|
28
|
+
*
|
|
29
|
+
* @description
|
|
30
|
+
* Indicates deterministic task progress (file upload %, form completion steps, etc.).
|
|
31
|
+
* Built on Radix UI Progress with smooth CSS transition.
|
|
32
|
+
*
|
|
33
|
+
* @ai-rules
|
|
34
|
+
* 1. Control progress via the `value` prop (e.g., `value={66}`).
|
|
35
|
+
* 2. Use `variant` to communicate semantic state: `success`, `info`, `warning`, `destructive`.
|
|
36
|
+
* 3. For indeterminate / infinite loading, use `<Loader2 className="animate-spin" />` — not `<Progress>`.
|
|
37
|
+
* 4. Available variants: `default`, `success`, `info`, `warning`, `destructive`.
|
|
38
|
+
*/
|
|
39
|
+
function Progress({
|
|
40
|
+
className,
|
|
41
|
+
value,
|
|
42
|
+
variant = "default",
|
|
43
|
+
...props
|
|
44
|
+
}: React.ComponentProps<typeof ProgressPrimitive.Root> & {
|
|
45
|
+
variant?: ProgressVariant;
|
|
46
|
+
}) {
|
|
47
|
+
return (
|
|
48
|
+
<ProgressPrimitive.Root
|
|
49
|
+
data-slot="progress"
|
|
50
|
+
aria-label={props["aria-label"] || "Progresso"}
|
|
51
|
+
className={cn(
|
|
52
|
+
"relative h-2 w-full overflow-hidden rounded-full",
|
|
53
|
+
trackVariants[variant],
|
|
54
|
+
className,
|
|
55
|
+
)}
|
|
56
|
+
{...props}
|
|
57
|
+
>
|
|
58
|
+
<ProgressPrimitive.Indicator
|
|
59
|
+
data-slot="progress-indicator"
|
|
60
|
+
className={cn("h-full w-full flex-1 transition-all", indicatorVariants[variant])}
|
|
61
|
+
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
|
62
|
+
/>
|
|
63
|
+
</ProgressPrimitive.Root>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { Progress };
|
|
68
|
+
export type { ProgressVariant };
|
|
@@ -10,14 +10,14 @@ import * as RadioGroupStories from './radio-group.stories';
|
|
|
10
10
|
|
|
11
11
|
<Primary />
|
|
12
12
|
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Variations
|
|
16
|
-
|
|
17
|
-
<Stories />
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Variations
|
|
16
|
+
|
|
17
|
+
<Stories />
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
21
|
## AI Best Practices
|
|
22
22
|
|
|
23
23
|
> [!IMPORTANT]
|
|
@@ -10,14 +10,14 @@ import * as RatingStories from './rating.stories';
|
|
|
10
10
|
|
|
11
11
|
<Primary />
|
|
12
12
|
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Variations
|
|
16
|
-
|
|
17
|
-
<Stories />
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Variations
|
|
16
|
+
|
|
17
|
+
<Stories />
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
21
|
## AI Best Practices
|
|
22
22
|
|
|
23
23
|
> [!IMPORTANT]
|
|
@@ -10,14 +10,14 @@ import * as ResizableStories from './resizable.stories';
|
|
|
10
10
|
|
|
11
11
|
<Primary />
|
|
12
12
|
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Variations
|
|
16
|
-
|
|
17
|
-
<Stories />
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Variations
|
|
16
|
+
|
|
17
|
+
<Stories />
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
21
|
## AI Best Practices
|
|
22
22
|
|
|
23
23
|
> [!IMPORTANT]
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { RichTextEditor } from './rich-text-editor';
|
|
3
|
+
import { useRichTextEditor } from './use-rich-text-editor';
|
|
3
4
|
import { Button } from '../button';
|
|
4
|
-
import { Check,
|
|
5
|
+
import { Check, Bold, Italic, Underline, Link, Unlink, Search } from 'lucide-react';
|
|
5
6
|
import React, { useState } from 'react';
|
|
6
7
|
|
|
7
8
|
const meta: Meta<typeof RichTextEditor> = {
|
|
@@ -132,3 +133,130 @@ export const Empty: Story = {
|
|
|
132
133
|
),
|
|
133
134
|
};
|
|
134
135
|
|
|
136
|
+
/**
|
|
137
|
+
* **Headless Hook** — demonstrates `useRichTextEditor()` with a fully custom UI.
|
|
138
|
+
*
|
|
139
|
+
* The hook manages all editor state (active formats, search, link popover,
|
|
140
|
+
* word/character counts) while you supply your own markup and toolbar.
|
|
141
|
+
* This story renders a minimal custom toolbar + `contenteditable` div
|
|
142
|
+
* without using `<RichTextEditor>` at all.
|
|
143
|
+
*/
|
|
144
|
+
export const HeadlessHook: Story = {
|
|
145
|
+
render: () => {
|
|
146
|
+
const [value, setValue] = useState(
|
|
147
|
+
'<p>Edit this text using the <strong>custom toolbar</strong> below.</p>'
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const {
|
|
151
|
+
editorRef,
|
|
152
|
+
linkInputRef,
|
|
153
|
+
activeFormats,
|
|
154
|
+
isLinkOpen,
|
|
155
|
+
linkUrl,
|
|
156
|
+
setLinkUrl,
|
|
157
|
+
wordCount,
|
|
158
|
+
characterCount,
|
|
159
|
+
execCommand,
|
|
160
|
+
handleInput,
|
|
161
|
+
updateActiveFormats,
|
|
162
|
+
handleCreateLink,
|
|
163
|
+
handleUnlink,
|
|
164
|
+
onLinkPopoverOpenChange,
|
|
165
|
+
} = useRichTextEditor({ value, onChange: setValue });
|
|
166
|
+
|
|
167
|
+
const btnClass = (active: boolean) =>
|
|
168
|
+
`px-2 py-1 rounded text-sm border transition-colors ${active
|
|
169
|
+
? 'bg-primary text-primary-foreground border-primary'
|
|
170
|
+
: 'bg-background text-foreground border-border hover:bg-muted'
|
|
171
|
+
}`;
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<div className="w-full max-w-2xl mx-auto p-6 space-y-4">
|
|
175
|
+
<div className="text-sm text-muted-foreground font-medium">
|
|
176
|
+
Custom headless editor — built with <code className="bg-muted px-1 rounded">useRichTextEditor()</code>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
{/* Custom toolbar */}
|
|
180
|
+
<div className="flex items-center gap-1 p-2 border border-border rounded-lg bg-muted/30 flex-wrap">
|
|
181
|
+
<button
|
|
182
|
+
className={btnClass(activeFormats.bold)}
|
|
183
|
+
onMouseDown={(e) => { e.preventDefault(); execCommand('bold'); }}
|
|
184
|
+
title="Bold"
|
|
185
|
+
>
|
|
186
|
+
<Bold className="w-4 h-4" />
|
|
187
|
+
</button>
|
|
188
|
+
<button
|
|
189
|
+
className={btnClass(activeFormats.italic)}
|
|
190
|
+
onMouseDown={(e) => { e.preventDefault(); execCommand('italic'); }}
|
|
191
|
+
title="Italic"
|
|
192
|
+
>
|
|
193
|
+
<Italic className="w-4 h-4" />
|
|
194
|
+
</button>
|
|
195
|
+
<button
|
|
196
|
+
className={btnClass(activeFormats.underline)}
|
|
197
|
+
onMouseDown={(e) => { e.preventDefault(); execCommand('underline'); }}
|
|
198
|
+
title="Underline"
|
|
199
|
+
>
|
|
200
|
+
<Underline className="w-4 h-4" />
|
|
201
|
+
</button>
|
|
202
|
+
|
|
203
|
+
<div className="w-px h-5 bg-border mx-1" />
|
|
204
|
+
|
|
205
|
+
{/* Link controls */}
|
|
206
|
+
<div className="relative flex items-center gap-1">
|
|
207
|
+
<button
|
|
208
|
+
className={btnClass(isLinkOpen || activeFormats.link)}
|
|
209
|
+
onMouseDown={(e) => { e.preventDefault(); onLinkPopoverOpenChange(!isLinkOpen); }}
|
|
210
|
+
title="Insert link"
|
|
211
|
+
>
|
|
212
|
+
<Link className="w-4 h-4" />
|
|
213
|
+
</button>
|
|
214
|
+
{activeFormats.link && (
|
|
215
|
+
<button
|
|
216
|
+
className={btnClass(false)}
|
|
217
|
+
onMouseDown={(e) => { e.preventDefault(); handleUnlink(); }}
|
|
218
|
+
title="Remove link"
|
|
219
|
+
>
|
|
220
|
+
<Unlink className="w-4 h-4" />
|
|
221
|
+
</button>
|
|
222
|
+
)}
|
|
223
|
+
{isLinkOpen && (
|
|
224
|
+
<div className="absolute top-full left-0 mt-1 z-50 bg-popover border border-border rounded-lg shadow-lg p-3 flex gap-2 w-72">
|
|
225
|
+
<input
|
|
226
|
+
ref={linkInputRef}
|
|
227
|
+
type="url"
|
|
228
|
+
value={linkUrl}
|
|
229
|
+
onChange={(e) => setLinkUrl(e.target.value)}
|
|
230
|
+
placeholder="https://example.com"
|
|
231
|
+
className="flex-1 text-sm border border-border rounded px-2 py-1 bg-background"
|
|
232
|
+
onKeyDown={(e) => { if (e.key === 'Enter') handleCreateLink(); }}
|
|
233
|
+
/>
|
|
234
|
+
<Button size="sm" onClick={handleCreateLink}>Apply</Button>
|
|
235
|
+
</div>
|
|
236
|
+
)}
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
{/* Custom contenteditable editor */}
|
|
241
|
+
<div
|
|
242
|
+
ref={editorRef}
|
|
243
|
+
contentEditable
|
|
244
|
+
suppressContentEditableWarning
|
|
245
|
+
onInput={handleInput}
|
|
246
|
+
onKeyUp={updateActiveFormats}
|
|
247
|
+
onMouseUp={updateActiveFormats}
|
|
248
|
+
className="min-h-[200px] p-4 border border-border rounded-lg bg-background focus:outline-none focus:ring-2 focus:ring-ring text-sm"
|
|
249
|
+
style={{ lineHeight: 1.6 }}
|
|
250
|
+
dangerouslySetInnerHTML={{ __html: value }}
|
|
251
|
+
/>
|
|
252
|
+
|
|
253
|
+
{/* Custom footer with counts */}
|
|
254
|
+
<div className="flex items-center justify-between text-xs text-muted-foreground px-1">
|
|
255
|
+
<span>Words: <strong>{wordCount}</strong></span>
|
|
256
|
+
<span>Characters: <strong>{characterCount}</strong></span>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
);
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
|