@marktiderman/genesis-ui 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AppShell-BJarYZzz.d.ts +198 -0
- package/dist/{chunk-NJMO3VPA.js → chunk-22XB3Q6H.js} +8 -8
- package/dist/chunk-22XB3Q6H.js.map +1 -0
- package/dist/{chunk-2MXMWBDV.js → chunk-27ELYTGE.js} +6 -6
- package/dist/chunk-27ELYTGE.js.map +1 -0
- package/dist/chunk-46TZGMFW.js +159 -0
- package/dist/chunk-46TZGMFW.js.map +1 -0
- package/dist/chunk-4CQHPWJD.js +117 -0
- package/dist/chunk-4CQHPWJD.js.map +1 -0
- package/dist/{chunk-ZDAT5JDH.js → chunk-65SPT7EA.js} +24 -6
- package/dist/chunk-65SPT7EA.js.map +1 -0
- package/dist/chunk-6KHY647C.js +67 -0
- package/dist/chunk-6KHY647C.js.map +1 -0
- package/dist/chunk-6M4NHHKG.js +51 -0
- package/dist/chunk-6M4NHHKG.js.map +1 -0
- package/dist/{chunk-AI6CCNWK.js → chunk-6NPEMLBA.js} +2 -2
- package/dist/chunk-6NPEMLBA.js.map +1 -0
- package/dist/chunk-6Q6CLLJ4.js +9 -0
- package/dist/chunk-6Q6CLLJ4.js.map +1 -0
- package/dist/{chunk-UGHQFQOE.js → chunk-6TVMQH6M.js} +28 -28
- package/dist/chunk-6TVMQH6M.js.map +1 -0
- package/dist/{chunk-UKSBLFEK.js → chunk-6XPMU65K.js} +5 -5
- package/dist/{chunk-UKSBLFEK.js.map → chunk-6XPMU65K.js.map} +1 -1
- package/dist/{chunk-YZEA5TGK.js → chunk-7XMZVISM.js} +16 -16
- package/dist/chunk-7XMZVISM.js.map +1 -0
- package/dist/chunk-A7AHVDG7.js +52 -0
- package/dist/chunk-A7AHVDG7.js.map +1 -0
- package/dist/chunk-AFZ76Q26.js +84 -0
- package/dist/chunk-AFZ76Q26.js.map +1 -0
- package/dist/{chunk-4V4H2ZM7.js → chunk-AL5TMEZQ.js} +31 -31
- package/dist/chunk-AL5TMEZQ.js.map +1 -0
- package/dist/{chunk-O4B5ALO7.js → chunk-AOZGQM6W.js} +3 -3
- package/dist/chunk-AOZGQM6W.js.map +1 -0
- package/dist/chunk-ARJI2JUV.js +122 -0
- package/dist/chunk-ARJI2JUV.js.map +1 -0
- package/dist/chunk-AVFZ4P4B.js +25 -0
- package/dist/chunk-AVFZ4P4B.js.map +1 -0
- package/dist/chunk-B2GIBUQD.js +11 -0
- package/dist/chunk-B2GIBUQD.js.map +1 -0
- package/dist/{chunk-JXI2MYL3.js → chunk-C46ITXE4.js} +5 -5
- package/dist/chunk-C46ITXE4.js.map +1 -0
- package/dist/chunk-CNTWQSUT.js +65 -0
- package/dist/chunk-CNTWQSUT.js.map +1 -0
- package/dist/chunk-D2LIXA2Y.js +65 -0
- package/dist/chunk-D2LIXA2Y.js.map +1 -0
- package/dist/{chunk-FCGFG4RV.js → chunk-D4JTQYP5.js} +3 -3
- package/dist/{chunk-FCGFG4RV.js.map → chunk-D4JTQYP5.js.map} +1 -1
- package/dist/{chunk-T5VX5DYO.js → chunk-F47U5KNW.js} +5 -5
- package/dist/chunk-F47U5KNW.js.map +1 -0
- package/dist/chunk-FEJAZFP3.js +1100 -0
- package/dist/chunk-FEJAZFP3.js.map +1 -0
- package/dist/{chunk-2B3MFHBQ.js → chunk-FK42ZGGV.js} +21 -21
- package/dist/chunk-FK42ZGGV.js.map +1 -0
- package/dist/{chunk-XISSLIMH.js → chunk-FTTNGGLA.js} +2 -2
- package/dist/{chunk-XISSLIMH.js.map → chunk-FTTNGGLA.js.map} +1 -1
- package/dist/chunk-G5YKBPBF.js +74 -0
- package/dist/chunk-G5YKBPBF.js.map +1 -0
- package/dist/chunk-GCQFVMPI.js +56 -0
- package/dist/chunk-GCQFVMPI.js.map +1 -0
- package/dist/{chunk-LDPPIBIJ.js → chunk-IJCT5IJD.js} +3 -3
- package/dist/{chunk-LDPPIBIJ.js.map → chunk-IJCT5IJD.js.map} +1 -1
- package/dist/{chunk-DGRHPJ3M.js → chunk-J6FBA7PH.js} +3 -3
- package/dist/{chunk-DGRHPJ3M.js.map → chunk-J6FBA7PH.js.map} +1 -1
- package/dist/chunk-JAMLFMDS.js +12 -0
- package/dist/chunk-JAMLFMDS.js.map +1 -0
- package/dist/{chunk-C6H7AMO4.js → chunk-LNFF2RBD.js} +9 -9
- package/dist/chunk-LNFF2RBD.js.map +1 -0
- package/dist/{chunk-QBZMY2AF.js → chunk-M62CHO3N.js} +18 -18
- package/dist/chunk-M62CHO3N.js.map +1 -0
- package/dist/{chunk-5Q4FABZQ.js → chunk-NUXW6NPP.js} +8 -8
- package/dist/chunk-NUXW6NPP.js.map +1 -0
- package/dist/{chunk-I6JQHP6D.js → chunk-NWSCYLPT.js} +78 -17
- package/dist/chunk-NWSCYLPT.js.map +1 -0
- package/dist/{chunk-63FW5ZUA.js → chunk-P3ZL6FGR.js} +7 -7
- package/dist/chunk-P3ZL6FGR.js.map +1 -0
- package/dist/chunk-P64MFQXZ.js +191 -0
- package/dist/chunk-P64MFQXZ.js.map +1 -0
- package/dist/chunk-PLIYF6KI.js +52 -0
- package/dist/chunk-PLIYF6KI.js.map +1 -0
- package/dist/chunk-PSR47JRA.js +26 -0
- package/dist/chunk-PSR47JRA.js.map +1 -0
- package/dist/{chunk-QLCDAFS7.js → chunk-Q4I4SFBD.js} +3 -3
- package/dist/chunk-Q4I4SFBD.js.map +1 -0
- package/dist/{chunk-SBJV3K4G.js → chunk-QJ6P2DW6.js} +28 -28
- package/dist/chunk-QJ6P2DW6.js.map +1 -0
- package/dist/{chunk-IP6TMT3D.js → chunk-R7QFP6EL.js} +3 -3
- package/dist/chunk-R7QFP6EL.js.map +1 -0
- package/dist/{chunk-WZWR2ELB.js → chunk-SEBU6JCK.js} +6 -6
- package/dist/chunk-SEBU6JCK.js.map +1 -0
- package/dist/{chunk-7OBL3H5P.js → chunk-SEFK4NZV.js} +3 -3
- package/dist/chunk-SEFK4NZV.js.map +1 -0
- package/dist/chunk-SRIT4NFQ.js +67 -0
- package/dist/chunk-SRIT4NFQ.js.map +1 -0
- package/dist/chunk-SY62XJFA.js +19 -0
- package/dist/chunk-SY62XJFA.js.map +1 -0
- package/dist/{chunk-SVXDWPLO.js → chunk-THD3YOOZ.js} +17 -17
- package/dist/chunk-THD3YOOZ.js.map +1 -0
- package/dist/chunk-VXFYETRK.js +58 -0
- package/dist/chunk-VXFYETRK.js.map +1 -0
- package/dist/chunk-XBX6QMR5.js +58 -0
- package/dist/chunk-XBX6QMR5.js.map +1 -0
- package/dist/{chunk-SCUT2ZCD.js → chunk-XCGZPFT7.js} +17 -17
- package/dist/chunk-XCGZPFT7.js.map +1 -0
- package/dist/chunk-XMK3T4TA.js +73 -0
- package/dist/chunk-XMK3T4TA.js.map +1 -0
- package/dist/chunk-YJ6MPS6U.js +226 -0
- package/dist/chunk-YJ6MPS6U.js.map +1 -0
- package/dist/chunk-YRYQXU3G.js +38 -0
- package/dist/chunk-YRYQXU3G.js.map +1 -0
- package/dist/{chunk-5J7BP5DI.js → chunk-YWFPMND7.js} +10 -10
- package/dist/chunk-YWFPMND7.js.map +1 -0
- package/dist/chunk-YYEGAFUE.js +94 -0
- package/dist/chunk-YYEGAFUE.js.map +1 -0
- package/dist/{chunk-TKA4LOMT.js → chunk-ZZ7H7VVX.js} +8 -8
- package/dist/chunk-ZZ7H7VVX.js.map +1 -0
- package/dist/components/data/index.d.ts +223 -74
- package/dist/components/data/index.js +504 -539
- package/dist/components/data/index.js.map +1 -1
- package/dist/components/layout/dashboard-page.d.ts +4 -0
- package/dist/components/layout/dashboard-page.js +5 -0
- package/dist/components/layout/dashboard-page.js.map +1 -0
- package/dist/components/layout/detail-page.d.ts +91 -0
- package/dist/components/layout/detail-page.js +6 -0
- package/dist/components/layout/detail-page.js.map +1 -0
- package/dist/components/layout/form-page.d.ts +82 -0
- package/dist/components/layout/form-page.js +5 -0
- package/dist/components/layout/form-page.js.map +1 -0
- package/dist/components/layout/index.d.ts +15 -0
- package/dist/components/layout/index.js +21 -0
- package/dist/components/layout/index.js.map +1 -0
- package/dist/components/layout/settings-page.d.ts +114 -0
- package/dist/components/layout/settings-page.js +6 -0
- package/dist/components/layout/settings-page.js.map +1 -0
- package/dist/components/patterns/empty-state.d.ts +74 -0
- package/dist/components/{ui → patterns}/empty-state.js +2 -1
- package/dist/components/{ui → patterns}/form-field.d.ts +2 -2
- package/dist/components/{ui → patterns}/form-field.js +2 -2
- package/dist/components/patterns/index.d.ts +18 -0
- package/dist/components/patterns/index.js +18 -0
- package/dist/components/patterns/index.js.map +1 -0
- package/dist/components/patterns/page-loading.d.ts +25 -0
- package/dist/components/patterns/page-loading.js +5 -0
- package/dist/components/patterns/page-loading.js.map +1 -0
- package/dist/components/patterns/settings-row.d.ts +42 -0
- package/dist/components/patterns/settings-row.js +6 -0
- package/dist/components/patterns/settings-row.js.map +1 -0
- package/dist/components/patterns/status-badge.d.ts +38 -0
- package/dist/components/patterns/status-badge.js +4 -0
- package/dist/components/patterns/status-badge.js.map +1 -0
- package/dist/components/patterns/toggle-row.d.ts +36 -0
- package/dist/components/patterns/toggle-row.js +8 -0
- package/dist/components/patterns/toggle-row.js.map +1 -0
- package/dist/components/patterns/user-avatar.d.ts +34 -0
- package/dist/components/patterns/user-avatar.js +5 -0
- package/dist/components/patterns/user-avatar.js.map +1 -0
- package/dist/components/ui/accessible-icon.d.ts +48 -0
- package/dist/components/ui/accessible-icon.js +3 -0
- package/dist/components/ui/accessible-icon.js.map +1 -0
- package/dist/components/ui/accordion.d.ts +5 -5
- package/dist/components/ui/accordion.js +1 -1
- package/dist/components/ui/alert-dialog.d.ts +10 -10
- package/dist/components/ui/alert-dialog.js +1 -1
- package/dist/components/ui/aspect-ratio.d.ts +2 -2
- package/dist/components/ui/aspect-ratio.js +1 -1
- package/dist/components/ui/avatar.d.ts +4 -4
- package/dist/components/ui/avatar.js +1 -1
- package/dist/components/ui/button.d.ts +75 -8
- package/dist/components/ui/button.js +1 -1
- package/dist/components/ui/calendar.js +2 -2
- package/dist/components/ui/carousel.d.ts +4 -3
- package/dist/components/ui/carousel.js +2 -2
- package/dist/components/ui/checkbox.d.ts +2 -2
- package/dist/components/ui/checkbox.js +1 -1
- package/dist/components/ui/collapsible.d.ts +4 -4
- package/dist/components/ui/collapsible.js +1 -1
- package/dist/components/ui/command-palette.d.ts +30 -0
- package/dist/components/ui/command-palette.js +6 -0
- package/dist/components/ui/command-palette.js.map +1 -0
- package/dist/components/ui/command.d.ts +9 -9
- package/dist/components/ui/command.js +2 -2
- package/dist/components/ui/context-menu.d.ts +15 -15
- package/dist/components/ui/context-menu.js +1 -1
- package/dist/components/ui/date-picker.js +4 -4
- package/dist/components/ui/dialog.d.ts +9 -9
- package/dist/components/ui/dialog.js +1 -1
- package/dist/components/ui/drawer.d.ts +7 -7
- package/dist/components/ui/dropdown-menu.d.ts +15 -15
- package/dist/components/ui/dropdown-menu.js +1 -1
- package/dist/components/ui/hover-card.d.ts +4 -4
- package/dist/components/ui/hover-card.js +1 -1
- package/dist/components/ui/input-otp.d.ts +2 -2
- package/dist/components/ui/label.d.ts +2 -2
- package/dist/components/ui/label.js +1 -1
- package/dist/components/ui/menubar.d.ts +16 -16
- package/dist/components/ui/menubar.js +1 -1
- package/dist/components/ui/navigation-menu.d.ts +9 -9
- package/dist/components/ui/navigation-menu.js +1 -1
- package/dist/components/ui/pagination.js +2 -2
- package/dist/components/ui/password-toggle-field.d.ts +121 -0
- package/dist/components/ui/password-toggle-field.js +4 -0
- package/dist/components/ui/password-toggle-field.js.map +1 -0
- package/dist/components/ui/popover.d.ts +4 -4
- package/dist/components/ui/popover.js +1 -1
- package/dist/components/ui/radio-group.d.ts +3 -3
- package/dist/components/ui/radio-group.js +1 -1
- package/dist/components/ui/resizable.d.ts +1 -1
- package/dist/components/ui/scroll-area.d.ts +60 -3
- package/dist/components/ui/scroll-area.js +1 -1
- package/dist/components/ui/select.d.ts +9 -9
- package/dist/components/ui/select.js +1 -1
- package/dist/components/ui/sheet.d.ts +11 -9
- package/dist/components/ui/sheet.js +1 -1
- package/dist/components/ui/slider.d.ts +2 -2
- package/dist/components/ui/slider.js +1 -1
- package/dist/components/ui/switch.d.ts +2 -2
- package/dist/components/ui/switch.js +1 -1
- package/dist/components/ui/tabs.d.ts +29 -14
- package/dist/components/ui/tabs.js +1 -1
- package/dist/components/ui/toggle-group.d.ts +3 -3
- package/dist/components/ui/toggle-group.js +2 -2
- package/dist/components/ui/toggle.d.ts +2 -2
- package/dist/components/ui/toggle.js +1 -1
- package/dist/components/ui/toolbar.d.ts +104 -0
- package/dist/components/ui/toolbar.js +6 -0
- package/dist/components/ui/toolbar.js.map +1 -0
- package/dist/components/ui/tooltip.d.ts +46 -4
- package/dist/components/ui/tooltip.js +1 -1
- package/dist/components/ui/visually-hidden.d.ts +48 -0
- package/dist/components/ui/visually-hidden.js +4 -0
- package/dist/components/ui/visually-hidden.js.map +1 -0
- package/dist/dashboard-page-C0TihRl5.d.ts +107 -0
- package/dist/hooks/index.d.ts +5 -3
- package/dist/hooks/index.js +10 -5
- package/dist/hooks/use-data-filters.d.ts +1 -1
- package/dist/hooks/use-data-filters.js +2 -2
- package/dist/hooks/use-keyboard-navigation.d.ts +58 -2
- package/dist/hooks/use-keyboard-navigation.js +1 -1
- package/dist/hooks/use-resource-page.d.ts +3 -2
- package/dist/hooks/use-resource-page.js +2 -2
- package/dist/hooks/use-saved-views.d.ts +28 -0
- package/dist/hooks/use-saved-views.js +3 -0
- package/dist/hooks/use-saved-views.js.map +1 -0
- package/dist/hooks/use-view-preference.d.ts +1 -1
- package/dist/hooks/use-view-preference.js +1 -1
- package/dist/index.d.ts +25 -26
- package/dist/index.js +55 -38
- package/dist/{navigation-qI86Ldug.d.ts → navigation-CpOEyUSb.d.ts} +4 -0
- package/dist/spacing-BS6qECD4.d.ts +191 -0
- package/dist/split-Dk9f4Mi0.d.ts +57 -0
- package/dist/{use-resource-page-B-CFcikK.d.ts → use-resource-page-CPs6wjKt.d.ts} +10 -2
- package/dist/view-settings--KXNT_jE.d.ts +12 -0
- package/package.json +77 -32
- package/dist/chunk-2B3MFHBQ.js.map +0 -1
- package/dist/chunk-2MXMWBDV.js.map +0 -1
- package/dist/chunk-4V4H2ZM7.js.map +0 -1
- package/dist/chunk-5J7BP5DI.js.map +0 -1
- package/dist/chunk-5Q4FABZQ.js.map +0 -1
- package/dist/chunk-63FW5ZUA.js.map +0 -1
- package/dist/chunk-7OBL3H5P.js.map +0 -1
- package/dist/chunk-AI6CCNWK.js.map +0 -1
- package/dist/chunk-C6H7AMO4.js.map +0 -1
- package/dist/chunk-EI5EGKHL.js +0 -32
- package/dist/chunk-EI5EGKHL.js.map +0 -1
- package/dist/chunk-I6JQHP6D.js.map +0 -1
- package/dist/chunk-IP6TMT3D.js.map +0 -1
- package/dist/chunk-JJ4NOMMI.js +0 -73
- package/dist/chunk-JJ4NOMMI.js.map +0 -1
- package/dist/chunk-JXI2MYL3.js.map +0 -1
- package/dist/chunk-KBS4BHME.js +0 -11
- package/dist/chunk-KBS4BHME.js.map +0 -1
- package/dist/chunk-LRXDZBV2.js +0 -26
- package/dist/chunk-LRXDZBV2.js.map +0 -1
- package/dist/chunk-NJMO3VPA.js.map +0 -1
- package/dist/chunk-O4B5ALO7.js.map +0 -1
- package/dist/chunk-OCTV3S4I.js +0 -29
- package/dist/chunk-OCTV3S4I.js.map +0 -1
- package/dist/chunk-QBZMY2AF.js.map +0 -1
- package/dist/chunk-QLCDAFS7.js.map +0 -1
- package/dist/chunk-SBJV3K4G.js.map +0 -1
- package/dist/chunk-SCUT2ZCD.js.map +0 -1
- package/dist/chunk-SVXDWPLO.js.map +0 -1
- package/dist/chunk-T465NJRL.js +0 -134
- package/dist/chunk-T465NJRL.js.map +0 -1
- package/dist/chunk-T5VX5DYO.js.map +0 -1
- package/dist/chunk-TKA4LOMT.js.map +0 -1
- package/dist/chunk-UGHQFQOE.js.map +0 -1
- package/dist/chunk-WALXVR6V.js +0 -9
- package/dist/chunk-WALXVR6V.js.map +0 -1
- package/dist/chunk-WZWR2ELB.js.map +0 -1
- package/dist/chunk-YZEA5TGK.js.map +0 -1
- package/dist/chunk-ZDAT5JDH.js.map +0 -1
- package/dist/components/ui/empty-state.d.ts +0 -22
- /package/dist/components/{ui → patterns}/empty-state.js.map +0 -0
- /package/dist/components/{ui → patterns}/form-field.js.map +0 -0
- /package/dist/{ViewToggle-DDe_Luj2.d.ts → view-toggle-DDe_Luj2.d.ts} +0 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { Split } from './chunk-D2LIXA2Y.js';
|
|
2
|
+
import { cn } from './chunk-NZ5WBA4R.js';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cva } from 'class-variance-authority';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var sectionHeadingVariants = cva("font-semibold tracking-tight", {
|
|
8
|
+
variants: {
|
|
9
|
+
// Visual weight follows the semantic level, so the document outline and
|
|
10
|
+
// the type scale cannot drift apart by accident. Override with
|
|
11
|
+
// `headingClassName` in the rare case where they genuinely should.
|
|
12
|
+
level: {
|
|
13
|
+
2: "text-xl",
|
|
14
|
+
3: "text-lg",
|
|
15
|
+
4: "text-base"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: {
|
|
19
|
+
level: 2
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
var Section = React.forwardRef(
|
|
23
|
+
({
|
|
24
|
+
className,
|
|
25
|
+
headingClassName,
|
|
26
|
+
title,
|
|
27
|
+
description,
|
|
28
|
+
actions,
|
|
29
|
+
level = 2,
|
|
30
|
+
testID,
|
|
31
|
+
children,
|
|
32
|
+
...props
|
|
33
|
+
}, ref) => {
|
|
34
|
+
const headingId = React.useId();
|
|
35
|
+
const Heading = `h${level}`;
|
|
36
|
+
return /* @__PURE__ */ jsxs(
|
|
37
|
+
"section",
|
|
38
|
+
{
|
|
39
|
+
ref,
|
|
40
|
+
"data-slot": "section",
|
|
41
|
+
"data-testid": testID,
|
|
42
|
+
"aria-labelledby": headingId,
|
|
43
|
+
className: cn("flex flex-col gap-4", className),
|
|
44
|
+
...props,
|
|
45
|
+
children: [
|
|
46
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 sm:flex-row sm:items-start sm:justify-between sm:gap-4", children: [
|
|
47
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
|
|
48
|
+
/* @__PURE__ */ jsx(
|
|
49
|
+
Heading,
|
|
50
|
+
{
|
|
51
|
+
id: headingId,
|
|
52
|
+
"data-slot": "section-title",
|
|
53
|
+
className: cn(sectionHeadingVariants({ level }), headingClassName),
|
|
54
|
+
children: title
|
|
55
|
+
}
|
|
56
|
+
),
|
|
57
|
+
description ? (
|
|
58
|
+
// A <div>, not a <p>. `description` is a ReactNode, so it can
|
|
59
|
+
// legitimately carry a list, a <div>, or a composed component
|
|
60
|
+
// with a block root — none of which may appear inside a
|
|
61
|
+
// paragraph. The browser silently reparents them out of the
|
|
62
|
+
// <p>, which changes the DOM shape and hydration-mismatches
|
|
63
|
+
// for SSR consumers. Same call, same reason, as widening
|
|
64
|
+
// Tooltip's bubble from <span> to <div>.
|
|
65
|
+
/* @__PURE__ */ jsx(
|
|
66
|
+
"div",
|
|
67
|
+
{
|
|
68
|
+
"data-slot": "section-description",
|
|
69
|
+
className: "text-sm text-muted-foreground",
|
|
70
|
+
children: description
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
) : null
|
|
74
|
+
] }),
|
|
75
|
+
actions ? /* @__PURE__ */ jsx(
|
|
76
|
+
"div",
|
|
77
|
+
{
|
|
78
|
+
"data-slot": "section-actions",
|
|
79
|
+
className: "flex shrink-0 items-center gap-2",
|
|
80
|
+
children: actions
|
|
81
|
+
}
|
|
82
|
+
) : null
|
|
83
|
+
] }),
|
|
84
|
+
children
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
Section.displayName = "Section";
|
|
91
|
+
var SettingsPage = React.forwardRef(
|
|
92
|
+
({
|
|
93
|
+
className,
|
|
94
|
+
title,
|
|
95
|
+
description,
|
|
96
|
+
actions,
|
|
97
|
+
sections,
|
|
98
|
+
navAt = "md",
|
|
99
|
+
testID,
|
|
100
|
+
...props
|
|
101
|
+
}, ref) => {
|
|
102
|
+
const panels = /* @__PURE__ */ jsx("div", { "data-slot": "settings-page-sections", className: "flex min-w-0 flex-col gap-10", children: sections.map((section) => /* @__PURE__ */ jsx(
|
|
103
|
+
Section,
|
|
104
|
+
{
|
|
105
|
+
id: section.id,
|
|
106
|
+
title: section.title,
|
|
107
|
+
description: section.description,
|
|
108
|
+
className: "scroll-mt-6",
|
|
109
|
+
children: section.content
|
|
110
|
+
},
|
|
111
|
+
section.id
|
|
112
|
+
)) });
|
|
113
|
+
return /* @__PURE__ */ jsxs(
|
|
114
|
+
"div",
|
|
115
|
+
{
|
|
116
|
+
ref,
|
|
117
|
+
"data-slot": "settings-page",
|
|
118
|
+
"data-testid": testID,
|
|
119
|
+
className: cn("flex flex-col gap-6", className),
|
|
120
|
+
...props,
|
|
121
|
+
children: [
|
|
122
|
+
/* @__PURE__ */ jsxs(
|
|
123
|
+
"header",
|
|
124
|
+
{
|
|
125
|
+
"data-slot": "settings-page-header",
|
|
126
|
+
className: "flex flex-col gap-1 sm:flex-row sm:items-start sm:justify-between sm:gap-4",
|
|
127
|
+
children: [
|
|
128
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
|
|
129
|
+
/* @__PURE__ */ jsx(
|
|
130
|
+
"h1",
|
|
131
|
+
{
|
|
132
|
+
"data-slot": "settings-page-title",
|
|
133
|
+
className: "truncate text-xl font-bold tracking-tight sm:text-2xl",
|
|
134
|
+
children: title
|
|
135
|
+
}
|
|
136
|
+
),
|
|
137
|
+
description ? /* @__PURE__ */ jsx(
|
|
138
|
+
"p",
|
|
139
|
+
{
|
|
140
|
+
"data-slot": "settings-page-description",
|
|
141
|
+
className: "text-sm text-muted-foreground",
|
|
142
|
+
children: description
|
|
143
|
+
}
|
|
144
|
+
) : null
|
|
145
|
+
] }),
|
|
146
|
+
actions ? /* @__PURE__ */ jsx(
|
|
147
|
+
"div",
|
|
148
|
+
{
|
|
149
|
+
"data-slot": "settings-page-actions",
|
|
150
|
+
className: "flex shrink-0 items-center gap-2",
|
|
151
|
+
children: actions
|
|
152
|
+
}
|
|
153
|
+
) : null
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
),
|
|
157
|
+
sections.length > 1 ? /* @__PURE__ */ jsx(
|
|
158
|
+
Split,
|
|
159
|
+
{
|
|
160
|
+
at: navAt,
|
|
161
|
+
ratio: "1/3",
|
|
162
|
+
gap: "xl",
|
|
163
|
+
start: /* @__PURE__ */ jsx(
|
|
164
|
+
"nav",
|
|
165
|
+
{
|
|
166
|
+
"data-slot": "settings-page-nav",
|
|
167
|
+
"aria-label": "Settings sections",
|
|
168
|
+
className: "sticky top-6",
|
|
169
|
+
children: /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-1", children: sections.map((section) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
170
|
+
"a",
|
|
171
|
+
{
|
|
172
|
+
href: `#${section.id}`,
|
|
173
|
+
className: "block truncate rounded-md px-3 py-2 text-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
174
|
+
children: section.title
|
|
175
|
+
}
|
|
176
|
+
) }, section.id)) })
|
|
177
|
+
}
|
|
178
|
+
),
|
|
179
|
+
end: panels
|
|
180
|
+
}
|
|
181
|
+
) : panels
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
SettingsPage.displayName = "SettingsPage";
|
|
188
|
+
|
|
189
|
+
export { Section, SettingsPage, sectionHeadingVariants };
|
|
190
|
+
//# sourceMappingURL=chunk-P64MFQXZ.js.map
|
|
191
|
+
//# sourceMappingURL=chunk-P64MFQXZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/layout/section.tsx","../src/components/layout/settings-page.tsx"],"names":["React2","jsx","jsxs"],"mappings":"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","file":"chunk-P64MFQXZ.js","sourcesContent":["import * as React from \"react\";\nimport { cva } from \"class-variance-authority\";\nimport { cn } from \"../../utils\";\n\nconst sectionHeadingVariants = cva(\"font-semibold tracking-tight\", {\n variants: {\n // Visual weight follows the semantic level, so the document outline and\n // the type scale cannot drift apart by accident. Override with\n // `headingClassName` in the rare case where they genuinely should.\n level: {\n 2: \"text-xl\",\n 3: \"text-lg\",\n 4: \"text-base\",\n },\n },\n defaultVariants: {\n level: 2,\n },\n});\n\nexport interface SectionProps\n // `HTMLAttributes` already declares `title?: string` (the native tooltip\n // attribute). Omit it before redeclaring the heading as a ReactNode,\n // otherwise the two are incompatible and the element sprouts a browser\n // tooltip repeating its own heading.\n extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n /** Section heading. Required — an untitled region is just a `Stack`. */\n title: React.ReactNode;\n /** Optional supporting copy under the heading. */\n description?: React.ReactNode;\n /** Optional trailing controls for this region (a button, a menu, a filter). */\n actions?: React.ReactNode;\n /**\n * Heading level. Default `2`, i.e. a top-level region of a page whose\n * `<h1>` is the page title. Drop to `3`/`4` when nesting so the outline\n * stays legal — heading level is document structure, not a font size.\n */\n level?: 2 | 3 | 4;\n /** Escape hatch for the heading's own classes (type scale, color). */\n headingClassName?: string;\n /** Stable test/automation selector (emitted as `data-testid`). */\n testID?: string;\n}\n\n/**\n * Section — a titled region of a page: heading, optional description,\n * optional trailing actions, and the content itself.\n *\n * ```tsx\n * <Section\n * title=\"Team members\"\n * description=\"Everyone with access to this workspace.\"\n * actions={<Button>Invite</Button>}\n * >\n * <Grid cols={3}>{…}</Grid>\n * </Section>\n * ```\n *\n * Renders a real `<section>` named by its own heading through\n * `aria-labelledby`, which is what turns it into a navigable landmark —\n * an unnamed `<section>` is announced as nothing at all, so the markup\n * that *looks* the most semantic is the markup that most often isn't.\n *\n * The header collapses to a column below `sm`, so a long title and an\n * action button never fight over the same line on a phone.\n *\n * @stability Beta\n */\nexport const Section = React.forwardRef<HTMLElement, SectionProps>(\n (\n {\n className,\n headingClassName,\n title,\n description,\n actions,\n level = 2,\n testID,\n children,\n ...props\n },\n ref\n ) => {\n const headingId = React.useId();\n const Heading = `h${level}` as \"h2\" | \"h3\" | \"h4\";\n\n return (\n <section\n ref={ref}\n data-slot=\"section\"\n data-testid={testID}\n aria-labelledby={headingId}\n className={cn(\"flex flex-col gap-4\", className)}\n {...props}\n >\n <div className=\"flex flex-col gap-1 sm:flex-row sm:items-start sm:justify-between sm:gap-4\">\n <div className=\"flex min-w-0 flex-col gap-1\">\n <Heading\n id={headingId}\n data-slot=\"section-title\"\n className={cn(sectionHeadingVariants({ level }), headingClassName)}\n >\n {title}\n </Heading>\n {description ? (\n // A <div>, not a <p>. `description` is a ReactNode, so it can\n // legitimately carry a list, a <div>, or a composed component\n // with a block root — none of which may appear inside a\n // paragraph. The browser silently reparents them out of the\n // <p>, which changes the DOM shape and hydration-mismatches\n // for SSR consumers. Same call, same reason, as widening\n // Tooltip's bubble from <span> to <div>.\n <div\n data-slot=\"section-description\"\n className=\"text-sm text-muted-foreground\"\n >\n {description}\n </div>\n ) : null}\n </div>\n {actions ? (\n <div\n data-slot=\"section-actions\"\n className=\"flex shrink-0 items-center gap-2\"\n >\n {actions}\n </div>\n ) : null}\n </div>\n {children}\n </section>\n );\n }\n);\nSection.displayName = \"Section\";\n\nexport { sectionHeadingVariants };\n","import * as React from \"react\";\nimport { cn } from \"../../utils\";\nimport { Section } from \"./section\";\nimport { Split, type SplitBreakpoint } from \"./split\";\n\n/**\n * One region of a settings surface: a nav-rail entry and the panel it jumps\n * to, declared together so the two can't drift apart.\n *\n * `@marktiderman/genesis-ui-native` exports a type of the same name whose\n * shape differs — it carries `rows: SettingsPageRow[]` and a `footnote`,\n * because a native settings screen is a sectioned list of drill-in rows\n * rather than a scrollable surface with a jump rail (a rail has nowhere to\n * live on a phone). The shared field names mean the same thing on both:\n * `id` keys the section, `title` names it. That parity is deliberate, and\n * it is the same accommodation `Stack` documents for its own two-surface\n * shape difference.\n */\nexport interface SettingsPageSection {\n /**\n * Fragment id. The rail links to `#${id}` and the `<section>` carries it,\n * so the jump works with no JavaScript and survives a deep link.\n */\n id: string;\n /**\n * Rail entry and section heading — one string, so they always match.\n *\n * Named `title` rather than `label` to match the native surface's word for\n * the same thing. Required here where native's is optional: a rail entry\n * with no name is a link to nowhere, whereas an unlabeled native section\n * is a normal grouping.\n */\n title: string;\n /** Supporting copy under the section heading. */\n description?: React.ReactNode;\n /** The rows themselves — `SettingsRow` / `ToggleRow` are the natural ones. */\n content: React.ReactNode;\n}\n\nexport interface SettingsPageProps\n // `HTMLAttributes` already declares `title?: string` (the native tooltip\n // attribute); omit it before redeclaring the page title as a ReactNode.\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Page title. Rendered as the page's single `<h1>`. */\n title: React.ReactNode;\n /** Supporting copy under the title. */\n description?: React.ReactNode;\n /** Page-level trailing controls — \"Save all\", a workspace switcher. */\n actions?: React.ReactNode;\n /** The settings regions, in the order they appear in the rail. */\n sections: SettingsPageSection[];\n /**\n * Breakpoint at and above which the rail sits beside the sections. Below\n * it the rail stacks above them, where it reads as jump links. Default\n * `\"md\"`.\n */\n navAt?: SplitBreakpoint;\n /** Stable test/automation selector (emitted as `data-testid`). */\n testID?: string;\n}\n\n/**\n * SettingsPage — a sectioned settings surface with a nav rail for jumping\n * between sections.\n *\n * ```tsx\n * <SettingsPage\n * title=\"Workspace settings\"\n * sections={[\n * {\n * id: \"profile\",\n * title: \"Profile\",\n * content: <SettingsRow label=\"Display name\" htmlFor=\"name\"><Input id=\"name\" /></SettingsRow>,\n * },\n * {\n * id: \"notifications\",\n * title: \"Notifications\",\n * description: \"How and when we contact you.\",\n * content: <ToggleRow label=\"Weekly digest\" checked={digest} onCheckedChange={setDigest} />,\n * },\n * ]}\n * />\n * ```\n *\n * Sections are a prop, not children, because the rail has to know their\n * titles and ids. Declaring each once is what makes \"the rail says\n * Notifications and the heading says Alerts\" unrepresentable.\n *\n * **The rail appears only when there is more than one section** — a rail\n * with a single entry is a link to where you already are. That is an\n * automatic consequence of the content rather than a prop, so nobody has to\n * remember to turn it off.\n *\n * The rail is `sticky top-6` with no breakpoint prefix, and that is correct\n * in both modes rather than an oversight: side by side it is a stretched\n * grid item, so it sticks for the length of the sections; stacked, its pane\n * is exactly its own height, so sticky positioning has nothing to travel\n * and it simply scrolls away. One class, right twice.\n *\n * Each section renders through `Section`, so it is a real `<section>` named\n * by its own heading via `aria-labelledby` — the thing that makes it a\n * landmark a screen-reader user can jump to, which is the whole promise the\n * visual rail is making.\n *\n * The header row is markup, not a component, and deliberately not\n * `PageHeader` — see `./detail-page` for the measured reasons composing it\n * was rejected across all four templates. Width is left to `Container` —\n * wrap, don't duplicate.\n *\n * This is a **layout**: it positions rows, it does not persist them. Where a\n * setting is stored, and when, is the app's. Genesis's answer for the\n * storing half is `useStorage` from `@marktiderman/genesis-core`, which a\n * caller wires up outside this component.\n *\n * @stability Beta\n */\nexport const SettingsPage = React.forwardRef<HTMLDivElement, SettingsPageProps>(\n (\n {\n className,\n title,\n description,\n actions,\n sections,\n navAt = \"md\",\n testID,\n ...props\n },\n ref\n ) => {\n const panels = (\n <div data-slot=\"settings-page-sections\" className=\"flex min-w-0 flex-col gap-10\">\n {sections.map((section) => (\n <Section\n key={section.id}\n id={section.id}\n title={section.title}\n description={section.description}\n // Leaves room above the heading when the rail jumps to it, so\n // the anchor doesn't land with the title flush against the\n // viewport edge.\n className=\"scroll-mt-6\"\n >\n {section.content}\n </Section>\n ))}\n </div>\n );\n\n return (\n <div\n ref={ref}\n data-slot=\"settings-page\"\n data-testid={testID}\n className={cn(\"flex flex-col gap-6\", className)}\n {...props}\n >\n <header\n data-slot=\"settings-page-header\"\n className=\"flex flex-col gap-1 sm:flex-row sm:items-start sm:justify-between sm:gap-4\"\n >\n <div className=\"flex min-w-0 flex-col gap-1\">\n <h1\n data-slot=\"settings-page-title\"\n className=\"truncate text-xl font-bold tracking-tight sm:text-2xl\"\n >\n {title}\n </h1>\n {description ? (\n <p\n data-slot=\"settings-page-description\"\n className=\"text-sm text-muted-foreground\"\n >\n {description}\n </p>\n ) : null}\n </div>\n {actions ? (\n <div\n data-slot=\"settings-page-actions\"\n className=\"flex shrink-0 items-center gap-2\"\n >\n {actions}\n </div>\n ) : null}\n </header>\n\n {sections.length > 1 ? (\n <Split\n at={navAt}\n ratio=\"1/3\"\n gap=\"xl\"\n start={\n <nav\n data-slot=\"settings-page-nav\"\n aria-label=\"Settings sections\"\n className=\"sticky top-6\"\n >\n <ul className=\"flex flex-col gap-1\">\n {sections.map((section) => (\n <li key={section.id}>\n <a\n href={`#${section.id}`}\n className=\"block truncate rounded-md px-3 py-2 text-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {section.title}\n </a>\n </li>\n ))}\n </ul>\n </nav>\n }\n end={panels}\n />\n ) : (\n panels\n )}\n </div>\n );\n }\n);\nSettingsPage.displayName = \"SettingsPage\";\n"]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Alert, AlertDescription } from './chunk-OZZTPE7F.js';
|
|
2
|
+
import { cn } from './chunk-NZ5WBA4R.js';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
var FormPage = React.forwardRef(
|
|
7
|
+
({ className, title, description, errors, actions, testID, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
8
|
+
"form",
|
|
9
|
+
{
|
|
10
|
+
ref,
|
|
11
|
+
"data-slot": "form-page",
|
|
12
|
+
"data-testid": testID,
|
|
13
|
+
className: cn("flex flex-col gap-6", className),
|
|
14
|
+
...props,
|
|
15
|
+
children: [
|
|
16
|
+
/* @__PURE__ */ jsxs("header", { "data-slot": "form-page-header", className: "flex min-w-0 flex-col gap-1", children: [
|
|
17
|
+
/* @__PURE__ */ jsx(
|
|
18
|
+
"h1",
|
|
19
|
+
{
|
|
20
|
+
"data-slot": "form-page-title",
|
|
21
|
+
className: "truncate text-xl font-bold tracking-tight sm:text-2xl",
|
|
22
|
+
children: title
|
|
23
|
+
}
|
|
24
|
+
),
|
|
25
|
+
description ? /* @__PURE__ */ jsx(
|
|
26
|
+
"p",
|
|
27
|
+
{
|
|
28
|
+
"data-slot": "form-page-description",
|
|
29
|
+
className: "text-sm text-muted-foreground",
|
|
30
|
+
children: description
|
|
31
|
+
}
|
|
32
|
+
) : null
|
|
33
|
+
] }),
|
|
34
|
+
errors ? /* @__PURE__ */ jsx(Alert, { variant: "destructive", "data-slot": "form-page-errors", children: /* @__PURE__ */ jsx(AlertDescription, { children: errors }) }) : null,
|
|
35
|
+
/* @__PURE__ */ jsx("div", { "data-slot": "form-page-body", className: "flex min-w-0 flex-col gap-8", children }),
|
|
36
|
+
actions ? /* @__PURE__ */ jsx(
|
|
37
|
+
"div",
|
|
38
|
+
{
|
|
39
|
+
"data-slot": "form-page-actions",
|
|
40
|
+
className: "sticky bottom-0 z-10 flex flex-wrap items-center justify-end gap-2 border-t border-border bg-background py-3",
|
|
41
|
+
children: actions
|
|
42
|
+
}
|
|
43
|
+
) : null
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
);
|
|
48
|
+
FormPage.displayName = "FormPage";
|
|
49
|
+
|
|
50
|
+
export { FormPage };
|
|
51
|
+
//# sourceMappingURL=chunk-PLIYF6KI.js.map
|
|
52
|
+
//# sourceMappingURL=chunk-PLIYF6KI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/layout/form-page.tsx"],"names":[],"mappings":";;;;;AAqFO,IAAM,QAAA,GAAiB,KAAA,CAAA,UAAA;AAAA,EAC5B,CACE,EAAE,SAAA,EAAW,KAAA,EAAO,WAAA,EAAa,MAAA,EAAQ,OAAA,EAAS,MAAA,EAAQ,QAAA,EAAU,GAAG,KAAA,EAAM,EAC7E,GAAA,qBAEA,IAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,GAAA;AAAA,MACA,WAAA,EAAU,WAAA;AAAA,MACV,aAAA,EAAa,MAAA;AAAA,MACb,SAAA,EAAW,EAAA,CAAG,qBAAA,EAAuB,SAAS,CAAA;AAAA,MAC7C,GAAG,KAAA;AAAA,MAEJ,QAAA,EAAA;AAAA,wBAAA,IAAA,CAAC,QAAA,EAAA,EAAO,WAAA,EAAU,kBAAA,EAAmB,SAAA,EAAU,6BAAA,EAC7C,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,IAAA;AAAA,YAAA;AAAA,cACC,WAAA,EAAU,iBAAA;AAAA,cACV,SAAA,EAAU,uDAAA;AAAA,cAET,QAAA,EAAA;AAAA;AAAA,WACH;AAAA,UACC,WAAA,mBACC,GAAA;AAAA,YAAC,GAAA;AAAA,YAAA;AAAA,cACC,WAAA,EAAU,uBAAA;AAAA,cACV,SAAA,EAAU,+BAAA;AAAA,cAET,QAAA,EAAA;AAAA;AAAA,WACH,GACE;AAAA,SAAA,EACN,CAAA;AAAA,QAEC,MAAA,mBACC,GAAA,CAAC,KAAA,EAAA,EAAM,OAAA,EAAQ,aAAA,EAAc,WAAA,EAAU,kBAAA,EACrC,QAAA,kBAAA,GAAA,CAAC,gBAAA,EAAA,EAAkB,QAAA,EAAA,MAAA,EAAO,CAAA,EAC5B,CAAA,GACE,IAAA;AAAA,4BAEH,KAAA,EAAA,EAAI,WAAA,EAAU,gBAAA,EAAiB,SAAA,EAAU,+BACvC,QAAA,EACH,CAAA;AAAA,QAEC,OAAA,mBACC,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,WAAA,EAAU,mBAAA;AAAA,YACV,SAAA,EAAU,8GAAA;AAAA,YAET,QAAA,EAAA;AAAA;AAAA,SACH,GACE;AAAA;AAAA;AAAA;AAGV;AACA,QAAA,CAAS,WAAA,GAAc,UAAA","file":"chunk-PLIYF6KI.js","sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils\";\nimport { Alert, AlertDescription } from \"../ui/alert\";\n\nexport interface FormPageProps\n // `FormHTMLAttributes` declares `title?: string` (the native tooltip\n // attribute); omit it before redeclaring the page title as a ReactNode.\n extends Omit<React.FormHTMLAttributes<HTMLFormElement>, \"title\"> {\n /** Page title. Rendered as the page's single `<h1>`. */\n title: React.ReactNode;\n /** Supporting copy under the title — what this form is for. */\n description?: React.ReactNode;\n /**\n * Validation summary. Rendered in a destructive `Alert` above the fields\n * when set, so the summary announces itself (`role=\"alert\"`) and sits\n * where a keyboard user lands after a failed submit. Pass a list, a\n * sentence, or your own markup — the copy is yours.\n */\n errors?: React.ReactNode;\n /**\n * Submit / cancel controls. Rendered in a bar that sticks to the bottom of\n * the scroll container, so the primary action stays reachable on a form\n * longer than the viewport.\n */\n actions?: React.ReactNode;\n /** Stable test/automation selector (emitted as `data-testid`). */\n testID?: string;\n}\n\n/**\n * FormPage — a titled form: header, validation summary, a body of sections,\n * and a sticky action bar.\n *\n * It renders a real `<form>` and forwards `onSubmit` (and every other form\n * attribute), so a plain `type=\"submit\"` button in `actions` submits it with\n * no `form=` wiring. That is what keeps it usable for a HAND-WRITTEN form —\n * `react-hook-form`, `useActionState`, or an uncontrolled `<form action>` —\n * rather than only for one of them.\n *\n * **Do not put a `ResourceForm` inside it.** `ResourceForm` renders its own\n * `<form>`, and nesting one form in another is invalid HTML: the browser\n * drops the inner element, SSR and hydration disagree about the tree, and a\n * submit fires both handlers. `ResourceForm` is the data-bound *alternative*\n * to this template — it already answers \"bind a form to a resource\", with a\n * `layout=\"page\"` mode of its own — not something this one wraps.\n *\n * ```tsx\n * <FormPage\n * title=\"New engagement\"\n * description=\"Both the coach and the client will be notified.\"\n * errors={submitFailed ? \"Fix the highlighted fields.\" : undefined}\n * onSubmit={handleSubmit(onValid)}\n * actions={\n * <>\n * <Button variant=\"ghost\" type=\"button\" testID=\"cancel\">Cancel</Button>\n * <Button type=\"submit\" testID=\"save\">Save</Button>\n * </>\n * }\n * >\n * <Section title=\"Basics\">\n * <FormField label=\"Name\">…</FormField>\n * </Section>\n * </FormPage>\n * ```\n *\n * Sections are just children — `Section` is the natural one, but any node\n * works. The body spaces them at `gap-8`, wider than the page's own `gap-6`,\n * so section boundaries read as boundaries rather than as more fields.\n *\n * The sticky bar needs a scrolling ancestor to stick to, which `AppShell`'s\n * `<main>` provides. It renders only when `actions` is set: an empty sticky\n * strip is a border floating over the content for no reason.\n *\n * This is a **layout** — it positions a form, it does not submit one. Binding\n * fields to a resource is `ResourceForm`'s job\n * (`@marktiderman/genesis-ui/data`), which is already the one home for that\n * question. The header row is markup, not a component, and deliberately not\n * `PageHeader` — see `./detail-page` for the measured reasons composing it\n * was rejected across all four templates (`title: string`, and a 6x jump in\n * the emitted per-component closure plus a new `radix-ui` runtime dependency\n * for chrome none of them render). Width is left to `Container` for the\n * one-home reason — wrap, don't duplicate.\n *\n * @stability Beta\n */\nexport const FormPage = React.forwardRef<HTMLFormElement, FormPageProps>(\n (\n { className, title, description, errors, actions, testID, children, ...props },\n ref\n ) => (\n <form\n ref={ref}\n data-slot=\"form-page\"\n data-testid={testID}\n className={cn(\"flex flex-col gap-6\", className)}\n {...props}\n >\n <header data-slot=\"form-page-header\" className=\"flex min-w-0 flex-col gap-1\">\n <h1\n data-slot=\"form-page-title\"\n className=\"truncate text-xl font-bold tracking-tight sm:text-2xl\"\n >\n {title}\n </h1>\n {description ? (\n <p\n data-slot=\"form-page-description\"\n className=\"text-sm text-muted-foreground\"\n >\n {description}\n </p>\n ) : null}\n </header>\n\n {errors ? (\n <Alert variant=\"destructive\" data-slot=\"form-page-errors\">\n <AlertDescription>{errors}</AlertDescription>\n </Alert>\n ) : null}\n\n <div data-slot=\"form-page-body\" className=\"flex min-w-0 flex-col gap-8\">\n {children}\n </div>\n\n {actions ? (\n <div\n data-slot=\"form-page-actions\"\n className=\"sticky bottom-0 z-10 flex flex-wrap items-center justify-end gap-2 border-t border-border bg-background py-3\"\n >\n {actions}\n </div>\n ) : null}\n </form>\n )\n);\nFormPage.displayName = \"FormPage\";\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from './chunk-NZ5WBA4R.js';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Slider as Slider$1 } from 'radix-ui';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var Slider = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
8
|
+
Slider$1.Root,
|
|
9
|
+
{
|
|
10
|
+
ref,
|
|
11
|
+
className: cn(
|
|
12
|
+
"relative flex w-full touch-none select-none items-center",
|
|
13
|
+
className
|
|
14
|
+
),
|
|
15
|
+
...props,
|
|
16
|
+
children: [
|
|
17
|
+
/* @__PURE__ */ jsx(Slider$1.Track, { className: "relative h-2 w-full grow overflow-hidden rounded-full bg-secondary", children: /* @__PURE__ */ jsx(Slider$1.Range, { className: "absolute h-full bg-primary" }) }),
|
|
18
|
+
/* @__PURE__ */ jsx(Slider$1.Thumb, { className: "block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" })
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
));
|
|
22
|
+
Slider.displayName = Slider$1.Root.displayName;
|
|
23
|
+
|
|
24
|
+
export { Slider };
|
|
25
|
+
//# sourceMappingURL=chunk-PSR47JRA.js.map
|
|
26
|
+
//# sourceMappingURL=chunk-PSR47JRA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ui/slider.tsx"],"names":["SliderPrimitive"],"mappings":";;;;;AAMA,IAAM,MAAA,GAAe,iBAGnB,CAAC,EAAE,WAAW,GAAG,KAAA,IAAS,GAAA,qBAC1B,IAAA;AAAA,EAACA,QAAA,CAAgB,IAAA;AAAA,EAAhB;AAAA,IACC,GAAA;AAAA,IACA,SAAA,EAAW,EAAA;AAAA,MACT,0DAAA;AAAA,MACA;AAAA,KACF;AAAA,IACC,GAAG,KAAA;AAAA,IAEJ,QAAA,EAAA;AAAA,sBAAA,GAAA,CAACA,QAAA,CAAgB,KAAA,EAAhB,EAAsB,SAAA,EAAU,oEAAA,EAC/B,QAAA,kBAAA,GAAA,CAACA,QAAA,CAAgB,KAAA,EAAhB,EAAsB,SAAA,EAAU,4BAAA,EAA6B,CAAA,EAChE,CAAA;AAAA,sBACA,GAAA,CAACA,QAAA,CAAgB,KAAA,EAAhB,EAAsB,WAAU,gQAAA,EAAiQ;AAAA;AAAA;AACpS,CACD;AACD,MAAA,CAAO,WAAA,GAAcA,SAAgB,IAAA,CAAK,WAAA","file":"chunk-PSR47JRA.js","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Slider as SliderPrimitive } from \"radix-ui\";\nimport { cn } from \"../../utils\";\n\nconst Slider = React.forwardRef<\n React.ComponentRef<typeof SliderPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>\n>(({ className, ...props }, ref) => (\n <SliderPrimitive.Root\n ref={ref}\n className={cn(\n \"relative flex w-full touch-none select-none items-center\",\n className,\n )}\n {...props}\n >\n <SliderPrimitive.Track className=\"relative h-2 w-full grow overflow-hidden rounded-full bg-secondary\">\n <SliderPrimitive.Range className=\"absolute h-full bg-primary\" />\n </SliderPrimitive.Track>\n <SliderPrimitive.Thumb className=\"block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\" />\n </SliderPrimitive.Root>\n));\nSlider.displayName = SliderPrimitive.Root.displayName;\n\nexport { Slider };\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useViewPreference } from './chunk-
|
|
2
|
+
import { useViewPreference } from './chunk-FTTNGGLA.js';
|
|
3
3
|
import { useMemo, useState, useCallback, useRef, useEffect } from 'react';
|
|
4
4
|
|
|
5
5
|
function titleCase(str) {
|
|
@@ -284,5 +284,5 @@ function useResourcePage(props) {
|
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
export { titleCase, useResourcePage };
|
|
287
|
-
//# sourceMappingURL=chunk-
|
|
288
|
-
//# sourceMappingURL=chunk-
|
|
287
|
+
//# sourceMappingURL=chunk-Q4I4SFBD.js.map
|
|
288
|
+
//# sourceMappingURL=chunk-Q4I4SFBD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-resource-page.ts"],"names":[],"mappings":"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","file":"chunk-Q4I4SFBD.js","sourcesContent":["\"use client\";\n\nimport { useState, useMemo, useCallback, useEffect, useRef } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { DataTableColumn } from \"../components/data/DataTable\";\nimport type { DataFilterConfig } from \"../components/data/DataFilters\";\nimport type { ViewMode } from \"../components/patterns/view-toggle\";\nimport type { Identifier, FilterParam, SortParam } from \"@marktiderman/genesis-core/provider\";\nimport { useViewPreference } from \"./use-view-preference\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Actions available inside column render functions. */\nexport interface ResourceActions<T> {\n update: (id: Identifier, data: Partial<T>) => Promise<T>;\n remove: (id: Identifier) => Promise<void>;\n refetch: () => void;\n}\n\n/** Column definition — full config form. */\nexport interface ResourceColumnDef<T> {\n key: string;\n header?: string;\n sortable?: boolean;\n hideBelow?: \"sm\" | \"md\" | \"lg\" | \"xl\";\n render?: (\n item: T,\n value: unknown,\n actions: ResourceActions<T>,\n ) => ReactNode;\n}\n\nexport interface UseResourcePageProps<T> {\n /** Resource name — used for view-preference storage key. */\n resource?: string;\n /** The data to display (pre-fetched or from useResource). */\n data: T[];\n /** Total count of records (before client-side filtering). */\n total: number;\n /** Column definitions. Strings are expanded to `{ key, header, sortable }`. Omit to auto-generate from data keys. */\n columns?: Array<string | ResourceColumnDef<T>>;\n /** Status filter config. String = field name (options auto-derived). Object = explicit field + options. */\n statusFilter?: string | { field: string; options?: string[] };\n /** Fields to search across. Defaults to all string-valued fields in data. */\n searchFields?: string[];\n /** Initial filter params applied on mount. */\n initialFilters?: FilterParam[];\n /** Callback when filters change. */\n onFiltersChange?: (filters: FilterParam[]) => void;\n /** Default view mode. */\n defaultView?: ViewMode;\n /** Detail interaction mode. */\n detail?: \"panel\" | \"modal\" | \"route\" | \"none\";\n /** Field used for the detail title. Defaults to first column key. */\n titleField?: string;\n /** Field used for the detail subtitle. Defaults to second column key. */\n subtitleField?: string;\n}\n\nexport interface UseResourcePageReturn<T> {\n /** Filtered and sorted data for display. */\n data: T[];\n /** Total count (of the filtered set). */\n total: number;\n /** Resolved column definitions. */\n columns: ResourceColumnDef<T>[];\n /** Columns mapped to DataTable format. */\n tableColumns: DataTableColumn<T>[];\n /** Ready-to-spread filter config for DataFilters. */\n filterConfig: {\n search: string;\n onSearchChange: (value: string) => void;\n sort: string;\n onSortChange: (sort: string) => void;\n sortOptions: Array<{ value: string; label: string }>;\n statusChips?: {\n options: string[];\n selected: string[];\n onChange: (values: string[]) => void;\n };\n };\n viewMode: ViewMode;\n setViewMode: (mode: ViewMode) => void;\n search: string;\n setSearch: (value: string) => void;\n sort: SortParam | null;\n setSort: (sort: SortParam | null) => void;\n statusValue: string[];\n setStatusValue: (values: string[]) => void;\n selectedItem: T | null;\n setSelectedItem: (item: T | null) => void;\n detailOpen: boolean;\n setDetailOpen: (open: boolean) => void;\n handleRowClick: (item: T) => void;\n titleField: string;\n subtitleField: string;\n statusOptions: string[];\n hasActiveFilters: boolean;\n clearFilters: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a key string to title case.\n * \"created_at\" → \"Created At\"\n * \"firstName\" → \"First Name\"\n */\nexport function titleCase(str: string): string {\n // Split on underscores, hyphens, and camelCase boundaries\n const words = str\n .replace(/([a-z])([A-Z])/g, \"$1 $2\") // camelCase → camel Case\n .replace(/[_-]/g, \" \") // snake_case / kebab-case → spaces\n .split(/\\s+/)\n .filter(Boolean);\n\n return words\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())\n .join(\" \");\n}\n\n/**\n * Resolve column shorthand into full ResourceColumnDef objects.\n * - undefined + data present → auto-generate from Object.keys(data[0]), skip \"id\"\n * - undefined + empty data → empty array\n * - string items → { key, header: titleCase(key), sortable: true }\n * - object items → defaults applied\n */\nfunction resolveColumns<T>(\n columns: Array<string | ResourceColumnDef<T>> | undefined,\n data: T[],\n): ResourceColumnDef<T>[] {\n if (columns === undefined) {\n if (data.length === 0) return [];\n const keys = Object.keys(data[0] as Record<string, unknown>).filter(\n (k) => k !== \"id\",\n );\n return keys.map((key) => ({\n key,\n header: titleCase(key),\n sortable: true,\n }));\n }\n\n return columns.map((col) => {\n if (typeof col === \"string\") {\n return { key: col, header: titleCase(col), sortable: true };\n }\n return {\n ...col,\n header: col.header ?? titleCase(col.key),\n sortable: col.sortable ?? true,\n };\n });\n}\n\n/**\n * Derive the list of string fields in a data set (for default search).\n */\nfunction deriveStringFields<T>(data: T[]): string[] {\n if (data.length === 0) return [];\n const first = data[0] as Record<string, unknown>;\n return Object.keys(first).filter((k) => typeof first[k] === \"string\");\n}\n\n/**\n * Derive unique status options from a data array.\n */\nfunction deriveStatusOptions<T>(data: T[], field: string): string[] {\n const values = new Set<string>();\n for (const item of data) {\n const val = (item as Record<string, unknown>)[field];\n if (val != null) values.add(String(val));\n }\n return Array.from(values).sort();\n}\n\n/**\n * Client-side text search across multiple fields.\n */\nfunction matchesSearch<T>(\n item: T,\n query: string,\n fields: string[],\n): boolean {\n if (!query) return true;\n const lower = query.toLowerCase();\n const record = item as Record<string, unknown>;\n return fields.some((f) => {\n const val = record[f];\n return val != null && String(val).toLowerCase().includes(lower);\n });\n}\n\n/**\n * Client-side sort comparator.\n */\nfunction compareItems<T>(a: T, b: T, sort: SortParam): number {\n const ra = a as Record<string, unknown>;\n const rb = b as Record<string, unknown>;\n const va = ra[sort.field];\n const vb = rb[sort.field];\n const sa = va == null ? \"\" : String(va);\n const sb = vb == null ? \"\" : String(vb);\n const cmp = sa.localeCompare(sb, undefined, {\n numeric: true,\n sensitivity: \"base\",\n });\n return sort.order === \"asc\" ? cmp : -cmp;\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\nexport function useResourcePage<T = Record<string, unknown>>(\n props: UseResourcePageProps<T>,\n): UseResourcePageReturn<T> {\n const {\n resource,\n data: rawData,\n total: rawTotal,\n columns: columnsProp,\n statusFilter: statusFilterProp,\n searchFields: searchFieldsProp,\n defaultView = \"table\",\n detail = \"panel\",\n titleField: titleFieldProp,\n subtitleField: subtitleFieldProp,\n initialFilters,\n onFiltersChange,\n } = props;\n\n // -- View mode --\n const [viewMode, setViewMode] = useViewPreference(\n resource ?? \"resource-page\",\n defaultView,\n );\n\n // -- Derive initial values from initialFilters --\n const initialSearch = useMemo(() => {\n if (!initialFilters) return \"\";\n const searchFilter = initialFilters.find(\n (f) => f.operator === \"contains\" && f.field === \"__search__\",\n );\n return searchFilter && \"value\" in searchFilter ? String(searchFilter.value ?? \"\") : \"\";\n }, []); // eslint-disable-line react-hooks/exhaustive-deps -- only on mount\n\n const initialSort = useMemo(() => {\n if (!initialFilters) return null;\n const sortFilter = initialFilters.find(\n (f) => f.field === \"__sort__\",\n );\n if (!sortFilter || !(\"value\" in sortFilter) || typeof sortFilter.value !== \"string\") return null;\n const [field, order] = sortFilter.value.split(\":\");\n if (!field || (order !== \"asc\" && order !== \"desc\")) return null;\n return { field, order } as SortParam;\n }, []); // eslint-disable-line react-hooks/exhaustive-deps -- only on mount\n\n const initialStatus = useMemo(() => {\n if (!initialFilters) return [];\n const statusFilter = initialFilters.find(\n (f) => f.operator === \"in\" && f.field === \"__status__\",\n );\n if (!statusFilter || statusFilter.operator !== \"in\") return [];\n return statusFilter.value.map((v) => String(v));\n }, []); // eslint-disable-line react-hooks/exhaustive-deps -- only on mount\n\n // -- Search --\n const [search, setSearch] = useState(initialSearch);\n\n // -- Sort --\n const [sort, setSort] = useState<SortParam | null>(initialSort);\n\n // -- Status filter --\n const [statusValue, setStatusValue] = useState<string[]>(initialStatus);\n\n // -- Detail panel --\n const [selectedItem, setSelectedItem] = useState<T | null>(null);\n const [detailOpen, setDetailOpen] = useState(false);\n\n // -- Resolve columns --\n const resolvedColumns = useMemo(\n () => resolveColumns(columnsProp, rawData),\n [columnsProp, rawData],\n );\n\n // -- Title / subtitle fields --\n const titleField =\n titleFieldProp ?? resolvedColumns[0]?.key ?? \"name\";\n const subtitleField =\n subtitleFieldProp ?? resolvedColumns[1]?.key ?? \"\";\n\n // -- Status filter config --\n const statusFieldName = useMemo(() => {\n if (!statusFilterProp) return null;\n return typeof statusFilterProp === \"string\"\n ? statusFilterProp\n : statusFilterProp.field;\n }, [statusFilterProp]);\n\n const statusOptions = useMemo(() => {\n if (!statusFilterProp) return [];\n if (typeof statusFilterProp === \"object\" && statusFilterProp.options) {\n return statusFilterProp.options;\n }\n if (!statusFieldName) return [];\n return deriveStatusOptions(rawData, statusFieldName);\n }, [statusFilterProp, statusFieldName, rawData]);\n\n // -- Search fields --\n const searchFields = useMemo(\n () => searchFieldsProp ?? deriveStringFields(rawData),\n [searchFieldsProp, rawData],\n );\n\n // -- Client-side filtering + sorting --\n const filteredData = useMemo(() => {\n let result = rawData;\n\n // Text search\n if (search) {\n result = result.filter((item) =>\n matchesSearch(item, search, searchFields),\n );\n }\n\n // Status filter\n if (statusFieldName && statusValue.length > 0) {\n result = result.filter((item) => {\n const val = (item as Record<string, unknown>)[statusFieldName];\n return val != null && statusValue.includes(String(val));\n });\n }\n\n // Sort\n if (sort) {\n result = [...result].sort((a, b) => compareItems(a, b, sort));\n }\n\n return result;\n }, [rawData, search, searchFields, statusFieldName, statusValue, sort]);\n\n // -- Sort options (built from resolved columns) --\n const sortOptions = useMemo(() => {\n const opts: Array<{ value: string; label: string }> = [];\n for (const col of resolvedColumns) {\n if (col.sortable !== false) {\n opts.push({\n value: `${col.key}:asc`,\n label: `${col.header ?? titleCase(col.key)} (A-Z)`,\n });\n opts.push({\n value: `${col.key}:desc`,\n label: `${col.header ?? titleCase(col.key)} (Z-A)`,\n });\n }\n }\n return opts;\n }, [resolvedColumns]);\n\n // -- Sort string for DataFilters select --\n const sortString = sort ? `${sort.field}:${sort.order}` : \"\";\n\n const handleSortChange = useCallback((value: string) => {\n if (!value) {\n setSort(null);\n return;\n }\n const [field, order] = value.split(\":\");\n setSort({ field, order: order as \"asc\" | \"desc\" });\n }, []);\n\n // -- Map to DataTableColumn --\n const tableColumns: DataTableColumn<T>[] = useMemo(\n () =>\n resolvedColumns.map((col) => ({\n key: col.key,\n header: col.header ?? titleCase(col.key),\n sortable: col.sortable ?? true,\n hideBelow: col.hideBelow,\n render: col.render\n ? (item: T, value: unknown) =>\n col.render!(item, value, {\n update: async () => {\n throw new Error(\n \"ResourceActions.update not available in server-data mode\",\n );\n },\n remove: async () => {\n throw new Error(\n \"ResourceActions.remove not available in server-data mode\",\n );\n },\n refetch: () => {\n /* no-op in server-data mode */\n },\n })\n : undefined,\n })),\n [resolvedColumns],\n );\n\n // -- Active filters check --\n const hasActiveFilters =\n search !== \"\" || statusValue.length > 0 || sort !== null;\n\n const clearFilters = useCallback(() => {\n setSearch(\"\");\n setStatusValue([]);\n setSort(null);\n }, []);\n\n // -- Notify parent of filter changes --\n const onFiltersChangeRef = useRef(onFiltersChange);\n onFiltersChangeRef.current = onFiltersChange;\n\n useEffect(() => {\n if (!onFiltersChangeRef.current) return;\n const filters: FilterParam[] = [];\n if (search) {\n filters.push({ field: \"__search__\", operator: \"contains\", value: search });\n }\n if (sort) {\n filters.push({ field: \"__sort__\", operator: \"eq\", value: `${sort.field}:${sort.order}` });\n }\n if (statusValue.length > 0) {\n filters.push({ field: \"__status__\", operator: \"in\", value: statusValue });\n }\n onFiltersChangeRef.current(filters);\n }, [search, sort, statusValue]);\n\n // -- Row click --\n const handleRowClick = useCallback(\n (item: T) => {\n if (detail === \"none\") return;\n setSelectedItem(item);\n setDetailOpen(true);\n },\n [detail],\n );\n\n // -- Filter config (ready to spread into DataFilters) --\n const filterConfig = useMemo(\n () => ({\n search,\n onSearchChange: setSearch,\n sort: sortString,\n onSortChange: handleSortChange,\n sortOptions,\n ...(statusFieldName && statusOptions.length > 0\n ? {\n statusChips: {\n options: statusOptions,\n selected: statusValue,\n onChange: setStatusValue,\n },\n }\n : {}),\n }),\n [\n search,\n sortString,\n handleSortChange,\n sortOptions,\n statusFieldName,\n statusOptions,\n statusValue,\n ],\n );\n\n return {\n data: filteredData,\n total: filteredData.length,\n columns: resolvedColumns,\n tableColumns,\n filterConfig,\n viewMode,\n setViewMode,\n search,\n setSearch,\n sort,\n setSort,\n statusValue,\n setStatusValue,\n selectedItem,\n setSelectedItem,\n detailOpen,\n setDetailOpen,\n handleRowClick,\n titleField,\n subtitleField,\n statusOptions,\n hasActiveFilters,\n clearFilters,\n };\n}\n"]}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from './chunk-NZ5WBA4R.js';
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import
|
|
4
|
+
import { DropdownMenu as DropdownMenu$1 } from 'radix-ui';
|
|
5
5
|
import { ChevronRight, Check, Circle } from 'lucide-react';
|
|
6
6
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
7
7
|
|
|
8
|
-
var DropdownMenu =
|
|
9
|
-
var DropdownMenuTrigger =
|
|
10
|
-
var DropdownMenuGroup =
|
|
11
|
-
var DropdownMenuPortal =
|
|
12
|
-
var DropdownMenuSub =
|
|
13
|
-
var DropdownMenuRadioGroup =
|
|
8
|
+
var DropdownMenu = DropdownMenu$1.Root;
|
|
9
|
+
var DropdownMenuTrigger = DropdownMenu$1.Trigger;
|
|
10
|
+
var DropdownMenuGroup = DropdownMenu$1.Group;
|
|
11
|
+
var DropdownMenuPortal = DropdownMenu$1.Portal;
|
|
12
|
+
var DropdownMenuSub = DropdownMenu$1.Sub;
|
|
13
|
+
var DropdownMenuRadioGroup = DropdownMenu$1.RadioGroup;
|
|
14
14
|
var DropdownMenuSubTrigger = React.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
15
|
-
|
|
15
|
+
DropdownMenu$1.SubTrigger,
|
|
16
16
|
{
|
|
17
17
|
ref,
|
|
18
18
|
className: cn(
|
|
@@ -27,9 +27,9 @@ var DropdownMenuSubTrigger = React.forwardRef(({ className, inset, children, ...
|
|
|
27
27
|
]
|
|
28
28
|
}
|
|
29
29
|
));
|
|
30
|
-
DropdownMenuSubTrigger.displayName =
|
|
30
|
+
DropdownMenuSubTrigger.displayName = DropdownMenu$1.SubTrigger.displayName;
|
|
31
31
|
var DropdownMenuSubContent = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
32
|
-
|
|
32
|
+
DropdownMenu$1.SubContent,
|
|
33
33
|
{
|
|
34
34
|
ref,
|
|
35
35
|
className: cn(
|
|
@@ -39,9 +39,9 @@ var DropdownMenuSubContent = React.forwardRef(({ className, ...props }, ref) =>
|
|
|
39
39
|
...props
|
|
40
40
|
}
|
|
41
41
|
));
|
|
42
|
-
DropdownMenuSubContent.displayName =
|
|
43
|
-
var DropdownMenuContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
44
|
-
|
|
42
|
+
DropdownMenuSubContent.displayName = DropdownMenu$1.SubContent.displayName;
|
|
43
|
+
var DropdownMenuContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(DropdownMenu$1.Portal, { children: /* @__PURE__ */ jsx(
|
|
44
|
+
DropdownMenu$1.Content,
|
|
45
45
|
{
|
|
46
46
|
ref,
|
|
47
47
|
sideOffset,
|
|
@@ -52,9 +52,9 @@ var DropdownMenuContent = React.forwardRef(({ className, sideOffset = 4, ...prop
|
|
|
52
52
|
...props
|
|
53
53
|
}
|
|
54
54
|
) }));
|
|
55
|
-
DropdownMenuContent.displayName =
|
|
55
|
+
DropdownMenuContent.displayName = DropdownMenu$1.Content.displayName;
|
|
56
56
|
var DropdownMenuItem = React.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
57
|
-
|
|
57
|
+
DropdownMenu$1.Item,
|
|
58
58
|
{
|
|
59
59
|
ref,
|
|
60
60
|
className: cn(
|
|
@@ -65,9 +65,9 @@ var DropdownMenuItem = React.forwardRef(({ className, inset, ...props }, ref) =>
|
|
|
65
65
|
...props
|
|
66
66
|
}
|
|
67
67
|
));
|
|
68
|
-
DropdownMenuItem.displayName =
|
|
68
|
+
DropdownMenuItem.displayName = DropdownMenu$1.Item.displayName;
|
|
69
69
|
var DropdownMenuCheckboxItem = React.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
70
|
-
|
|
70
|
+
DropdownMenu$1.CheckboxItem,
|
|
71
71
|
{
|
|
72
72
|
ref,
|
|
73
73
|
className: cn(
|
|
@@ -77,14 +77,14 @@ var DropdownMenuCheckboxItem = React.forwardRef(({ className, children, checked,
|
|
|
77
77
|
checked,
|
|
78
78
|
...props,
|
|
79
79
|
children: [
|
|
80
|
-
/* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
80
|
+
/* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(DropdownMenu$1.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }) }),
|
|
81
81
|
children
|
|
82
82
|
]
|
|
83
83
|
}
|
|
84
84
|
));
|
|
85
|
-
DropdownMenuCheckboxItem.displayName =
|
|
85
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenu$1.CheckboxItem.displayName;
|
|
86
86
|
var DropdownMenuRadioItem = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
87
|
-
|
|
87
|
+
DropdownMenu$1.RadioItem,
|
|
88
88
|
{
|
|
89
89
|
ref,
|
|
90
90
|
className: cn(
|
|
@@ -93,28 +93,28 @@ var DropdownMenuRadioItem = React.forwardRef(({ className, children, ...props },
|
|
|
93
93
|
),
|
|
94
94
|
...props,
|
|
95
95
|
children: [
|
|
96
|
-
/* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
96
|
+
/* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(DropdownMenu$1.ItemIndicator, { children: /* @__PURE__ */ jsx(Circle, { className: "h-2 w-2 fill-current" }) }) }),
|
|
97
97
|
children
|
|
98
98
|
]
|
|
99
99
|
}
|
|
100
100
|
));
|
|
101
|
-
DropdownMenuRadioItem.displayName =
|
|
101
|
+
DropdownMenuRadioItem.displayName = DropdownMenu$1.RadioItem.displayName;
|
|
102
102
|
var DropdownMenuLabel = React.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
103
|
-
|
|
103
|
+
DropdownMenu$1.Label,
|
|
104
104
|
{
|
|
105
105
|
ref,
|
|
106
106
|
className: cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className),
|
|
107
107
|
...props
|
|
108
108
|
}
|
|
109
109
|
));
|
|
110
|
-
DropdownMenuLabel.displayName =
|
|
111
|
-
var DropdownMenuSeparator = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
112
|
-
DropdownMenuSeparator.displayName =
|
|
110
|
+
DropdownMenuLabel.displayName = DropdownMenu$1.Label.displayName;
|
|
111
|
+
var DropdownMenuSeparator = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DropdownMenu$1.Separator, { ref, className: cn("-mx-1 my-1 h-px bg-muted", className), ...props }));
|
|
112
|
+
DropdownMenuSeparator.displayName = DropdownMenu$1.Separator.displayName;
|
|
113
113
|
var DropdownMenuShortcut = ({ className, ...props }) => {
|
|
114
114
|
return /* @__PURE__ */ jsx("span", { className: cn("ml-auto text-xs tracking-widest opacity-60", className), ...props });
|
|
115
115
|
};
|
|
116
116
|
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
|
117
117
|
|
|
118
118
|
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger };
|
|
119
|
-
//# sourceMappingURL=chunk-
|
|
120
|
-
//# sourceMappingURL=chunk-
|
|
119
|
+
//# sourceMappingURL=chunk-QJ6P2DW6.js.map
|
|
120
|
+
//# sourceMappingURL=chunk-QJ6P2DW6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ui/dropdown-menu.tsx"],"names":["DropdownMenuPrimitive"],"mappings":"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","file":"chunk-QJ6P2DW6.js","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\nimport { Check, ChevronRight, Circle } from \"lucide-react\";\n\nimport { cn } from \"../../utils\";\n\nconst DropdownMenu = DropdownMenuPrimitive.Root;\nconst DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;\nconst DropdownMenuGroup = DropdownMenuPrimitive.Group;\nconst DropdownMenuPortal = DropdownMenuPrimitive.Portal;\nconst DropdownMenuSub = DropdownMenuPrimitive.Sub;\nconst DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & { inset?: boolean }\n>(({ className, inset, children, ...props }, ref) => (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n className={cn(\n \"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[state=open]:bg-accent focus:bg-accent\",\n inset && \"pl-8\",\n className,\n )}\n {...props}\n >\n {children}\n <ChevronRight className=\"ml-auto h-4 w-4\" />\n </DropdownMenuPrimitive.SubTrigger>\n));\nDropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;\n\nconst DropdownMenuSubContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n className={cn(\n \"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n className,\n )}\n {...props}\n />\n));\nDropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;\n\nconst DropdownMenuContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n className,\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n));\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;\n\nconst DropdownMenuItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { inset?: boolean }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground\",\n inset && \"pl-8\",\n className,\n )}\n {...props}\n />\n));\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>\n>(({ className, children, checked, ...props }, ref) => (\n <DropdownMenuPrimitive.CheckboxItem\n ref={ref}\n className={cn(\n \"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground\",\n className,\n )}\n checked={checked}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <Check className=\"h-4 w-4\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n));\nDropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;\n\nconst DropdownMenuRadioItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n <DropdownMenuPrimitive.RadioItem\n ref={ref}\n className={cn(\n \"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground\",\n className,\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <Circle className=\"h-2 w-2 fill-current\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.RadioItem>\n));\nDropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;\n\nconst DropdownMenuLabel = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & { inset?: boolean }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Label\n ref={ref}\n className={cn(\"px-2 py-1.5 text-sm font-semibold\", inset && \"pl-8\", className)}\n {...props}\n />\n));\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;\n\nconst DropdownMenuSeparator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator ref={ref} className={cn(\"-mx-1 my-1 h-px bg-muted\", className)} {...props} />\n));\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;\n\nconst DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {\n return <span className={cn(\"ml-auto text-xs tracking-widest opacity-60\", className)} {...props} />;\n};\nDropdownMenuShortcut.displayName = \"DropdownMenuShortcut\";\n\nexport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuRadioItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuGroup,\n DropdownMenuPortal,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuRadioGroup,\n};\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Label } from './chunk-
|
|
1
|
+
import { Label } from './chunk-F47U5KNW.js';
|
|
2
2
|
import { cn } from './chunk-NZ5WBA4R.js';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
@@ -55,5 +55,5 @@ function FormDescription({
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export { FormDescription, FormError, FormField, FormLabel };
|
|
58
|
-
//# sourceMappingURL=chunk-
|
|
59
|
-
//# sourceMappingURL=chunk-
|
|
58
|
+
//# sourceMappingURL=chunk-R7QFP6EL.js.map
|
|
59
|
+
//# sourceMappingURL=chunk-R7QFP6EL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/patterns/form-field.tsx"],"names":[],"mappings":";;;;;AAQA,SAAS,SAAA,CAAU;AAAA,EACjB,SAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAyC;AACvC,EAAA,uBACE,GAAA,CAAC,SAAI,SAAA,EAAW,EAAA,CAAG,aAAa,SAAS,CAAA,EAAI,GAAG,KAAA,EAC7C,QAAA,EACH,CAAA;AAEJ;AAWA,IAAM,SAAA,GAAkB,KAAA,CAAA,UAAA,CAGtB,CAAC,EAAE,SAAA,EAAW,KAAA,EAAO,QAAA,EAAU,QAAA,EAAU,GAAG,KAAA,EAAM,EAAG,GAAA,qBACrD,IAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACC,GAAA;AAAA,IACA,SAAA,EAAW,EAAA,CAAG,KAAA,IAAS,kBAAA,EAAoB,SAAS,CAAA;AAAA,IACnD,GAAG,KAAA;AAAA,IAEH,QAAA,EAAA;AAAA,MAAA,QAAA;AAAA,MACA,QAAA,oBAAY,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,yBAAwB,QAAA,EAAA,GAAA,EAAC;AAAA;AAAA;AACxD,CACD;AACD,SAAA,CAAU,WAAA,GAAc,WAAA;AAMxB,SAAS,SAAA,CAAU;AAAA,EACjB,SAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAG;AACL,CAAA,EAA+C;AAC7C,EAAA,IAAI,CAAC,UAAU,OAAO,IAAA;AACtB,EAAA,uBACE,GAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA,CAAG,sCAAA,EAAwC,SAAS,CAAA;AAAA,MAC9D,GAAG,KAAA;AAAA,MAEH;AAAA;AAAA,GACH;AAEJ;AAMA,SAAS,eAAA,CAAgB;AAAA,EACvB,SAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAG;AACL,CAAA,EAA+C;AAC7C,EAAA,IAAI,CAAC,UAAU,OAAO,IAAA;AACtB,EAAA,uBACE,GAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA,CAAG,+BAAA,EAAiC,SAAS,CAAA;AAAA,MACvD,GAAG,KAAA;AAAA,MAEH;AAAA;AAAA,GACH;AAEJ","file":"chunk-R7QFP6EL.js","sourcesContent":["import * as React from \"react\";\nimport { Label } from \"../ui/label\";\nimport { cn } from \"../../utils\";\n\n// ---------------------------------------------------------------------------\n// FormField — wrapper div for consistent form field layout\n// ---------------------------------------------------------------------------\n\nfunction FormField({\n className,\n children,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n return (\n <div className={cn(\"space-y-2\", className)} {...props}>\n {children}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// FormLabel — label with error styling support\n// ---------------------------------------------------------------------------\n\ninterface FormLabelProps extends React.ComponentPropsWithoutRef<typeof Label> {\n error?: boolean;\n required?: boolean;\n}\n\nconst FormLabel = React.forwardRef<\n React.ComponentRef<typeof Label>,\n FormLabelProps\n>(({ className, error, required, children, ...props }, ref) => (\n <Label\n ref={ref}\n className={cn(error && \"text-destructive\", className)}\n {...props}\n >\n {children}\n {required && <span className=\"text-destructive ml-1\">*</span>}\n </Label>\n));\nFormLabel.displayName = \"FormLabel\";\n\n// ---------------------------------------------------------------------------\n// FormError — error message display\n// ---------------------------------------------------------------------------\n\nfunction FormError({\n className,\n children,\n ...props\n}: React.HTMLAttributes<HTMLParagraphElement>) {\n if (!children) return null;\n return (\n <p\n className={cn(\"text-sm font-medium text-destructive\", className)}\n {...props}\n >\n {children}\n </p>\n );\n}\n\n// ---------------------------------------------------------------------------\n// FormDescription — help text\n// ---------------------------------------------------------------------------\n\nfunction FormDescription({\n className,\n children,\n ...props\n}: React.HTMLAttributes<HTMLParagraphElement>) {\n if (!children) return null;\n return (\n <p\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n >\n {children}\n </p>\n );\n}\n\nexport { FormField, FormLabel, FormError, FormDescription };\n"]}
|