@k3-universe/react-kit 0.0.1 → 0.0.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/.storybook/preview.ts +1 -1
- package/dist/index.d.ts +63 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +91 -0
- package/dist/kit/builder/data-table/components/DataTable.d.ts +4 -1
- package/dist/kit/builder/data-table/components/DataTable.d.ts.map +1 -1
- package/dist/kit/builder/data-table/index.d.ts +6 -5
- package/dist/kit/builder/data-table/index.d.ts.map +1 -1
- package/dist/kit/builder/form/components/FormBuilder.d.ts +5 -2
- package/dist/kit/builder/form/components/FormBuilder.d.ts.map +1 -1
- package/dist/kit/builder/form/components/{FormActions.d.ts → FormBuilderActions.d.ts} +3 -3
- package/dist/kit/builder/form/components/FormBuilderActions.d.ts.map +1 -0
- package/{storybook-static/kit/builder/form/components/FormField.d.ts → dist/kit/builder/form/components/FormBuilderField.d.ts} +4 -4
- package/dist/kit/builder/form/components/FormBuilderField.d.ts.map +1 -0
- package/dist/kit/builder/form/components/index.d.ts +2 -3
- package/dist/kit/builder/form/components/index.d.ts.map +1 -1
- package/dist/kit/builder/form/utils/field-factories.d.ts +1 -0
- package/dist/kit/builder/form/utils/field-factories.d.ts.map +1 -1
- package/dist/kit/builder/section/SectionBuilder.d.ts.map +1 -1
- package/dist/kit/components/autocomplete/types.d.ts +1 -1
- package/dist/kit/components/autocomplete/types.d.ts.map +1 -1
- package/dist/kit/layouts/admin/components/AdminLayout.d.ts.map +1 -1
- package/dist/kit/layouts/admin/hooks/menu.d.ts +1 -0
- package/dist/kit/layouts/admin/hooks/menu.d.ts.map +1 -1
- package/dist/kit/providers/ThemeProvider.d.ts.map +1 -1
- package/dist/kit/themes/base.css +1 -0
- package/dist/kit/themes/clean-slate.css +1 -0
- package/dist/kit/themes/default.css +1 -0
- package/dist/kit/themes/minimal-modern.css +1 -0
- package/dist/kit/themes/spotify.css +1 -0
- package/package.json +11 -10
- package/src/index.ts +88 -0
- package/src/kit/builder/data-table/components/DataTable.tsx +63 -23
- package/src/kit/builder/data-table/index.ts +6 -5
- package/src/kit/builder/form/components/FormBuilder.tsx +37 -10
- package/src/kit/builder/form/components/{FormActions.tsx → FormBuilderActions.tsx} +3 -3
- package/src/kit/builder/form/components/{FormField.tsx → FormBuilderField.tsx} +131 -9
- package/src/kit/builder/form/components/index.ts +2 -3
- package/src/kit/builder/form/utils/field-factories.ts +13 -0
- package/src/kit/builder/section/SectionBuilder.tsx +67 -10
- package/src/kit/components/autocomplete/types.ts +1 -1
- package/src/kit/layouts/admin/components/AdminLayout.tsx +1 -2
- package/src/kit/layouts/admin/hooks/menu.ts +2 -2
- package/src/kit/providers/ThemeProvider.tsx +38 -38
- package/src/stories/kit/builder/Form.Basic.stories.tsx +2 -0
- package/src/stories/kit/builder/Form.Simple.stories.tsx +4 -0
- package/src/stories/kit/layouts/admin/AdminLayout.Basic.stories.tsx +2 -0
- package/src/stories/kit/layouts/admin/AdminLayout.Collapsible.stories.tsx +2 -0
- package/src/stories/kit/layouts/admin/AdminLayout.Complex.stories.tsx +2 -0
- package/src/stories/kit/layouts/admin/AdminLayout.CustomSidebarHeaderComponent.stories.tsx +2 -0
- package/src/stories/kit/layouts/admin/AdminLayout.CustomSidebarTitleAndIcon.stories.tsx +2 -0
- package/src/stories/kit/layouts/admin/AdminLayout.HeaderSlots.stories.tsx +2 -0
- package/storybook-static/assets/{Accordion.stories-N1auke5L.js → Accordion.stories-q6yg6wg1.js} +1 -1
- package/storybook-static/assets/{AdminLayout-Cxd0-A_L.js → AdminLayout-B9bV4J_6.js} +1 -1
- package/storybook-static/assets/{AdminLayout.Basic.stories-C_I6rWuq.js → AdminLayout.Basic.stories-C-ZxuH-O.js} +1 -1
- package/storybook-static/assets/AdminLayout.Collapsible.stories-xcQzkxio.js +4 -0
- package/storybook-static/assets/{AdminLayout.Complex.stories-BbY1Ue6d.js → AdminLayout.Complex.stories-DyjkVpvE.js} +1 -1
- package/storybook-static/assets/{AdminLayout.CustomSidebarHeaderComponent.stories-LFVi-aIv.js → AdminLayout.CustomSidebarHeaderComponent.stories-BqhzWCIA.js} +1 -1
- package/storybook-static/assets/AdminLayout.CustomSidebarTitleAndIcon.stories-aWxyR67T.js +4 -0
- package/storybook-static/assets/{AdminLayout.HeaderSlots.stories-BactzgkF.js → AdminLayout.HeaderSlots.stories-dNzhUdDt.js} +1 -1
- package/storybook-static/assets/{Alert.stories-_FtuixdR.js → Alert.stories-DXwNfJ3w.js} +1 -1
- package/storybook-static/assets/{AlertDialog.stories-6NY3BjwR.js → AlertDialog.stories-I324NsnP.js} +1 -1
- package/storybook-static/assets/{AspectRatio.stories-DAT11y02.js → AspectRatio.stories-CghHiX3Z.js} +1 -1
- package/storybook-static/assets/{Autocomplete-DngjqsCM.js → Autocomplete-B4gV705L.js} +1 -1
- package/storybook-static/assets/{Autocomplete.stories-BCaOrgTR.js → Autocomplete.stories-CyOvjTGN.js} +1 -1
- package/storybook-static/assets/{Avatar.stories-DlzxSLJ-.js → Avatar.stories-CEF5FVSR.js} +1 -1
- package/storybook-static/assets/{Badge.stories-DKHXsoV5.js → Badge.stories-_-G3GriP.js} +1 -1
- package/storybook-static/assets/{Breadcrumb.stories-D8eUHpf7.js → Breadcrumb.stories-DORe9T4b.js} +1 -1
- package/storybook-static/assets/{Button.stories-kvPdKxdw.js → Button.stories-3nQ6wsBX.js} +1 -1
- package/storybook-static/assets/{Calendar.stories-D-hUtQQI.js → Calendar.stories-Dn__djE1.js} +1 -1
- package/storybook-static/assets/{Card.stories-y8Z7NgrQ.js → Card.stories-BJcbdwCI.js} +1 -1
- package/storybook-static/assets/{Carousel.stories-CFcHYKgp.js → Carousel.stories-CfqbE7Va.js} +1 -1
- package/storybook-static/assets/{Chart.stories-BJwLNKfE.js → Chart.stories-cGgef3tv.js} +1 -1
- package/storybook-static/assets/{Checkbox.stories-B22Fbwx1.js → Checkbox.stories-BcaxWzCS.js} +1 -1
- package/storybook-static/assets/{Collapsible.stories-BgcRp8Rk.js → Collapsible.stories-CQ95s7Cs.js} +1 -1
- package/storybook-static/assets/{Combination-BT_gXe1L.js → Combination-CeVus13L.js} +1 -1
- package/storybook-static/assets/{Command.stories-Dp4CtSQR.js → Command.stories-DYflJh8u.js} +1 -1
- package/storybook-static/assets/{ContextMenu.stories-DnVnIi-T.js → ContextMenu.stories-CU7ehYrE.js} +1 -1
- package/storybook-static/assets/DataTable.Basic.stories-BQNWUKiw.js +6 -0
- package/storybook-static/assets/{DataTable.Filters.stories-CO0ipuGm.js → DataTable.Filters.stories-DPCoeYhK.js} +1 -1
- package/storybook-static/assets/{DataTable.Pagination.stories-DNwCw64e.js → DataTable.Pagination.stories-8UNqTNgw.js} +1 -1
- package/storybook-static/assets/{DataTable.SelectionAndActions.stories-DGgdEbsf.js → DataTable.SelectionAndActions.stories-CDEjgQ6T.js} +1 -1
- package/storybook-static/assets/DataTable.Sorting.stories-qG7-X_6r.js +6 -0
- package/storybook-static/assets/{Dialog.stories-DQkYN8ht.js → Dialog.stories-Bu6ZJXNH.js} +1 -1
- package/storybook-static/assets/{Dialog.stories-DXZINEgo.js → Dialog.stories-DobNZ0Hp.js} +1 -1
- package/storybook-static/assets/{Drawer.stories-DMH_vZgi.js → Drawer.stories-BN0idp4u.js} +1 -1
- package/storybook-static/assets/{DropdownMenu.stories-Dl8y-N7T.js → DropdownMenu.stories-sfsVDTvm.js} +1 -1
- package/storybook-static/assets/{Form.Basic.stories-CJugIYsx.js → Form.Basic.stories-dE4nbgpr.js} +1 -1
- package/storybook-static/assets/{Form.Complex.stories-BPogRp6F.js → Form.Complex.stories-BEZufnjb.js} +1 -1
- package/storybook-static/assets/{Form.Dynamic.stories-Boway5AK.js → Form.Dynamic.stories-cJTbd6cV.js} +1 -1
- package/storybook-static/assets/{Form.Simple.stories-_OXGXXpE.js → Form.Simple.stories-DOPzZKhh.js} +1 -1
- package/storybook-static/assets/{Form.stories-BoHyR8ho.js → Form.stories-27doU7JS.js} +1 -1
- package/storybook-static/assets/{FormBuilder-BGgeY8ik.js → FormBuilder-C0S7C69Q.js} +1 -1
- package/storybook-static/assets/{HoverCard.stories-1HM92M79.js → HoverCard.stories-0lted9Zx.js} +1 -1
- package/storybook-static/assets/{Input.stories-sQ4EUlWt.js → Input.stories-CwsIObFs.js} +1 -1
- package/storybook-static/assets/{InputOtp.stories-Cpk_EmLx.js → InputOtp.stories-DVc086h4.js} +1 -1
- package/storybook-static/assets/{Label.stories-MjABHKSw.js → Label.stories-bJa0rk1A.js} +1 -1
- package/storybook-static/assets/{Login.stories-BY4OYGk3.js → Login.stories-C6X6Kj5B.js} +1 -1
- package/storybook-static/assets/{Menubar.stories-B_60TWNE.js → Menubar.stories-CuediVp7.js} +1 -1
- package/storybook-static/assets/{NavigationMenu.stories-DyuaBvvq.js → NavigationMenu.stories-CKGZYhKk.js} +1 -1
- package/storybook-static/assets/{Page.stories-DQKMxrPZ.js → Page.stories-Cf76wtRx.js} +1 -1
- package/storybook-static/assets/{Pagination.stories-CBo7UrNd.js → Pagination.stories-D7IdL1_4.js} +1 -1
- package/storybook-static/assets/{Popover.stories-D98_JrxN.js → Popover.stories-1VP_zNY8.js} +1 -1
- package/storybook-static/assets/{Progress.stories-Dejt5v3z.js → Progress.stories-C35R5YvA.js} +1 -1
- package/storybook-static/assets/{RadioGroup.stories-Tz2LW6O6.js → RadioGroup.stories-CjuD3CwD.js} +1 -1
- package/storybook-static/assets/{Resizable.stories-DhP2GKN7.js → Resizable.stories-Cs8dvdc5.js} +1 -1
- package/storybook-static/assets/{ScrollArea.stories-CF7-8wbM.js → ScrollArea.stories-D6_z-5jm.js} +1 -1
- package/storybook-static/assets/{Section.stories-BpurjHIQ.js → Section.stories-Ce5qYITI.js} +1 -1
- package/storybook-static/assets/SectionBuilder-Df_lRZkj.js +1 -0
- package/storybook-static/assets/{Select.stories-DJrU-mi0.js → Select.stories-w_kbza5k.js} +1 -1
- package/storybook-static/assets/{Separator.stories-BYH_o8QM.js → Separator.stories-B5QnaJXb.js} +1 -1
- package/storybook-static/assets/{Sheet.stories-BNJ7BIb-.js → Sheet.stories-CORnEGvV.js} +1 -1
- package/storybook-static/assets/{Sidebar.stories-BbjcJuOg.js → Sidebar.stories-DVuWTi4j.js} +1 -1
- package/storybook-static/assets/{Slider.stories-CWOjKHKD.js → Slider.stories-YoS2vvT2.js} +1 -1
- package/storybook-static/assets/{Sonner.stories-CXSZs5yN.js → Sonner.stories-CXW5e_Qg.js} +1 -1
- package/storybook-static/assets/{Switch.stories-BUQjobWe.js → Switch.stories-zr6i-aNi.js} +1 -1
- package/storybook-static/assets/{Table.stories-DJcV7M9R.js → Table.stories-BuGU4MUM.js} +1 -1
- package/storybook-static/assets/{Tabs.stories-ByeVrP18.js → Tabs.stories-jxkJ-AUI.js} +1 -1
- package/storybook-static/assets/{Textarea.stories-CKsDPHYc.js → Textarea.stories-CHXVobz6.js} +1 -1
- package/storybook-static/assets/{Toggle.stories-DutEzZ8k.js → Toggle.stories-C4uB4Hkq.js} +1 -1
- package/storybook-static/assets/{ToggleGroup.stories-C9fzJTh-.js → ToggleGroup.stories-Cr229wf_.js} +1 -1
- package/storybook-static/assets/{Tooltip.stories-CngnJbP7.js → Tooltip.stories-DmMYGR3T.js} +1 -1
- package/storybook-static/assets/{accordion-bQe9Rep4.js → accordion-BCfsz_gl.js} +1 -1
- package/storybook-static/assets/{alert-dialog-CypF_yaW.js → alert-dialog-BYt6Z7Kt.js} +1 -1
- package/storybook-static/assets/{avatar-CukM9hXu.js → avatar-CRn1qQsC.js} +1 -1
- package/storybook-static/assets/{axe-W8QMjM0E.js → axe-JCJl60WC.js} +1 -1
- package/storybook-static/assets/{button-BTWmFXop.js → button-BObfgcV1.js} +1 -1
- package/storybook-static/assets/{chart-column-Bzh5arua.js → chart-column-CplFCmg8.js} +1 -1
- package/storybook-static/assets/{check-BgWXKGqi.js → check-DyecuwP4.js} +1 -1
- package/storybook-static/assets/{checkbox-QlugAqJY.js → checkbox-D5BmNJeL.js} +1 -1
- package/storybook-static/assets/{chevron-down-BqLjUn1_.js → chevron-down-Bp7zbUB0.js} +1 -1
- package/storybook-static/assets/{chevron-left-BR_0lKnE.js → chevron-left-D1L4J3UW.js} +1 -1
- package/storybook-static/assets/{chevron-right-DpCIoMaJ.js → chevron-right-oMYqDJ_f.js} +1 -1
- package/storybook-static/assets/{circle-DVJTkDI7.js → circle-CyarsADt.js} +1 -1
- package/storybook-static/assets/clean-slate-V4nUw2Bm.css +1 -0
- package/storybook-static/assets/{command-BERL33lL.js → command-DCnXmNmT.js} +1 -1
- package/storybook-static/assets/{createLucideIcon-1ZwIAs_l.js → createLucideIcon-CLBo0iA0.js} +1 -1
- package/storybook-static/assets/default-CuTBjDca.css +1 -0
- package/storybook-static/assets/{dialog-DM9YJ1JD.js → dialog-DL0QBIbC.js} +1 -1
- package/storybook-static/assets/{dropdown-menu-TfFll7G9.js → dropdown-menu-BwL9gQwG.js} +1 -1
- package/storybook-static/assets/{ellipsis-BSY8VuLI.js → ellipsis-DPg968Rc.js} +1 -1
- package/storybook-static/assets/{grip-vertical-Buja1rv9.js → grip-vertical-CM0GDwvq.js} +1 -1
- package/storybook-static/assets/{iframe-G-6sM9Mt.js → iframe-BWjPIle6.js} +4 -4
- package/storybook-static/assets/{index-DqWzLCH-.js → index-04C4iZwC.js} +1 -1
- package/storybook-static/assets/{index-B-fXBfyD.js → index-3zykFCgl.js} +1 -1
- package/storybook-static/assets/{index-CAwQR9Pv.js → index-7XoYQV2z.js} +1 -1
- package/storybook-static/assets/{index-DySHPxMy.js → index-A7UzX-Xw.js} +1 -1
- package/storybook-static/assets/index-BN5_W3Yi.js +1 -0
- package/storybook-static/assets/{index-1v1lhNFD.js → index-BcAKBHrX.js} +1 -1
- package/storybook-static/assets/{index-7yNAGow7.js → index-BfKpUbCE.js} +1 -1
- package/storybook-static/assets/index-C5eJ31Z3.js +1 -0
- package/storybook-static/assets/{index-CayhpKmv.js → index-C6O7WofA.js} +1 -1
- package/storybook-static/assets/{index-BDwnENHR.js → index-C9xvlw_B.js} +1 -1
- package/storybook-static/assets/{index-Bq6UNRVc.js → index-CKwUGXmt.js} +1 -1
- package/storybook-static/assets/{index-d_S6mtzU.js → index-Cce91yJQ.js} +1 -1
- package/storybook-static/assets/{index-CMmbDm5O.js → index-Cw7p-pms.js} +1 -1
- package/storybook-static/assets/{index-CLmN5G1a.js → index-DE2OlfDP.js} +1 -1
- package/storybook-static/assets/{index-ev1RjzGv.js → index-DMk2qc2_.js} +1 -1
- package/storybook-static/assets/index-DWC9SV-P.js +1 -0
- package/storybook-static/assets/{index-HghBIZeg.js → index-Dqw7miiX.js} +1 -1
- package/storybook-static/assets/{index-ClNNG_ys.js → index-Dr2xmx-F.js} +1 -1
- package/storybook-static/assets/{index-lH-AZpAn.js → index-DschQYGl.js} +1 -1
- package/storybook-static/assets/{index-SxI7_jxe.js → index-Dsg7S8zu.js} +1 -1
- package/storybook-static/assets/{index-DvM9azdj.js → index-Pk2lGsul.js} +1 -1
- package/storybook-static/assets/{index-DNAxNqEO.js → index-URSssr5a.js} +1 -1
- package/storybook-static/assets/{index-CZaD3imo.js → index-npvyVsxD.js} +1 -1
- package/storybook-static/assets/{index-CzJf-yyP.js → index-u70nzfOV.js} +1 -1
- package/storybook-static/assets/{label-C5vJTTwH.js → label-CwntpYJ0.js} +1 -1
- package/storybook-static/assets/{lodash-Q9aGJGMb.js → lodash-BkmSIg_J.js} +1 -1
- package/storybook-static/assets/minimal-modern-d2yFlFJM.css +1 -0
- package/storybook-static/assets/{popover-DW1K4QCO.js → popover-ClqrrvjL.js} +1 -1
- package/storybook-static/assets/{radio-group-iPL-8jvw.js → radio-group-CvN0LQZp.js} +1 -1
- package/storybook-static/assets/{react-18-CK1-M7n3.js → react-18-Bj31y5Nr.js} +1 -1
- package/storybook-static/assets/{react-icons.esm-n2MUhK0n.js → react-icons.esm-DNr9VcvP.js} +1 -1
- package/storybook-static/assets/{refresh-cw-FYEbhX1i.js → refresh-cw-OZakDsFY.js} +1 -1
- package/storybook-static/assets/{schemas-S_Tg7JYp.js → schemas-CaLvKjsI.js} +1 -1
- package/storybook-static/assets/{section-factories-CC6eFfbk.js → section-factories-DKfKL-5s.js} +1 -1
- package/storybook-static/assets/{select-DbLzULCJ.js → select-gtXRB92c.js} +1 -1
- package/storybook-static/assets/{separator-DoUX1TNx.js → separator-CxCWfpZX.js} +1 -1
- package/storybook-static/assets/{settings-2-CpOcGlDm.js → settings-2-DSSkfF6W.js} +1 -1
- package/storybook-static/assets/{sheet-n5VQ25jh.js → sheet-r0VNiBiN.js} +1 -1
- package/storybook-static/assets/{shopping-cart-9kOJ1UDd.js → shopping-cart-CJBClF2m.js} +1 -1
- package/storybook-static/assets/{sidebar-CvUiZOJe.js → sidebar-BsMnOE7Y.js} +1 -1
- package/storybook-static/assets/spotify-BuPUgQEa.css +1 -0
- package/storybook-static/assets/{toggle-B-YKWMF8.js → toggle-D08-8sQA.js} +1 -1
- package/storybook-static/assets/{tooltip-CNONbPiI.js → tooltip-CZ1UAE_e.js} +1 -1
- package/storybook-static/assets/{trash-2-Bt5LMclM.js → trash-2-D55eseMQ.js} +1 -1
- package/storybook-static/assets/{x-DG9mLFAg.js → x-n8sFtlI2.js} +1 -1
- package/storybook-static/iframe.html +1 -1
- package/storybook-static/index.d.ts +63 -0
- package/storybook-static/index.d.ts.map +1 -1
- package/storybook-static/kit/builder/form/components/{FormActions.d.ts → FormBuilderActions.d.ts} +3 -3
- package/storybook-static/kit/builder/form/components/FormBuilderActions.d.ts.map +1 -0
- package/{dist/kit/builder/form/components/FormField.d.ts → storybook-static/kit/builder/form/components/FormBuilderField.d.ts} +4 -4
- package/storybook-static/kit/builder/form/components/FormBuilderField.d.ts.map +1 -0
- package/storybook-static/kit/builder/form/components/index.d.ts +2 -3
- package/storybook-static/kit/builder/form/components/index.d.ts.map +1 -1
- package/storybook-static/kit/builder/section/SectionBuilder.d.ts.map +1 -1
- package/storybook-static/project.json +1 -1
- package/vite.config.ts +76 -52
- package/dist/kit/builder/form/components/FormActions.d.ts.map +0 -1
- package/dist/kit/builder/form/components/FormField.d.ts.map +0 -1
- package/dist/kit/builder/form/components/FormSection.d.ts +0 -15
- package/dist/kit/builder/form/components/FormSection.d.ts.map +0 -1
- package/src/kit/builder/form/components/FormSection.tsx +0 -88
- package/storybook-static/assets/AdminLayout.Collapsible.stories-CsU3DwTo.js +0 -4
- package/storybook-static/assets/AdminLayout.CustomSidebarTitleAndIcon.stories-BqP6UVc7.js +0 -4
- package/storybook-static/assets/DataTable.Basic.stories-DBF5VYq9.js +0 -6
- package/storybook-static/assets/DataTable.Sorting.stories-CWpqloAB.js +0 -6
- package/storybook-static/assets/SectionBuilder-Bav4WrnT.js +0 -1
- package/storybook-static/assets/clean-slate-BR-XvZPt.css +0 -1
- package/storybook-static/assets/default-M24vcGB8.css +0 -1
- package/storybook-static/assets/index-DhoByIgc.js +0 -1
- package/storybook-static/assets/index-I343IfOC.js +0 -1
- package/storybook-static/assets/index-_crRFOM5.js +0 -1
- package/storybook-static/assets/minimal-modern-BmvR5wyr.css +0 -1
- package/storybook-static/assets/spotify-DpbeJq1r.css +0 -1
- package/storybook-static/kit/builder/form/components/FormActions.d.ts.map +0 -1
- package/storybook-static/kit/builder/form/components/FormField.d.ts.map +0 -1
- package/storybook-static/kit/builder/form/components/FormSection.d.ts +0 -15
- package/storybook-static/kit/builder/form/components/FormSection.d.ts.map +0 -1
- /package/src/{index.css → kit/themes/base.css} +0 -0
|
@@ -4,7 +4,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { cn } from '../../../../shadcn/lib/utils';
|
|
6
6
|
import { Button } from '../../../../shadcn/ui/button';
|
|
7
|
-
import {
|
|
7
|
+
import { FormBuilderField } from './FormBuilderField';
|
|
8
8
|
import SectionBuilder from '../../section/SectionBuilder';
|
|
9
9
|
import type { SectionLayout, SectionGridOptions, SectionFlexOptions, SectionNode } from '../../section/types';
|
|
10
10
|
import { AutocompleteFetcher, AutocompleteOption } from '../../../components/autocomplete/types';
|
|
@@ -22,6 +22,7 @@ export interface FormBuilderFieldConfig {
|
|
|
22
22
|
| 'select'
|
|
23
23
|
| 'autocomplete'
|
|
24
24
|
| 'checkbox'
|
|
25
|
+
| 'switch'
|
|
25
26
|
| 'radio'
|
|
26
27
|
| 'date'
|
|
27
28
|
| 'file'
|
|
@@ -70,6 +71,10 @@ export interface FormBuilderFieldConfig {
|
|
|
70
71
|
value: any;
|
|
71
72
|
}; // For conditional field visibility
|
|
72
73
|
hidden?: boolean; // Declarative hide
|
|
74
|
+
// Label placement control across inputs
|
|
75
|
+
labelPlacement?: 'stacked' | 'inline' | 'hidden';
|
|
76
|
+
// Wrapper container className (applies to the field wrapper, not the input)
|
|
77
|
+
wrapperClassName?: string;
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
export interface FormBuilderSectionConfig {
|
|
@@ -108,6 +113,8 @@ export interface FormBuilderProps {
|
|
|
108
113
|
actionsClassName?: string;
|
|
109
114
|
showActions?: boolean;
|
|
110
115
|
customActions?: React.ReactNode;
|
|
116
|
+
// UI: show a separator line above action buttons
|
|
117
|
+
showActionsSeparator?: boolean;
|
|
111
118
|
}
|
|
112
119
|
|
|
113
120
|
export function FormBuilder({
|
|
@@ -127,6 +134,7 @@ export function FormBuilder({
|
|
|
127
134
|
actionsClassName,
|
|
128
135
|
showActions = true,
|
|
129
136
|
customActions,
|
|
137
|
+
showActionsSeparator = true,
|
|
130
138
|
}: FormBuilderProps) {
|
|
131
139
|
// Generate schema from field configs if not provided
|
|
132
140
|
const generatedSchema = useMemo(() => {
|
|
@@ -154,6 +162,7 @@ export function FormBuilder({
|
|
|
154
162
|
baseSchema = z.union([z.string(), z.number()]).nullable();
|
|
155
163
|
break;
|
|
156
164
|
case 'checkbox':
|
|
165
|
+
case 'switch':
|
|
157
166
|
baseSchema = z.boolean();
|
|
158
167
|
break;
|
|
159
168
|
case 'date':
|
|
@@ -196,6 +205,7 @@ export function FormBuilder({
|
|
|
196
205
|
fieldSchema = z.union([z.string(), z.number(), z.object()]).nullable();
|
|
197
206
|
break;
|
|
198
207
|
case 'checkbox':
|
|
208
|
+
case 'switch':
|
|
199
209
|
fieldSchema = z.boolean();
|
|
200
210
|
break;
|
|
201
211
|
case 'date':
|
|
@@ -309,18 +319,27 @@ export function FormBuilder({
|
|
|
309
319
|
|
|
310
320
|
const { control, handleSubmit, reset, setValue, getValues, watch } = form;
|
|
311
321
|
|
|
312
|
-
//
|
|
313
|
-
const
|
|
322
|
+
// Determine if any field dependencies are declared
|
|
323
|
+
const hasDependencies = useMemo(() => {
|
|
324
|
+
return sections.some((section) =>
|
|
325
|
+
section.fields?.some((f) => Array.isArray(f.dependencies) && f.dependencies.length > 0),
|
|
326
|
+
);
|
|
327
|
+
}, [sections]);
|
|
328
|
+
|
|
329
|
+
// Only watch values when there are dependencies to respond to
|
|
330
|
+
// This prevents unnecessary re-renders that can cause focus loss
|
|
331
|
+
const emptyWatchedValues = useMemo(() => ({} as Record<string, any>), []);
|
|
332
|
+
const watchedValues = hasDependencies ? watch() : emptyWatchedValues;
|
|
314
333
|
|
|
315
334
|
// Handle field dependencies
|
|
316
335
|
const handleFieldDependencies = useCallback(
|
|
317
336
|
(field: FormBuilderFieldConfig) => {
|
|
318
|
-
if (!field.dependencies) return {};
|
|
337
|
+
if (!hasDependencies || !field.dependencies) return {};
|
|
319
338
|
|
|
320
339
|
const result: { disabled?: boolean; hidden?: boolean } = {};
|
|
321
340
|
|
|
322
341
|
field.dependencies.forEach((dep) => {
|
|
323
|
-
const dependentValue = watchedValues[dep.field];
|
|
342
|
+
const dependentValue = (watchedValues as Record<string, any>)[dep.field];
|
|
324
343
|
const conditionMet = dep.condition(dependentValue);
|
|
325
344
|
|
|
326
345
|
switch (dep.action) {
|
|
@@ -350,7 +369,7 @@ export function FormBuilder({
|
|
|
350
369
|
|
|
351
370
|
return result;
|
|
352
371
|
},
|
|
353
|
-
[watchedValues, setValue, getValues],
|
|
372
|
+
[hasDependencies, watchedValues, setValue, getValues],
|
|
354
373
|
);
|
|
355
374
|
|
|
356
375
|
// Handle field change with custom onChange
|
|
@@ -387,7 +406,7 @@ export function FormBuilder({
|
|
|
387
406
|
id: section.id ?? `section-${sectionIndex}`,
|
|
388
407
|
title: section.title,
|
|
389
408
|
subtitle: section.description,
|
|
390
|
-
variant: section.variant ?? '
|
|
409
|
+
variant: section.variant ?? 'plain',
|
|
391
410
|
className: section.className,
|
|
392
411
|
layout: section.layout ?? 'grid',
|
|
393
412
|
grid: section.grid ?? { cols: 1, mdCols: 2, gap: 'gap-4' },
|
|
@@ -403,9 +422,10 @@ export function FormBuilder({
|
|
|
403
422
|
return {
|
|
404
423
|
key: field.name,
|
|
405
424
|
span: { base: 1, md: spanMd },
|
|
425
|
+
className: field.wrapperClassName,
|
|
406
426
|
hidden: field.hidden,
|
|
407
427
|
content: (
|
|
408
|
-
<
|
|
428
|
+
<FormBuilderField
|
|
409
429
|
key={field.name}
|
|
410
430
|
field={{ ...field, disabled: field.disabled || fieldState.disabled }}
|
|
411
431
|
control={control}
|
|
@@ -430,7 +450,14 @@ export function FormBuilder({
|
|
|
430
450
|
<SectionBuilder sections={sectionNodes} />
|
|
431
451
|
|
|
432
452
|
{showActions && (
|
|
433
|
-
<div
|
|
453
|
+
<div
|
|
454
|
+
className={cn(
|
|
455
|
+
'flex flex-col sm:flex-row gap-3',
|
|
456
|
+
showActionsSeparator && 'pt-6',
|
|
457
|
+
showActionsSeparator && 'border-t',
|
|
458
|
+
actionsClassName,
|
|
459
|
+
)}
|
|
460
|
+
>
|
|
434
461
|
<Button type="submit" disabled={isSubmitting} className="sm:order-last">
|
|
435
462
|
{isSubmitting ? 'Submitting...' : submitLabel}
|
|
436
463
|
</Button>
|
|
@@ -442,7 +469,7 @@ export function FormBuilder({
|
|
|
442
469
|
)}
|
|
443
470
|
|
|
444
471
|
{onReset && (
|
|
445
|
-
<Button type="button" variant="
|
|
472
|
+
<Button type="button" variant="outline" onClick={handleReset} disabled={isSubmitting}>
|
|
446
473
|
{resetLabel}
|
|
447
474
|
</Button>
|
|
448
475
|
)}
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { Button } from '../../../../shadcn/ui/button';
|
|
3
3
|
import { cn } from '../../../../shadcn/lib/utils';
|
|
4
4
|
|
|
5
|
-
export interface
|
|
5
|
+
export interface FormBuilderActionsProps {
|
|
6
6
|
onSubmit?: () => void;
|
|
7
7
|
onCancel?: () => void;
|
|
8
8
|
onReset?: () => void;
|
|
@@ -19,7 +19,7 @@ export interface FormActionsProps {
|
|
|
19
19
|
resetButtonProps?: React.ComponentProps<typeof Button>;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export function
|
|
22
|
+
export function FormBuilderActions({
|
|
23
23
|
onSubmit,
|
|
24
24
|
onCancel,
|
|
25
25
|
onReset,
|
|
@@ -34,7 +34,7 @@ export function FormActions({
|
|
|
34
34
|
submitButtonProps,
|
|
35
35
|
cancelButtonProps,
|
|
36
36
|
resetButtonProps,
|
|
37
|
-
}:
|
|
37
|
+
}: FormBuilderActionsProps) {
|
|
38
38
|
return (
|
|
39
39
|
<div className={cn('flex items-center justify-end space-x-4', className)}>
|
|
40
40
|
{customActions}
|
|
@@ -6,6 +6,7 @@ import { Input } from '../../../../shadcn/ui/input';
|
|
|
6
6
|
import { Textarea } from '../../../../shadcn/ui/textarea';
|
|
7
7
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../../../shadcn/ui/select';
|
|
8
8
|
import { Checkbox } from '../../../../shadcn/ui/checkbox';
|
|
9
|
+
import { Switch } from '../../../../shadcn/ui/switch';
|
|
9
10
|
import { RadioGroup, RadioGroupItem } from '../../../../shadcn/ui/radio-group';
|
|
10
11
|
import { Label } from '../../../../shadcn/ui/label';
|
|
11
12
|
import { Card, CardContent, CardHeader, CardTitle } from '../../../../shadcn/ui/card';
|
|
@@ -14,7 +15,7 @@ import { FormBuilderFieldConfig } from './FormBuilder';
|
|
|
14
15
|
import { Autocomplete } from '../../../components/autocomplete/Autocomplete';
|
|
15
16
|
import type { AutocompleteOption } from '../../../components/autocomplete/types';
|
|
16
17
|
|
|
17
|
-
export interface
|
|
18
|
+
export interface FormBuilderFieldProps {
|
|
18
19
|
field: FormBuilderFieldConfig;
|
|
19
20
|
control: Control<any>;
|
|
20
21
|
onChange?: (value: any) => void;
|
|
@@ -22,7 +23,7 @@ export interface FormFieldProps {
|
|
|
22
23
|
parentPath?: string;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
export function
|
|
26
|
+
export function FormBuilderField({ field, control, onChange, onFieldChange, parentPath }: FormBuilderFieldProps) {
|
|
26
27
|
const fieldPath = parentPath ? `${parentPath}.${field.name}` : field.name;
|
|
27
28
|
const NULL_SENTINEL = '__NULL__';
|
|
28
29
|
|
|
@@ -130,7 +131,39 @@ export function FormField({ field, control, onChange, onFieldChange, parentPath
|
|
|
130
131
|
);
|
|
131
132
|
}
|
|
132
133
|
|
|
133
|
-
case 'checkbox':
|
|
134
|
+
case 'checkbox': {
|
|
135
|
+
const placement = field.labelPlacement ?? 'inline';
|
|
136
|
+
if (placement === 'stacked') {
|
|
137
|
+
const labelId = `${fieldPath}-label`;
|
|
138
|
+
return (
|
|
139
|
+
<div className="space-y-2">
|
|
140
|
+
<Label id={labelId} className="text-sm font-medium">
|
|
141
|
+
{field.label}
|
|
142
|
+
{field.required && <span className="text-destructive ml-1">*</span>}
|
|
143
|
+
</Label>
|
|
144
|
+
<Checkbox
|
|
145
|
+
aria-labelledby={labelId}
|
|
146
|
+
id={fieldPath}
|
|
147
|
+
checked={controllerField.value || false}
|
|
148
|
+
onCheckedChange={handleChange}
|
|
149
|
+
disabled={field.disabled}
|
|
150
|
+
className={cn(error && 'border-destructive', field.className)}
|
|
151
|
+
/>
|
|
152
|
+
</div>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
if (placement === 'hidden') {
|
|
156
|
+
return (
|
|
157
|
+
<Checkbox
|
|
158
|
+
id={fieldPath}
|
|
159
|
+
checked={controllerField.value || false}
|
|
160
|
+
onCheckedChange={handleChange}
|
|
161
|
+
disabled={field.disabled}
|
|
162
|
+
className={cn(error && 'border-destructive', field.className)}
|
|
163
|
+
/>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
// inline (default)
|
|
134
167
|
return (
|
|
135
168
|
<div className="flex items-center space-x-2">
|
|
136
169
|
<Checkbox
|
|
@@ -142,9 +175,61 @@ export function FormField({ field, control, onChange, onFieldChange, parentPath
|
|
|
142
175
|
/>
|
|
143
176
|
<Label htmlFor={fieldPath} className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
|
144
177
|
{field.label}
|
|
178
|
+
{field.required && <span className="text-destructive ml-1">*</span>}
|
|
145
179
|
</Label>
|
|
146
180
|
</div>
|
|
147
181
|
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
case 'switch': {
|
|
185
|
+
const placement = field.labelPlacement ?? 'inline';
|
|
186
|
+
if (placement === 'stacked') {
|
|
187
|
+
const labelId = `${fieldPath}-label`;
|
|
188
|
+
return (
|
|
189
|
+
<div className="space-y-2">
|
|
190
|
+
<Label id={labelId} className="text-sm font-medium">
|
|
191
|
+
{field.label}
|
|
192
|
+
{field.required && <span className="text-destructive ml-1">*</span>}
|
|
193
|
+
</Label>
|
|
194
|
+
<Switch
|
|
195
|
+
aria-labelledby={labelId}
|
|
196
|
+
id={fieldPath}
|
|
197
|
+
checked={controllerField.value || false}
|
|
198
|
+
onCheckedChange={handleChange}
|
|
199
|
+
disabled={field.disabled}
|
|
200
|
+
className={cn(error && 'border-destructive', field.className)}
|
|
201
|
+
/>
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
if (placement === 'hidden') {
|
|
206
|
+
return (
|
|
207
|
+
<Switch
|
|
208
|
+
id={fieldPath}
|
|
209
|
+
checked={controllerField.value || false}
|
|
210
|
+
onCheckedChange={handleChange}
|
|
211
|
+
disabled={field.disabled}
|
|
212
|
+
className={cn(error && 'border-destructive', field.className)}
|
|
213
|
+
/>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
// inline (default)
|
|
217
|
+
return (
|
|
218
|
+
<div className="flex items-center space-x-2">
|
|
219
|
+
<Switch
|
|
220
|
+
id={fieldPath}
|
|
221
|
+
checked={controllerField.value || false}
|
|
222
|
+
onCheckedChange={handleChange}
|
|
223
|
+
disabled={field.disabled}
|
|
224
|
+
className={cn(error && 'border-destructive', field.className)}
|
|
225
|
+
/>
|
|
226
|
+
<Label htmlFor={fieldPath} className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
|
227
|
+
{field.label}
|
|
228
|
+
{field.required && <span className="text-destructive ml-1">*</span>}
|
|
229
|
+
</Label>
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
}
|
|
148
233
|
|
|
149
234
|
case 'radio': {
|
|
150
235
|
const toUiValue = (val: unknown) => (val === null || val === undefined ? NULL_SENTINEL : String(val));
|
|
@@ -213,7 +298,7 @@ export function FormField({ field, control, onChange, onFieldChange, parentPath
|
|
|
213
298
|
<CardContent className="space-y-4">
|
|
214
299
|
<div className="grid gap-4 md:grid-cols-2">
|
|
215
300
|
{field.fields.map(subField => (
|
|
216
|
-
<
|
|
301
|
+
<FormBuilderField
|
|
217
302
|
key={subField.name}
|
|
218
303
|
field={subField}
|
|
219
304
|
control={control}
|
|
@@ -306,7 +391,7 @@ export function FormField({ field, control, onChange, onFieldChange, parentPath
|
|
|
306
391
|
<CardContent>
|
|
307
392
|
{field.fields && field.fields.length === 1 ? (
|
|
308
393
|
// Single field array
|
|
309
|
-
<
|
|
394
|
+
<FormBuilderField
|
|
310
395
|
field={{
|
|
311
396
|
...field.fields[0],
|
|
312
397
|
name: field.fields[0].name,
|
|
@@ -320,7 +405,7 @@ export function FormField({ field, control, onChange, onFieldChange, parentPath
|
|
|
320
405
|
// Object array
|
|
321
406
|
<div className="grid gap-4 md:grid-cols-2">
|
|
322
407
|
{field.fields.map(subField => (
|
|
323
|
-
<
|
|
408
|
+
<FormBuilderField
|
|
324
409
|
key={subField.name}
|
|
325
410
|
field={subField}
|
|
326
411
|
control={control}
|
|
@@ -360,8 +445,24 @@ export function FormField({ field, control, onChange, onFieldChange, parentPath
|
|
|
360
445
|
return renderArrayField();
|
|
361
446
|
}
|
|
362
447
|
|
|
363
|
-
// For checkbox,
|
|
364
|
-
if (field.type === 'checkbox') {
|
|
448
|
+
// For checkbox/switch, label may be inline/stacked/hidden handled inside renderBasicField
|
|
449
|
+
if (field.type === 'checkbox' || field.type === 'switch') {
|
|
450
|
+
return (
|
|
451
|
+
<div className={cn('space-y-2', field.gridCols && `md:col-span-${field.gridCols}`)}>
|
|
452
|
+
{renderBasicField()}
|
|
453
|
+
{field.description && (
|
|
454
|
+
<p className="text-sm text-muted-foreground">{field.description}</p>
|
|
455
|
+
)}
|
|
456
|
+
{error && (
|
|
457
|
+
<p className="text-sm text-destructive">{error.message}</p>
|
|
458
|
+
)}
|
|
459
|
+
</div>
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Non-checkbox fields: support labelPlacement
|
|
464
|
+
const placement = field.labelPlacement ?? 'stacked';
|
|
465
|
+
if (placement === 'hidden') {
|
|
365
466
|
return (
|
|
366
467
|
<div className={cn('space-y-2', field.gridCols && `md:col-span-${field.gridCols}`)}>
|
|
367
468
|
{renderBasicField()}
|
|
@@ -375,6 +476,27 @@ export function FormField({ field, control, onChange, onFieldChange, parentPath
|
|
|
375
476
|
);
|
|
376
477
|
}
|
|
377
478
|
|
|
479
|
+
if (placement === 'inline') {
|
|
480
|
+
return (
|
|
481
|
+
<div className={cn('space-y-1', field.gridCols && `md:col-span-${field.gridCols}`)}>
|
|
482
|
+
<div className="flex items-center gap-2">
|
|
483
|
+
<Label htmlFor={fieldPath} className="text-sm font-medium">
|
|
484
|
+
{field.label}
|
|
485
|
+
{field.required && <span className="text-destructive ml-1">*</span>}
|
|
486
|
+
</Label>
|
|
487
|
+
{renderBasicField()}
|
|
488
|
+
</div>
|
|
489
|
+
{field.description && (
|
|
490
|
+
<p className="text-sm text-muted-foreground">{field.description}</p>
|
|
491
|
+
)}
|
|
492
|
+
{error && (
|
|
493
|
+
<p className="text-sm text-destructive">{error.message}</p>
|
|
494
|
+
)}
|
|
495
|
+
</div>
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// stacked (default)
|
|
378
500
|
return (
|
|
379
501
|
<div className={cn('space-y-2', field.gridCols && `md:col-span-${field.gridCols}`)}>
|
|
380
502
|
<Label htmlFor={fieldPath} className="text-sm font-medium">
|
|
@@ -392,4 +514,4 @@ export function FormField({ field, control, onChange, onFieldChange, parentPath
|
|
|
392
514
|
);
|
|
393
515
|
}
|
|
394
516
|
|
|
395
|
-
export default
|
|
517
|
+
export default FormBuilderField;
|
|
@@ -93,6 +93,19 @@ export const createField = {
|
|
|
93
93
|
...options,
|
|
94
94
|
}),
|
|
95
95
|
|
|
96
|
+
switch: (
|
|
97
|
+
name: string,
|
|
98
|
+
label: string,
|
|
99
|
+
options: Partial<FormBuilderFieldConfig> = {},
|
|
100
|
+
): FormBuilderFieldConfig => ({
|
|
101
|
+
name,
|
|
102
|
+
label,
|
|
103
|
+
type: 'switch',
|
|
104
|
+
required: false,
|
|
105
|
+
defaultValue: false,
|
|
106
|
+
...options,
|
|
107
|
+
}),
|
|
108
|
+
|
|
96
109
|
radio: (
|
|
97
110
|
name: string,
|
|
98
111
|
label: string,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { memo } from 'react';
|
|
2
2
|
import { cn } from '../../../shadcn/lib/utils';
|
|
3
|
-
import {
|
|
3
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../../shadcn/ui/card';
|
|
4
|
+
import { Separator } from '../../../shadcn/ui/separator';
|
|
4
5
|
import type { SectionBuilderProps, SectionLeaf, SectionNode } from './types';
|
|
5
6
|
|
|
6
7
|
// Tailwind-safe literal class maps (1-12)
|
|
@@ -176,15 +177,71 @@ function SectionNodeRenderer({ node, renderLeaf }: { node: SectionNode; renderLe
|
|
|
176
177
|
const layout = node.layout ?? 'grid';
|
|
177
178
|
const containerClass = layout === 'grid' ? gridClasses(node.grid) : flexClasses(node.flex);
|
|
178
179
|
|
|
180
|
+
// Local presentational container replacing FormBuilderSection
|
|
181
|
+
const SectionContainer = ({ children }: { children: React.ReactNode }) => {
|
|
182
|
+
const title = node.title;
|
|
183
|
+
const description = node.subtitle;
|
|
184
|
+
const variant = node.variant ?? 'card';
|
|
185
|
+
const className = node.className;
|
|
186
|
+
const headerClassName = node.headerClassName;
|
|
187
|
+
const contentClassName = node.contentClassName;
|
|
188
|
+
|
|
189
|
+
const renderHeader = () => {
|
|
190
|
+
if (!title && !description) return null;
|
|
191
|
+
return (
|
|
192
|
+
<div className={cn('space-y-1', headerClassName)}>
|
|
193
|
+
{title && <h3 className="text-lg font-medium leading-none">{title}</h3>}
|
|
194
|
+
{description && <p className="text-sm text-muted-foreground">{description}</p>}
|
|
195
|
+
</div>
|
|
196
|
+
);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const renderContent = () => (
|
|
200
|
+
<div className={cn('space-y-4', contentClassName)}>
|
|
201
|
+
{children}
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
switch (variant) {
|
|
206
|
+
case 'card':
|
|
207
|
+
return (
|
|
208
|
+
<Card className={className}>
|
|
209
|
+
{(title || description) && (
|
|
210
|
+
<CardHeader>
|
|
211
|
+
{title && <CardTitle>{title}</CardTitle>}
|
|
212
|
+
{description && <CardDescription>{description}</CardDescription>}
|
|
213
|
+
</CardHeader>
|
|
214
|
+
)}
|
|
215
|
+
<CardContent>
|
|
216
|
+
{renderContent()}
|
|
217
|
+
</CardContent>
|
|
218
|
+
</Card>
|
|
219
|
+
);
|
|
220
|
+
case 'separator':
|
|
221
|
+
return (
|
|
222
|
+
<div className={cn('space-y-6', className)}>
|
|
223
|
+
{(title || description) && (
|
|
224
|
+
<>
|
|
225
|
+
{renderHeader()}
|
|
226
|
+
<Separator />
|
|
227
|
+
</>
|
|
228
|
+
)}
|
|
229
|
+
{renderContent()}
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
case 'plain':
|
|
233
|
+
default:
|
|
234
|
+
return (
|
|
235
|
+
<div className={cn('space-y-6', className)}>
|
|
236
|
+
{renderHeader()}
|
|
237
|
+
{renderContent()}
|
|
238
|
+
</div>
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
179
243
|
return (
|
|
180
|
-
<
|
|
181
|
-
title={node.title}
|
|
182
|
-
description={node.subtitle}
|
|
183
|
-
variant={node.variant ?? 'card'}
|
|
184
|
-
className={node.className}
|
|
185
|
-
headerClassName={node.headerClassName}
|
|
186
|
-
contentClassName={node.contentClassName}
|
|
187
|
-
>
|
|
244
|
+
<SectionContainer>
|
|
188
245
|
<div className={containerClass}>
|
|
189
246
|
{(node.children ?? []).map((child) => {
|
|
190
247
|
if (isLeaf(child)) {
|
|
@@ -198,7 +255,7 @@ function SectionNodeRenderer({ node, renderLeaf }: { node: SectionNode; renderLe
|
|
|
198
255
|
);
|
|
199
256
|
})}
|
|
200
257
|
</div>
|
|
201
|
-
</
|
|
258
|
+
</SectionContainer>
|
|
202
259
|
);
|
|
203
260
|
}
|
|
204
261
|
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
Circle,
|
|
23
23
|
} from 'lucide-react';
|
|
24
24
|
|
|
25
|
-
import {
|
|
25
|
+
import { useLocation, Link } from '@tanstack/react-router';
|
|
26
26
|
import { Fragment, useState, useCallback } from 'react';
|
|
27
27
|
import ThemeToggle from './ThemeToggle';
|
|
28
28
|
import { useAdminSidebarMenu } from '../hooks/menu';
|
|
@@ -214,7 +214,6 @@ function AdminLayoutContent({
|
|
|
214
214
|
</header>
|
|
215
215
|
<main className="flex flex-1 flex-col gap-4 p-4">
|
|
216
216
|
{children}
|
|
217
|
-
<Outlet />
|
|
218
217
|
</main>
|
|
219
218
|
</SidebarInset>
|
|
220
219
|
</div>
|
|
@@ -21,6 +21,6 @@ export function useAdminSidebarMenu() {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export function useAdminSidebarMenuRegistration() {
|
|
24
|
-
const { registerGroup, registerItem, clear } = useAdminSidebarMenu();
|
|
25
|
-
return { registerGroup, registerItem, clear };
|
|
24
|
+
const { setGroups, registerGroup, registerItem, clear } = useAdminSidebarMenu();
|
|
25
|
+
return { setGroups, registerGroup, registerItem, clear };
|
|
26
26
|
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import { createContext, use, useEffect
|
|
1
|
+
import { createContext, use, useEffect } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
ThemeProvider as NextThemesProvider,
|
|
4
|
+
useTheme as useNextThemes,
|
|
5
|
+
} from 'next-themes';
|
|
2
6
|
|
|
3
7
|
type Theme = 'dark' | 'light' | 'system';
|
|
4
8
|
|
|
5
9
|
type ThemeProviderProps = {
|
|
6
10
|
children: React.ReactNode;
|
|
7
|
-
defaultTheme?: Theme;
|
|
8
|
-
storageKey?: string;
|
|
11
|
+
defaultTheme?: Theme; // maps to next-themes defaultTheme
|
|
12
|
+
storageKey?: string; // maps to next-themes storageKey
|
|
9
13
|
};
|
|
10
14
|
|
|
11
15
|
type ThemeProviderState = {
|
|
@@ -26,51 +30,47 @@ export function ThemeProvider({
|
|
|
26
30
|
storageKey = 'vite-ui-theme',
|
|
27
31
|
...props
|
|
28
32
|
}: ThemeProviderProps) {
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
return (
|
|
34
|
+
<NextThemesProvider
|
|
35
|
+
attribute="class"
|
|
36
|
+
defaultTheme={defaultTheme}
|
|
37
|
+
storageKey={storageKey}
|
|
38
|
+
enableSystem
|
|
39
|
+
disableTransitionOnChange
|
|
40
|
+
>
|
|
41
|
+
<ThemeBridge {...props}>{children}</ThemeBridge>
|
|
42
|
+
</NextThemesProvider>
|
|
31
43
|
);
|
|
44
|
+
}
|
|
32
45
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
const stored = localStorage.getItem(storageKey) as Theme | null;
|
|
37
|
-
if ((!stored || stored === 'system') && defaultTheme !== 'system') {
|
|
38
|
-
localStorage.setItem(storageKey, defaultTheme);
|
|
39
|
-
setTheme(defaultTheme);
|
|
40
|
-
}
|
|
41
|
-
}, []);
|
|
46
|
+
function ThemeBridge({ children, ...props }: { children: React.ReactNode }) {
|
|
47
|
+
const { theme, resolvedTheme, setTheme } = useNextThemes();
|
|
42
48
|
|
|
49
|
+
// Mirror resolved theme to data-theme and color-scheme for CSS/Tailwind consumers
|
|
43
50
|
useEffect(() => {
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
app?.classList.remove('light', 'dark');
|
|
51
|
+
if (typeof document === 'undefined') return;
|
|
52
|
+
const root = document.documentElement;
|
|
53
|
+
const body = document.body;
|
|
54
|
+
const app = document.getElementById('app');
|
|
55
|
+
const resolved = (resolvedTheme as 'light' | 'dark') ?? 'light';
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
? 'dark'
|
|
55
|
-
: 'light';
|
|
56
|
-
}
|
|
57
|
-
return theme;
|
|
58
|
-
})();
|
|
57
|
+
// data-theme + color-scheme for CSS variables and form controls
|
|
58
|
+
root.setAttribute('data-theme', resolved);
|
|
59
|
+
root.style.colorScheme = resolved;
|
|
59
60
|
|
|
61
|
+
// Ensure class-based dark mode works across html, body, and #app
|
|
62
|
+
const opposite = resolved === 'dark' ? 'light' : 'dark';
|
|
63
|
+
root.classList.remove(opposite);
|
|
64
|
+
body.classList.remove(opposite);
|
|
65
|
+
app?.classList.remove(opposite);
|
|
60
66
|
root.classList.add(resolved);
|
|
61
67
|
body.classList.add(resolved);
|
|
62
68
|
app?.classList.add(resolved);
|
|
63
|
-
|
|
64
|
-
// Important for Tailwind v4 @theme dark support
|
|
65
|
-
root.setAttribute('data-theme', resolved);
|
|
66
|
-
}, [theme]);
|
|
69
|
+
}, [resolvedTheme]);
|
|
67
70
|
|
|
68
|
-
const value = {
|
|
69
|
-
theme,
|
|
70
|
-
setTheme: (
|
|
71
|
-
localStorage.setItem(storageKey, theme);
|
|
72
|
-
setTheme(theme);
|
|
73
|
-
},
|
|
71
|
+
const value: ThemeProviderState = {
|
|
72
|
+
theme: (theme as Theme) ?? 'system',
|
|
73
|
+
setTheme: (t: Theme) => setTheme(t),
|
|
74
74
|
};
|
|
75
75
|
|
|
76
76
|
return (
|
|
@@ -29,6 +29,8 @@ export const BasicUsage: Story = {
|
|
|
29
29
|
{ name: 'lastName', label: 'Last name', type: 'text', required: true },
|
|
30
30
|
{ name: 'email', label: 'Email', type: 'email', required: true },
|
|
31
31
|
{ name: 'newsletter', label: 'Subscribe to newsletter', type: 'checkbox', defaultValue: false, gridCols: 2 },
|
|
32
|
+
{ name: 'darkMode', label: 'Enable dark mode', type: 'switch', defaultValue: false, gridCols: 2 },
|
|
33
|
+
{ name: 'darkModeStacked', label: 'Enable dark mode (stacked)', type: 'switch', defaultValue: true, labelPlacement: 'stacked', gridCols: 2 },
|
|
32
34
|
],
|
|
33
35
|
},
|
|
34
36
|
],
|