@dirstack/ui 1.0.1 → 1.1.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.
@@ -26,16 +26,16 @@ declare const dialogContentVariants: import("tailwind-variants").TVReturnType<{
26
26
  * (`[[role=dialog]~&]`) sits a little lower still, so both remain visible.
27
27
  */
28
28
  fixed: {
29
- true: "top-[10vh] max-h-[calc(90vh-2rem)] [[role=dialog]~&]:top-[15vh] [[role=dialog]~&]:max-h-[calc(85vh-2rem)]";
30
- false: "top-1/2 -translate-y-1/2 max-h-[calc(100vh-2rem)]";
29
+ true: "sm:top-[10vh] sm:max-h-[calc(90vh-2rem)] sm:[[role=dialog]~&]:top-[15vh] sm:[[role=dialog]~&]:max-h-[calc(85vh-2rem)]";
30
+ false: "sm:top-1/2 sm:-translate-y-1/2 sm:max-h-[calc(100vh-2rem)]";
31
31
  };
32
32
  /**
33
33
  * Drop the dialog's own padding and gap so sections can run edge to edge and draw their
34
34
  * own borders, e.g. a bordered header over a scrolling body over a bordered footer.
35
35
  */
36
36
  flush: {
37
- true: "gap-0 p-0";
38
- false: "gap-6 p-6";
37
+ true: "gap-0 p-0 max-sm:pb-[env(safe-area-inset-bottom)]";
38
+ false: "gap-6 p-6 max-sm:pb-[calc(1.5rem+env(safe-area-inset-bottom))]";
39
39
  };
40
40
  }, undefined, string[], {
41
41
  size: {
@@ -55,16 +55,16 @@ declare const dialogContentVariants: import("tailwind-variants").TVReturnType<{
55
55
  * (`[[role=dialog]~&]`) sits a little lower still, so both remain visible.
56
56
  */
57
57
  fixed: {
58
- true: "top-[10vh] max-h-[calc(90vh-2rem)] [[role=dialog]~&]:top-[15vh] [[role=dialog]~&]:max-h-[calc(85vh-2rem)]";
59
- false: "top-1/2 -translate-y-1/2 max-h-[calc(100vh-2rem)]";
58
+ true: "sm:top-[10vh] sm:max-h-[calc(90vh-2rem)] sm:[[role=dialog]~&]:top-[15vh] sm:[[role=dialog]~&]:max-h-[calc(85vh-2rem)]";
59
+ false: "sm:top-1/2 sm:-translate-y-1/2 sm:max-h-[calc(100vh-2rem)]";
60
60
  };
61
61
  /**
62
62
  * Drop the dialog's own padding and gap so sections can run edge to edge and draw their
63
63
  * own borders, e.g. a bordered header over a scrolling body over a bordered footer.
64
64
  */
65
65
  flush: {
66
- true: "gap-0 p-0";
67
- false: "gap-6 p-6";
66
+ true: "gap-0 p-0 max-sm:pb-[env(safe-area-inset-bottom)]";
67
+ false: "gap-6 p-6 max-sm:pb-[calc(1.5rem+env(safe-area-inset-bottom))]";
68
68
  };
69
69
  }, undefined, import("tailwind-variants").TVReturnTypeLike<{
70
70
  size: {
@@ -84,16 +84,16 @@ declare const dialogContentVariants: import("tailwind-variants").TVReturnType<{
84
84
  * (`[[role=dialog]~&]`) sits a little lower still, so both remain visible.
85
85
  */
86
86
  fixed: {
87
- true: "top-[10vh] max-h-[calc(90vh-2rem)] [[role=dialog]~&]:top-[15vh] [[role=dialog]~&]:max-h-[calc(85vh-2rem)]";
88
- false: "top-1/2 -translate-y-1/2 max-h-[calc(100vh-2rem)]";
87
+ true: "sm:top-[10vh] sm:max-h-[calc(90vh-2rem)] sm:[[role=dialog]~&]:top-[15vh] sm:[[role=dialog]~&]:max-h-[calc(85vh-2rem)]";
88
+ false: "sm:top-1/2 sm:-translate-y-1/2 sm:max-h-[calc(100vh-2rem)]";
89
89
  };
90
90
  /**
91
91
  * Drop the dialog's own padding and gap so sections can run edge to edge and draw their
92
92
  * own borders, e.g. a bordered header over a scrolling body over a bordered footer.
93
93
  */
94
94
  flush: {
95
- true: "gap-0 p-0";
96
- false: "gap-6 p-6";
95
+ true: "gap-0 p-0 max-sm:pb-[env(safe-area-inset-bottom)]";
96
+ false: "gap-6 p-6 max-sm:pb-[calc(1.5rem+env(safe-area-inset-bottom))]";
97
97
  };
98
98
  }, undefined>>;
99
99
  type DialogContentProps = Omit<Dialog$1.Popup.Props, "className"> & VariantProps<typeof dialogContentVariants> & {
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.d.ts","names":[],"sources":["../../src/components/dialog.tsx"],"mappings":";;;;;;cAcM,eAAM,SAAA;cACN,eAAa,SAAA;cACb,6BAAW,0BAAA,8BAAA,gDAAA,cAAA;cACX,mCAAiB,0BAAA,8BAAA,sDAAA,cAAA;cAiBjB,mDAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAiDtB,qBAAqB,KAAK,SAAgB,MAAM,sBACnD,oBAAoB;EAClB;;;;;EAMA;;iBAGK,gBACP,WACA,UACA,MACA,OACA,OACA,cACG,SACF,qCAAkB,IAAA;cAsBf,kDAAoB;;;;;;;;;;;;;;;;;;;;;;;;;KAYrB,oBAAoB,KAAK,sBAAsB,2CAClD,oBAAoB;EAClB,OAAO;EACP,cAAc;;;;;;;iBAQT,eACP,OACA,aACA,UACA,WACA,aACG,SACF,oCAAiB,IAAA;cAgBd,kDAAoB;;;;;;;;;;;;;;;;;;;;;;;;;KAcrB,oBAAoB,wBAAwB,oBAAoB;iBAE5D,eAAe,WAAW,aAAa,SAAS,oCAAiB,IAAA;iBAIjE,iBAAiB,SAAS,sBAAsB,0BAAO,IAAA"}
1
+ {"version":3,"file":"dialog.d.ts","names":[],"sources":["../../src/components/dialog.tsx"],"mappings":";;;;;;cAcM,eAAM,SAAA;cACN,eAAa,SAAA;cACb,6BAAW,0BAAA,8BAAA,gDAAA,cAAA;cACX,mCAAiB,0BAAA,8BAAA,sDAAA,cAAA;cAiBjB,mDAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAqDtB,qBAAqB,KAAK,SAAgB,MAAM,sBACnD,oBAAoB;EAClB;;;;;EAMA;;iBAGK,gBACP,WACA,UACA,MACA,OACA,OACA,cACG,SACF,qCAAkB,IAAA;cAsBf,kDAAoB;;;;;;;;;;;;;;;;;;;;;;;;;KAYrB,oBAAoB,KAAK,sBAAsB,2CAClD,oBAAoB;EAClB,OAAO;EACP,cAAc;;;;;;;iBAQT,eACP,OACA,aACA,UACA,WACA,aACG,SACF,oCAAiB,IAAA;cAgBd,kDAAoB;;;;;;;;;;;;;;;;;;;;;;;;;KAcrB,oBAAoB,wBAAwB,oBAAoB;iBAE5D,eAAe,WAAW,aAAa,SAAS,oCAAiB,IAAA;iBAIjE,iBAAiB,SAAS,sBAAsB,0BAAO,IAAA"}
@@ -19,11 +19,13 @@ function DialogOverlay({ className, ...props }) {
19
19
  }
20
20
  const dialogContentVariants = variants({
21
21
  base: [
22
- "fixed left-1/2 z-50 grid w-[calc(100vw-2rem)] -translate-x-1/2 overflow-y-auto",
23
- "border bg-card shadow-sm rounded-xl",
22
+ "fixed z-50 grid overflow-y-auto border bg-card shadow-sm",
23
+ "max-sm:inset-x-0 max-sm:bottom-0 max-sm:max-h-[calc(100dvh-3rem)] max-sm:rounded-t-2xl max-sm:border-b-0",
24
+ "sm:left-1/2 sm:w-[calc(100vw-2rem)] sm:-translate-x-1/2 sm:rounded-xl",
24
25
  "data-open:animate-in data-closed:animate-out",
25
26
  "data-open:fade-in-0 data-closed:fade-out-0",
26
- "data-open:slide-in-from-bottom-4 data-closed:slide-out-to-bottom-4"
27
+ "data-open:slide-in-from-bottom-4 data-closed:slide-out-to-bottom-4",
28
+ "max-sm:data-open:slide-in-from-bottom-8 max-sm:data-closed:slide-out-to-bottom-8"
27
29
  ],
28
30
  variants: {
29
31
  size: {
@@ -43,16 +45,16 @@ const dialogContentVariants = variants({
43
45
  * (`[[role=dialog]~&]`) sits a little lower still, so both remain visible.
44
46
  */
45
47
  fixed: {
46
- true: "top-[10vh] max-h-[calc(90vh-2rem)] [[role=dialog]~&]:top-[15vh] [[role=dialog]~&]:max-h-[calc(85vh-2rem)]",
47
- false: "top-1/2 -translate-y-1/2 max-h-[calc(100vh-2rem)]"
48
+ true: "sm:top-[10vh] sm:max-h-[calc(90vh-2rem)] sm:[[role=dialog]~&]:top-[15vh] sm:[[role=dialog]~&]:max-h-[calc(85vh-2rem)]",
49
+ false: "sm:top-1/2 sm:-translate-y-1/2 sm:max-h-[calc(100vh-2rem)]"
48
50
  },
49
51
  /**
50
52
  * Drop the dialog's own padding and gap so sections can run edge to edge and draw their
51
53
  * own borders, e.g. a bordered header over a scrolling body over a bordered footer.
52
54
  */
53
55
  flush: {
54
- true: "gap-0 p-0",
55
- false: "gap-6 p-6"
56
+ true: "gap-0 p-0 max-sm:pb-[env(safe-area-inset-bottom)]",
57
+ false: "gap-6 p-6 max-sm:pb-[calc(1.5rem+env(safe-area-inset-bottom))]"
56
58
  }
57
59
  },
58
60
  defaultVariants: {
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.js","names":["DialogPrimitive"],"sources":["../../src/components/dialog.tsx"],"sourcesContent":["\"use client\"\n\nimport { Dialog as DialogPrimitive } from \"@base-ui/react/dialog\"\nimport type { ComponentProps, ReactNode } from \"react\"\nimport { Button } from \"~/components/button\"\nimport {\n SectionHeader,\n SectionHeaderBody,\n SectionHeaderDescription,\n SectionHeaderTitle,\n} from \"~/components/section-header\"\nimport { XIcon } from \"~/lib/icons\"\nimport { variants, cn, type VariantProps } from \"~/lib/variants\"\n\nconst Dialog = DialogPrimitive.Root\nconst DialogTrigger = DialogPrimitive.Trigger\nconst DialogTitle = DialogPrimitive.Title\nconst DialogDescription = DialogPrimitive.Description\n\n/** The dashed, blurred backdrop behind the dialog. Rendered by `DialogContent`. */\nfunction DialogOverlay({ className, ...props }: DialogPrimitive.Backdrop.Props) {\n return (\n <DialogPrimitive.Backdrop\n className={cn(\n \"fixed inset-0 z-50 bg-dashed backdrop-blur-xs\",\n \"data-open:animate-in data-closed:animate-out\",\n \"data-open:fade-in-0 data-closed:fade-out-0\",\n className,\n )}\n {...props}\n />\n )\n}\n\nconst dialogContentVariants = variants({\n base: [\n \"fixed left-1/2 z-50 grid w-[calc(100vw-2rem)] -translate-x-1/2 overflow-y-auto\",\n \"border bg-card shadow-sm rounded-xl\",\n \"data-open:animate-in data-closed:animate-out\",\n \"data-open:fade-in-0 data-closed:fade-out-0\",\n \"data-open:slide-in-from-bottom-4 data-closed:slide-out-to-bottom-4\",\n ],\n\n variants: {\n size: {\n xs: \"max-w-xs\",\n sm: \"max-w-sm\",\n md: \"max-w-md\",\n lg: \"max-w-lg\",\n xl: \"max-w-xl\",\n \"2xl\": \"max-w-2xl\",\n \"3xl\": \"max-w-3xl\",\n \"4xl\": \"max-w-4xl\",\n \"5xl\": \"max-w-5xl\",\n },\n\n /**\n * Pin the dialog near the top of the viewport instead of centering it, so its height can\n * change without the whole thing jumping. A dialog stacked on top of another one\n * (`[[role=dialog]~&]`) sits a little lower still, so both remain visible.\n */\n fixed: {\n true: \"top-[10vh] max-h-[calc(90vh-2rem)] [[role=dialog]~&]:top-[15vh] [[role=dialog]~&]:max-h-[calc(85vh-2rem)]\",\n false: \"top-1/2 -translate-y-1/2 max-h-[calc(100vh-2rem)]\",\n },\n\n /**\n * Drop the dialog's own padding and gap so sections can run edge to edge and draw their\n * own borders, e.g. a bordered header over a scrolling body over a bordered footer.\n */\n flush: {\n true: \"gap-0 p-0\",\n false: \"gap-6 p-6\",\n },\n },\n\n defaultVariants: {\n size: \"md\",\n fixed: true,\n flush: false,\n },\n})\n\ntype DialogContentProps = Omit<DialogPrimitive.Popup.Props, \"className\"> &\n VariantProps<typeof dialogContentVariants> & {\n className?: string\n\n /**\n * Hide the close button in the top right corner, for a dialog that must be dismissed\n * through one of its own actions.\n */\n hideClose?: boolean\n }\n\nfunction DialogContent({\n className,\n children,\n size,\n fixed,\n flush,\n hideClose,\n ...props\n}: DialogContentProps) {\n return (\n <DialogPrimitive.Portal>\n <DialogOverlay />\n\n <DialogPrimitive.Popup\n className={dialogContentVariants({ size, fixed, flush, className })}\n {...props}\n >\n {children}\n\n {!hideClose && (\n <DialogPrimitive.Close\n render={<Button variant=\"ghost\" size=\"sm\" aria-label=\"Close\" prefix={<XIcon />} />}\n className=\"absolute top-3 right-3\"\n />\n )}\n </DialogPrimitive.Popup>\n </DialogPrimitive.Portal>\n )\n}\n\nconst dialogHeaderVariants = variants({\n variants: {\n /**\n * Give the header its own hairline and padding, for a `flush` dialog whose body scrolls\n * under it. The extra right padding clears the dialog's close button.\n */\n bordered: {\n true: \"border-b px-6 py-5 pr-14\",\n },\n },\n})\n\ntype DialogHeaderProps = Omit<ComponentProps<typeof SectionHeader>, \"title\" | \"description\"> &\n VariantProps<typeof dialogHeaderVariants> & {\n title: ReactNode\n description?: ReactNode\n }\n\n/**\n * The standard dialog heading: a card-scale `SectionHeader` whose title and description are wired to\n * Base UI's `DialogTitle` / `DialogDescription` for accessibility. Extra children render after\n * them.\n */\nfunction DialogHeader({\n title,\n description,\n bordered,\n className,\n children,\n ...props\n}: DialogHeaderProps) {\n return (\n <SectionHeader size=\"card\" className={dialogHeaderVariants({ bordered, className })} {...props}>\n <SectionHeaderBody>\n <DialogTitle render={<SectionHeaderTitle />}>{title}</DialogTitle>\n\n {description && (\n <DialogDescription render={<SectionHeaderDescription />}>{description}</DialogDescription>\n )}\n </SectionHeaderBody>\n\n {children}\n </SectionHeader>\n )\n}\n\nconst dialogFooterVariants = variants({\n base: \"flex flex-col-reverse flex-wrap gap-x-2 gap-y-4 sm:flex-row sm:justify-end\",\n\n variants: {\n /**\n * Give the footer its own hairline and padding, for a `flush` dialog where the body\n * scrolls behind it.\n */\n bordered: {\n true: \"border-t px-6 py-4\",\n },\n },\n})\n\ntype DialogFooterProps = ComponentProps<\"div\"> & VariantProps<typeof dialogFooterVariants>\n\nfunction DialogFooter({ className, bordered, ...props }: DialogFooterProps) {\n return <div className={dialogFooterVariants({ bordered, className })} {...props} />\n}\n\nfunction DialogClose({ ...props }: ComponentProps<typeof Button>) {\n return <DialogPrimitive.Close render={<Button variant=\"secondary\" {...props} />} />\n}\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n}\n"],"mappings":";;;;;;;;AAcA,MAAM,SAASA,SAAgB;AAC/B,MAAM,gBAAgBA,SAAgB;AACtC,MAAM,cAAcA,SAAgB;AACpC,MAAM,oBAAoBA,SAAgB;;AAG1C,SAAS,cAAc,EAAE,WAAW,GAAG,SAAyC;CAC9E,OACE,oBAACA,SAAgB,UAAjB;EACE,WAAW,GACT,iDACA,gDACA,8CACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,MAAM,wBAAwB,SAAS;CACrC,MAAM;EACJ;EACA;EACA;EACA;EACA;CACF;CAEA,UAAU;EACR,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,OAAO;GACP,OAAO;GACP,OAAO;GACP,OAAO;EACT;;;;;;EAOA,OAAO;GACL,MAAM;GACN,OAAO;EACT;;;;;EAMA,OAAO;GACL,MAAM;GACN,OAAO;EACT;CACF;CAEA,iBAAiB;EACf,MAAM;EACN,OAAO;EACP,OAAO;CACT;AACF,CAAC;AAaD,SAAS,cAAc,EACrB,WACA,UACA,MACA,OACA,OACA,WACA,GAAG,SACkB;CACrB,OACE,qBAACA,SAAgB,QAAjB,EAAA,UAAA,CACE,oBAAC,eAAD,CAAgB,CAAA,GAEhB,qBAACA,SAAgB,OAAjB;EACE,WAAW,sBAAsB;GAAE;GAAM;GAAO;GAAO;EAAU,CAAC;EAClE,GAAI;EAFN,UAAA,CAIG,UAEA,CAAC,aACA,oBAACA,SAAgB,OAAjB;GACE,QAAQ,oBAAC,QAAD;IAAQ,SAAQ;IAAQ,MAAK;IAAK,cAAW;IAAQ,QAAQ,oBAAC,OAAD,CAAQ,CAAA;GAAI,CAAA;GACjF,WAAU;EACX,CAAA,CAEkB;CACD,CAAA,CAAA,EAAA,CAAA;AAE5B;AAEA,MAAM,uBAAuB,SAAS,EACpC,UAAU;;;;;AAKR,UAAU,EACR,MAAM,2BACR,EACF,EACF,CAAC;;;;;;AAaD,SAAS,aAAa,EACpB,OACA,aACA,UACA,WACA,UACA,GAAG,SACiB;CACpB,OACE,qBAAC,eAAD;EAAe,MAAK;EAAO,WAAW,qBAAqB;GAAE;GAAU;EAAU,CAAC;EAAG,GAAI;EAAzF,UAAA,CACE,qBAAC,mBAAD,EAAA,UAAA,CACE,oBAAC,aAAD;GAAa,QAAQ,oBAAC,oBAAD,CAAqB,CAAA;GAAI,UAAA;EAAmB,CAAA,GAEhE,eACC,oBAAC,mBAAD;GAAmB,QAAQ,oBAAC,0BAAD,CAA2B,CAAA;GAAI,UAAA;EAA+B,CAAA,CAE1E,EAAA,CAAA,GAElB,QACY;;AAEnB;AAEA,MAAM,uBAAuB,SAAS;CACpC,MAAM;CAEN,UAAU;;;;;AAKR,UAAU,EACR,MAAM,qBACR,EACF;AACF,CAAC;AAID,SAAS,aAAa,EAAE,WAAW,UAAU,GAAG,SAA4B;CAC1E,OAAO,oBAAC,OAAD;EAAK,WAAW,qBAAqB;GAAE;GAAU;EAAU,CAAC;EAAG,GAAI;CAAQ,CAAA;AACpF;AAEA,SAAS,YAAY,EAAE,GAAG,SAAwC;CAChE,OAAO,oBAACA,SAAgB,OAAjB,EAAuB,QAAQ,oBAAC,QAAD;EAAQ,SAAQ;EAAY,GAAI;CAAQ,CAAA,EAAI,CAAA;AACpF"}
1
+ {"version":3,"file":"dialog.js","names":["DialogPrimitive"],"sources":["../../src/components/dialog.tsx"],"sourcesContent":["\"use client\"\n\nimport { Dialog as DialogPrimitive } from \"@base-ui/react/dialog\"\nimport type { ComponentProps, ReactNode } from \"react\"\nimport { Button } from \"~/components/button\"\nimport {\n SectionHeader,\n SectionHeaderBody,\n SectionHeaderDescription,\n SectionHeaderTitle,\n} from \"~/components/section-header\"\nimport { XIcon } from \"~/lib/icons\"\nimport { variants, cn, type VariantProps } from \"~/lib/variants\"\n\nconst Dialog = DialogPrimitive.Root\nconst DialogTrigger = DialogPrimitive.Trigger\nconst DialogTitle = DialogPrimitive.Title\nconst DialogDescription = DialogPrimitive.Description\n\n/** The dashed, blurred backdrop behind the dialog. Rendered by `DialogContent`. */\nfunction DialogOverlay({ className, ...props }: DialogPrimitive.Backdrop.Props) {\n return (\n <DialogPrimitive.Backdrop\n className={cn(\n \"fixed inset-0 z-50 bg-dashed backdrop-blur-xs\",\n \"data-open:animate-in data-closed:animate-out\",\n \"data-open:fade-in-0 data-closed:fade-out-0\",\n className,\n )}\n {...props}\n />\n )\n}\n\nconst dialogContentVariants = variants({\n base: [\n \"fixed z-50 grid overflow-y-auto border bg-card shadow-sm\",\n // Under `sm` the dialog is a bottom sheet: full width, pinned to the bottom edge,\n // padded past the home indicator.\n \"max-sm:inset-x-0 max-sm:bottom-0 max-sm:max-h-[calc(100dvh-3rem)] max-sm:rounded-t-2xl max-sm:border-b-0\",\n \"sm:left-1/2 sm:w-[calc(100vw-2rem)] sm:-translate-x-1/2 sm:rounded-xl\",\n \"data-open:animate-in data-closed:animate-out\",\n \"data-open:fade-in-0 data-closed:fade-out-0\",\n \"data-open:slide-in-from-bottom-4 data-closed:slide-out-to-bottom-4\",\n \"max-sm:data-open:slide-in-from-bottom-8 max-sm:data-closed:slide-out-to-bottom-8\",\n ],\n\n variants: {\n size: {\n xs: \"max-w-xs\",\n sm: \"max-w-sm\",\n md: \"max-w-md\",\n lg: \"max-w-lg\",\n xl: \"max-w-xl\",\n \"2xl\": \"max-w-2xl\",\n \"3xl\": \"max-w-3xl\",\n \"4xl\": \"max-w-4xl\",\n \"5xl\": \"max-w-5xl\",\n },\n\n /**\n * Pin the dialog near the top of the viewport instead of centering it, so its height can\n * change without the whole thing jumping. A dialog stacked on top of another one\n * (`[[role=dialog]~&]`) sits a little lower still, so both remain visible.\n */\n fixed: {\n true: \"sm:top-[10vh] sm:max-h-[calc(90vh-2rem)] sm:[[role=dialog]~&]:top-[15vh] sm:[[role=dialog]~&]:max-h-[calc(85vh-2rem)]\",\n false: \"sm:top-1/2 sm:-translate-y-1/2 sm:max-h-[calc(100vh-2rem)]\",\n },\n\n /**\n * Drop the dialog's own padding and gap so sections can run edge to edge and draw their\n * own borders, e.g. a bordered header over a scrolling body over a bordered footer.\n */\n flush: {\n true: \"gap-0 p-0 max-sm:pb-[env(safe-area-inset-bottom)]\",\n false: \"gap-6 p-6 max-sm:pb-[calc(1.5rem+env(safe-area-inset-bottom))]\",\n },\n },\n\n defaultVariants: {\n size: \"md\",\n fixed: true,\n flush: false,\n },\n})\n\ntype DialogContentProps = Omit<DialogPrimitive.Popup.Props, \"className\"> &\n VariantProps<typeof dialogContentVariants> & {\n className?: string\n\n /**\n * Hide the close button in the top right corner, for a dialog that must be dismissed\n * through one of its own actions.\n */\n hideClose?: boolean\n }\n\nfunction DialogContent({\n className,\n children,\n size,\n fixed,\n flush,\n hideClose,\n ...props\n}: DialogContentProps) {\n return (\n <DialogPrimitive.Portal>\n <DialogOverlay />\n\n <DialogPrimitive.Popup\n className={dialogContentVariants({ size, fixed, flush, className })}\n {...props}\n >\n {children}\n\n {!hideClose && (\n <DialogPrimitive.Close\n render={<Button variant=\"ghost\" size=\"sm\" aria-label=\"Close\" prefix={<XIcon />} />}\n className=\"absolute top-3 right-3\"\n />\n )}\n </DialogPrimitive.Popup>\n </DialogPrimitive.Portal>\n )\n}\n\nconst dialogHeaderVariants = variants({\n variants: {\n /**\n * Give the header its own hairline and padding, for a `flush` dialog whose body scrolls\n * under it. The extra right padding clears the dialog's close button.\n */\n bordered: {\n true: \"border-b px-6 py-5 pr-14\",\n },\n },\n})\n\ntype DialogHeaderProps = Omit<ComponentProps<typeof SectionHeader>, \"title\" | \"description\"> &\n VariantProps<typeof dialogHeaderVariants> & {\n title: ReactNode\n description?: ReactNode\n }\n\n/**\n * The standard dialog heading: a card-scale `SectionHeader` whose title and description are wired to\n * Base UI's `DialogTitle` / `DialogDescription` for accessibility. Extra children render after\n * them.\n */\nfunction DialogHeader({\n title,\n description,\n bordered,\n className,\n children,\n ...props\n}: DialogHeaderProps) {\n return (\n <SectionHeader size=\"card\" className={dialogHeaderVariants({ bordered, className })} {...props}>\n <SectionHeaderBody>\n <DialogTitle render={<SectionHeaderTitle />}>{title}</DialogTitle>\n\n {description && (\n <DialogDescription render={<SectionHeaderDescription />}>{description}</DialogDescription>\n )}\n </SectionHeaderBody>\n\n {children}\n </SectionHeader>\n )\n}\n\nconst dialogFooterVariants = variants({\n base: \"flex flex-col-reverse flex-wrap gap-x-2 gap-y-4 sm:flex-row sm:justify-end\",\n\n variants: {\n /**\n * Give the footer its own hairline and padding, for a `flush` dialog where the body\n * scrolls behind it.\n */\n bordered: {\n true: \"border-t px-6 py-4\",\n },\n },\n})\n\ntype DialogFooterProps = ComponentProps<\"div\"> & VariantProps<typeof dialogFooterVariants>\n\nfunction DialogFooter({ className, bordered, ...props }: DialogFooterProps) {\n return <div className={dialogFooterVariants({ bordered, className })} {...props} />\n}\n\nfunction DialogClose({ ...props }: ComponentProps<typeof Button>) {\n return <DialogPrimitive.Close render={<Button variant=\"secondary\" {...props} />} />\n}\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n}\n"],"mappings":";;;;;;;;AAcA,MAAM,SAASA,SAAgB;AAC/B,MAAM,gBAAgBA,SAAgB;AACtC,MAAM,cAAcA,SAAgB;AACpC,MAAM,oBAAoBA,SAAgB;;AAG1C,SAAS,cAAc,EAAE,WAAW,GAAG,SAAyC;CAC9E,OACE,oBAACA,SAAgB,UAAjB;EACE,WAAW,GACT,iDACA,gDACA,8CACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,MAAM,wBAAwB,SAAS;CACrC,MAAM;EACJ;EAGA;EACA;EACA;EACA;EACA;EACA;CACF;CAEA,UAAU;EACR,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,OAAO;GACP,OAAO;GACP,OAAO;GACP,OAAO;EACT;;;;;;EAOA,OAAO;GACL,MAAM;GACN,OAAO;EACT;;;;;EAMA,OAAO;GACL,MAAM;GACN,OAAO;EACT;CACF;CAEA,iBAAiB;EACf,MAAM;EACN,OAAO;EACP,OAAO;CACT;AACF,CAAC;AAaD,SAAS,cAAc,EACrB,WACA,UACA,MACA,OACA,OACA,WACA,GAAG,SACkB;CACrB,OACE,qBAACA,SAAgB,QAAjB,EAAA,UAAA,CACE,oBAAC,eAAD,CAAgB,CAAA,GAEhB,qBAACA,SAAgB,OAAjB;EACE,WAAW,sBAAsB;GAAE;GAAM;GAAO;GAAO;EAAU,CAAC;EAClE,GAAI;EAFN,UAAA,CAIG,UAEA,CAAC,aACA,oBAACA,SAAgB,OAAjB;GACE,QAAQ,oBAAC,QAAD;IAAQ,SAAQ;IAAQ,MAAK;IAAK,cAAW;IAAQ,QAAQ,oBAAC,OAAD,CAAQ,CAAA;GAAI,CAAA;GACjF,WAAU;EACX,CAAA,CAEkB;CACD,CAAA,CAAA,EAAA,CAAA;AAE5B;AAEA,MAAM,uBAAuB,SAAS,EACpC,UAAU;;;;;AAKR,UAAU,EACR,MAAM,2BACR,EACF,EACF,CAAC;;;;;;AAaD,SAAS,aAAa,EACpB,OACA,aACA,UACA,WACA,UACA,GAAG,SACiB;CACpB,OACE,qBAAC,eAAD;EAAe,MAAK;EAAO,WAAW,qBAAqB;GAAE;GAAU;EAAU,CAAC;EAAG,GAAI;EAAzF,UAAA,CACE,qBAAC,mBAAD,EAAA,UAAA,CACE,oBAAC,aAAD;GAAa,QAAQ,oBAAC,oBAAD,CAAqB,CAAA;GAAI,UAAA;EAAmB,CAAA,GAEhE,eACC,oBAAC,mBAAD;GAAmB,QAAQ,oBAAC,0BAAD,CAA2B,CAAA;GAAI,UAAA;EAA+B,CAAA,CAE1E,EAAA,CAAA,GAElB,QACY;;AAEnB;AAEA,MAAM,uBAAuB,SAAS;CACpC,MAAM;CAEN,UAAU;;;;;AAKR,UAAU,EACR,MAAM,qBACR,EACF;AACF,CAAC;AAID,SAAS,aAAa,EAAE,WAAW,UAAU,GAAG,SAA4B;CAC1E,OAAO,oBAAC,OAAD;EAAK,WAAW,qBAAqB;GAAE;GAAU;EAAU,CAAC;EAAG,GAAI;CAAQ,CAAA;AACpF;AAEA,SAAS,YAAY,EAAE,GAAG,SAAwC;CAChE,OAAO,oBAACA,SAAgB,OAAjB,EAAuB,QAAQ,oBAAC,QAAD;EAAQ,SAAQ;EAAY,GAAI;CAAQ,CAAA,EAAI,CAAA;AACpF"}
@@ -18,7 +18,7 @@ function FormSection({ id, title, description, actions, footer, className, child
18
18
  children: /* @__PURE__ */ jsxs(Fragment, { children: [
19
19
  (title || actions) && /* @__PURE__ */ jsx(SectionHeader, {
20
20
  size: "panel",
21
- className: "py-4",
21
+ className: "py-4 max-sm:flex-col max-sm:items-stretch",
22
22
  title,
23
23
  description,
24
24
  actions
@@ -1 +1 @@
1
- {"version":3,"file":"form-section.js","names":[],"sources":["../../src/components/form-section.tsx"],"sourcesContent":["import { useRender } from \"@base-ui/react/use-render\"\nimport type { ReactNode } from \"react\"\nimport { SectionHeader } from \"~/components/section-header\"\nimport { cn } from \"~/lib/variants\"\n\ntype FormSectionProps = Omit<useRender.ComponentProps<\"section\">, \"title\"> & {\n id?: string\n /**\n * Optional heading. Leave it out inside the settings dialog, whose header owns the title.\n */\n title?: ReactNode\n description?: ReactNode\n actions?: ReactNode\n /**\n * Right-aligned actions after the rows, e.g. a save button. Rendered as the last row.\n */\n footer?: ReactNode\n}\n\n/**\n * A block of settings: one padded container whose children (optional heading, the rows,\n * optional footer) stack with hairlines between them. Renders as a `<form>` via `render`\n * when the rows are fields, so the form is the only wrapper.\n */\nexport function FormSection({\n id,\n title,\n description,\n actions,\n footer,\n className,\n children,\n render,\n ...props\n}: FormSectionProps) {\n return useRender({\n render,\n defaultTagName: \"section\",\n props: {\n id,\n className: cn(\"flex flex-col divide-y px-5 md:px-6\", className),\n children: (\n <>\n {(title || actions) && (\n <SectionHeader\n size=\"panel\"\n className=\"py-4\"\n title={title}\n description={description}\n actions={actions}\n />\n )}\n\n {children}\n\n {footer && <div className=\"flex justify-end py-5\">{footer}</div>}\n </>\n ),\n ...props,\n },\n })\n}\n"],"mappings":";;;;;;;;;;AAwBA,SAAgB,YAAY,EAC1B,IACA,OACA,aACA,SACA,QACA,WACA,UACA,QACA,GAAG,SACgB;CACnB,OAAO,UAAU;EACf;EACA,gBAAgB;EAChB,OAAO;GACL;GACA,WAAW,GAAG,uCAAuC,SAAS;GAC9D,UACE,qBAAA,UAAA,EAAA,UAAA;KACI,SAAS,YACT,oBAAC,eAAD;KACE,MAAK;KACL,WAAU;KACH;KACM;KACJ;IACV,CAAA;IAGF;IAEA,UAAU,oBAAC,OAAD;KAAK,WAAU;KAAyB,UAAA;IAAY,CAAA;GAC/D,EAAA,CAAA;GAEJ,GAAG;EACL;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"form-section.js","names":[],"sources":["../../src/components/form-section.tsx"],"sourcesContent":["import { useRender } from \"@base-ui/react/use-render\"\nimport type { ReactNode } from \"react\"\nimport { SectionHeader } from \"~/components/section-header\"\nimport { cn } from \"~/lib/variants\"\n\ntype FormSectionProps = Omit<useRender.ComponentProps<\"section\">, \"title\"> & {\n id?: string\n /**\n * Optional heading. Leave it out inside the settings dialog, whose header owns the title.\n */\n title?: ReactNode\n description?: ReactNode\n actions?: ReactNode\n /**\n * Right-aligned actions after the rows, e.g. a save button. Rendered as the last row.\n */\n footer?: ReactNode\n}\n\n/**\n * A block of settings: one padded container whose children (optional heading, the rows,\n * optional footer) stack with hairlines between them. Renders as a `<form>` via `render`\n * when the rows are fields, so the form is the only wrapper.\n */\nexport function FormSection({\n id,\n title,\n description,\n actions,\n footer,\n className,\n children,\n render,\n ...props\n}: FormSectionProps) {\n return useRender({\n render,\n defaultTagName: \"section\",\n props: {\n id,\n className: cn(\"flex flex-col divide-y px-5 md:px-6\", className),\n children: (\n <>\n {(title || actions) && (\n <SectionHeader\n size=\"panel\"\n className=\"py-4 max-sm:flex-col max-sm:items-stretch\"\n title={title}\n description={description}\n actions={actions}\n />\n )}\n\n {children}\n\n {footer && <div className=\"flex justify-end py-5\">{footer}</div>}\n </>\n ),\n ...props,\n },\n })\n}\n"],"mappings":";;;;;;;;;;AAwBA,SAAgB,YAAY,EAC1B,IACA,OACA,aACA,SACA,QACA,WACA,UACA,QACA,GAAG,SACgB;CACnB,OAAO,UAAU;EACf;EACA,gBAAgB;EAChB,OAAO;GACL;GACA,WAAW,GAAG,uCAAuC,SAAS;GAC9D,UACE,qBAAA,UAAA,EAAA,UAAA;KACI,SAAS,YACT,oBAAC,eAAD;KACE,MAAK;KACL,WAAU;KACH;KACM;KACJ;IACV,CAAA;IAGF;IAEA,UAAU,oBAAC,OAAD;KAAK,WAAU;KAAyB,UAAA;IAAY,CAAA;GAC/D,EAAA,CAAA;GAEJ,GAAG;EACL;CACF,CAAC;AACH"}
@@ -24,13 +24,13 @@ function MenuContent({ className, sideOffset = 4, align, side, ...props }) {
24
24
  }
25
25
  function MenuItem({ className, inset, ...props }) {
26
26
  return /* @__PURE__ */ jsx(Menu$1.Item, {
27
- className: cn(`relative flex m-0 cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 ${controlTextClasses} outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0`, inset && "pl-8", className),
27
+ className: cn(`relative flex m-0 cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 pointer-coarse:min-h-10 ${controlTextClasses} outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0`, inset && "pl-8", className),
28
28
  ...props
29
29
  });
30
30
  }
31
31
  function MenuCheckboxItem({ className, children, checked, ...props }) {
32
32
  return /* @__PURE__ */ jsxs(Menu$1.CheckboxItem, {
33
- className: cn(`relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 ${controlTextClasses} outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50`, className),
33
+ className: cn(`relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 pointer-coarse:min-h-10 ${controlTextClasses} outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50`, className),
34
34
  checked,
35
35
  ...props,
36
36
  children: [/* @__PURE__ */ jsx("span", {
@@ -41,7 +41,7 @@ function MenuCheckboxItem({ className, children, checked, ...props }) {
41
41
  }
42
42
  function MenuRadioItem({ className, children, ...props }) {
43
43
  return /* @__PURE__ */ jsxs(Menu$1.RadioItem, {
44
- className: cn(`relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 ${controlTextClasses} outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50`, className),
44
+ className: cn(`relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 pointer-coarse:min-h-10 ${controlTextClasses} outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50`, className),
45
45
  ...props,
46
46
  children: [/* @__PURE__ */ jsx("span", {
47
47
  className: "absolute left-2 flex size-3.5 items-center justify-center",
@@ -1 +1 @@
1
- {"version":3,"file":"menu.js","names":["MenuPrimitive"],"sources":["../../src/components/menu.tsx"],"sourcesContent":["\"use client\"\n\nimport { Menu as MenuPrimitive } from \"@base-ui/react/menu\"\nimport type { ComponentProps } from \"react\"\nimport { Kbd } from \"~/components/kbd\"\nimport { controlTextClasses, eyebrowClasses, popoverAnimationClasses } from \"~/lib/classes\"\nimport { CheckIcon, CircleIcon } from \"~/lib/icons\"\nimport { cn } from \"~/lib/variants\"\n\nconst Menu = MenuPrimitive.Root\nconst MenuTrigger = MenuPrimitive.Trigger\nconst MenuGroup = MenuPrimitive.Group\nconst MenuRadioGroup = MenuPrimitive.RadioGroup\n\nfunction MenuContent({\n className,\n sideOffset = 4,\n align,\n side,\n ...props\n}: MenuPrimitive.Popup.Props &\n Pick<MenuPrimitive.Positioner.Props, \"align\" | \"side\" | \"sideOffset\">) {\n return (\n <MenuPrimitive.Portal>\n <MenuPrimitive.Positioner\n sideOffset={sideOffset}\n align={align}\n side={side}\n className=\"isolate z-50\"\n >\n <MenuPrimitive.Popup\n className={cn(\n \"min-w-40 flex flex-col rounded-md border bg-popover p-1 text-popover-foreground shadow-sm backdrop-blur-xs\",\n popoverAnimationClasses,\n className,\n )}\n {...props}\n />\n </MenuPrimitive.Positioner>\n </MenuPrimitive.Portal>\n )\n}\n\nfunction MenuItem({\n className,\n inset,\n ...props\n}: MenuPrimitive.Item.Props & {\n inset?: boolean\n}) {\n return (\n <MenuPrimitive.Item\n className={cn(\n `relative flex m-0 cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 ${controlTextClasses} outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0`,\n inset && \"pl-8\",\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction MenuCheckboxItem({\n className,\n children,\n checked,\n ...props\n}: MenuPrimitive.CheckboxItem.Props) {\n return (\n <MenuPrimitive.CheckboxItem\n className={cn(\n `relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 ${controlTextClasses} outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50`,\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 <MenuPrimitive.CheckboxItemIndicator>\n <CheckIcon />\n </MenuPrimitive.CheckboxItemIndicator>\n </span>\n {children}\n </MenuPrimitive.CheckboxItem>\n )\n}\n\nfunction MenuRadioItem({ className, children, ...props }: MenuPrimitive.RadioItem.Props) {\n return (\n <MenuPrimitive.RadioItem\n className={cn(\n `relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 ${controlTextClasses} outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50`,\n className,\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex size-3.5 items-center justify-center\">\n <MenuPrimitive.RadioItemIndicator>\n <CircleIcon className=\"fill-current size-2\" />\n </MenuPrimitive.RadioItemIndicator>\n </span>\n {children}\n </MenuPrimitive.RadioItem>\n )\n}\n\nfunction MenuGroupLabel({\n className,\n inset,\n ...props\n}: MenuPrimitive.GroupLabel.Props & {\n inset?: boolean\n}) {\n return (\n <MenuPrimitive.GroupLabel\n className={cn(\"px-2 pt-1.5 pb-1\", eyebrowClasses, inset && \"pl-8\", className)}\n {...props}\n />\n )\n}\n\nfunction MenuSeparator({ className, ...props }: MenuPrimitive.Separator.Props) {\n return (\n <MenuPrimitive.Separator className={cn(\"-mx-1 my-1 h-px bg-border\", className)} {...props} />\n )\n}\n\nfunction MenuShortcut({ className, ...props }: ComponentProps<typeof Kbd>) {\n return <Kbd className={cn(\"ml-auto\", className)} {...props} />\n}\n\nexport {\n Menu,\n MenuCheckboxItem,\n MenuContent,\n MenuGroup,\n MenuItem,\n MenuGroupLabel,\n MenuRadioGroup,\n MenuRadioItem,\n MenuSeparator,\n MenuShortcut,\n MenuTrigger,\n}\n"],"mappings":";;;;;;;;AASA,MAAM,OAAOA,OAAc;AAC3B,MAAM,cAAcA,OAAc;AAClC,MAAM,YAAYA,OAAc;AAChC,MAAM,iBAAiBA,OAAc;AAErC,SAAS,YAAY,EACnB,WACA,aAAa,GACb,OACA,MACA,GAAG,SAEoE;CACvE,OACE,oBAACA,OAAc,QAAf,EAAA,UACE,oBAACA,OAAc,YAAf;EACc;EACL;EACD;EACN,WAAU;EAEV,UAAA,oBAACA,OAAc,OAAf;GACE,WAAW,GACT,8GACA,yBACA,SACF;GACA,GAAI;EACL,CAAA;CACuB,CAAA,EACN,CAAA;AAE1B;AAEA,SAAS,SAAS,EAChB,WACA,OACA,GAAG,SAGF;CACD,OACE,oBAACA,OAAc,MAAf;EACE,WAAW,GACT,0FAA0F,mBAAmB,0JAC7G,SAAS,QACT,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,iBAAiB,EACxB,WACA,UACA,SACA,GAAG,SACgC;CACnC,OACE,qBAACA,OAAc,cAAf;EACE,WAAW,GACT,qFAAqF,mBAAmB,0HACxG,SACF;EACS;EACT,GAAI;EANN,UAAA,CAQE,oBAAC,QAAD;GAAM,WAAU;GACd,UAAA,oBAACA,OAAc,uBAAf,EAAA,UACE,oBAAC,WAAD,CAAY,CAAA,EACuB,CAAA;EACjC,CAAA,GACL,QACyB;;AAEhC;AAEA,SAAS,cAAc,EAAE,WAAW,UAAU,GAAG,SAAwC;CACvF,OACE,qBAACA,OAAc,WAAf;EACE,WAAW,GACT,qFAAqF,mBAAmB,0HACxG,SACF;EACA,GAAI;EALN,UAAA,CAOE,oBAAC,QAAD;GAAM,WAAU;GACd,UAAA,oBAACA,OAAc,oBAAf,EAAA,UACE,oBAAC,YAAD,EAAY,WAAU,sBAAuB,CAAA,EACb,CAAA;EAC9B,CAAA,GACL,QACsB;;AAE7B;AAEA,SAAS,eAAe,EACtB,WACA,OACA,GAAG,SAGF;CACD,OACE,oBAACA,OAAc,YAAf;EACE,WAAW,GAAG,oBAAoB,gBAAgB,SAAS,QAAQ,SAAS;EAC5E,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,cAAc,EAAE,WAAW,GAAG,SAAwC;CAC7E,OACE,oBAACA,OAAc,WAAf;EAAyB,WAAW,GAAG,6BAA6B,SAAS;EAAG,GAAI;CAAQ,CAAA;AAEhG;AAEA,SAAS,aAAa,EAAE,WAAW,GAAG,SAAqC;CACzE,OAAO,oBAAC,KAAD;EAAK,WAAW,GAAG,WAAW,SAAS;EAAG,GAAI;CAAQ,CAAA;AAC/D"}
1
+ {"version":3,"file":"menu.js","names":["MenuPrimitive"],"sources":["../../src/components/menu.tsx"],"sourcesContent":["\"use client\"\n\nimport { Menu as MenuPrimitive } from \"@base-ui/react/menu\"\nimport type { ComponentProps } from \"react\"\nimport { Kbd } from \"~/components/kbd\"\nimport { controlTextClasses, eyebrowClasses, popoverAnimationClasses } from \"~/lib/classes\"\nimport { CheckIcon, CircleIcon } from \"~/lib/icons\"\nimport { cn } from \"~/lib/variants\"\n\nconst Menu = MenuPrimitive.Root\nconst MenuTrigger = MenuPrimitive.Trigger\nconst MenuGroup = MenuPrimitive.Group\nconst MenuRadioGroup = MenuPrimitive.RadioGroup\n\nfunction MenuContent({\n className,\n sideOffset = 4,\n align,\n side,\n ...props\n}: MenuPrimitive.Popup.Props &\n Pick<MenuPrimitive.Positioner.Props, \"align\" | \"side\" | \"sideOffset\">) {\n return (\n <MenuPrimitive.Portal>\n <MenuPrimitive.Positioner\n sideOffset={sideOffset}\n align={align}\n side={side}\n className=\"isolate z-50\"\n >\n <MenuPrimitive.Popup\n className={cn(\n \"min-w-40 flex flex-col rounded-md border bg-popover p-1 text-popover-foreground shadow-sm backdrop-blur-xs\",\n popoverAnimationClasses,\n className,\n )}\n {...props}\n />\n </MenuPrimitive.Positioner>\n </MenuPrimitive.Portal>\n )\n}\n\nfunction MenuItem({\n className,\n inset,\n ...props\n}: MenuPrimitive.Item.Props & {\n inset?: boolean\n}) {\n return (\n <MenuPrimitive.Item\n className={cn(\n `relative flex m-0 cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 pointer-coarse:min-h-10 ${controlTextClasses} outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0`,\n inset && \"pl-8\",\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction MenuCheckboxItem({\n className,\n children,\n checked,\n ...props\n}: MenuPrimitive.CheckboxItem.Props) {\n return (\n <MenuPrimitive.CheckboxItem\n className={cn(\n `relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 pointer-coarse:min-h-10 ${controlTextClasses} outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50`,\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 <MenuPrimitive.CheckboxItemIndicator>\n <CheckIcon />\n </MenuPrimitive.CheckboxItemIndicator>\n </span>\n {children}\n </MenuPrimitive.CheckboxItem>\n )\n}\n\nfunction MenuRadioItem({ className, children, ...props }: MenuPrimitive.RadioItem.Props) {\n return (\n <MenuPrimitive.RadioItem\n className={cn(\n `relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 pointer-coarse:min-h-10 ${controlTextClasses} outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50`,\n className,\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex size-3.5 items-center justify-center\">\n <MenuPrimitive.RadioItemIndicator>\n <CircleIcon className=\"fill-current size-2\" />\n </MenuPrimitive.RadioItemIndicator>\n </span>\n {children}\n </MenuPrimitive.RadioItem>\n )\n}\n\nfunction MenuGroupLabel({\n className,\n inset,\n ...props\n}: MenuPrimitive.GroupLabel.Props & {\n inset?: boolean\n}) {\n return (\n <MenuPrimitive.GroupLabel\n className={cn(\"px-2 pt-1.5 pb-1\", eyebrowClasses, inset && \"pl-8\", className)}\n {...props}\n />\n )\n}\n\nfunction MenuSeparator({ className, ...props }: MenuPrimitive.Separator.Props) {\n return (\n <MenuPrimitive.Separator className={cn(\"-mx-1 my-1 h-px bg-border\", className)} {...props} />\n )\n}\n\nfunction MenuShortcut({ className, ...props }: ComponentProps<typeof Kbd>) {\n return <Kbd className={cn(\"ml-auto\", className)} {...props} />\n}\n\nexport {\n Menu,\n MenuCheckboxItem,\n MenuContent,\n MenuGroup,\n MenuItem,\n MenuGroupLabel,\n MenuRadioGroup,\n MenuRadioItem,\n MenuSeparator,\n MenuShortcut,\n MenuTrigger,\n}\n"],"mappings":";;;;;;;;AASA,MAAM,OAAOA,OAAc;AAC3B,MAAM,cAAcA,OAAc;AAClC,MAAM,YAAYA,OAAc;AAChC,MAAM,iBAAiBA,OAAc;AAErC,SAAS,YAAY,EACnB,WACA,aAAa,GACb,OACA,MACA,GAAG,SAEoE;CACvE,OACE,oBAACA,OAAc,QAAf,EAAA,UACE,oBAACA,OAAc,YAAf;EACc;EACL;EACD;EACN,WAAU;EAEV,UAAA,oBAACA,OAAc,OAAf;GACE,WAAW,GACT,8GACA,yBACA,SACF;GACA,GAAI;EACL,CAAA;CACuB,CAAA,EACN,CAAA;AAE1B;AAEA,SAAS,SAAS,EAChB,WACA,OACA,GAAG,SAGF;CACD,OACE,oBAACA,OAAc,MAAf;EACE,WAAW,GACT,kHAAkH,mBAAmB,0JACrI,SAAS,QACT,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,iBAAiB,EACxB,WACA,UACA,SACA,GAAG,SACgC;CACnC,OACE,qBAACA,OAAc,cAAf;EACE,WAAW,GACT,6GAA6G,mBAAmB,0HAChI,SACF;EACS;EACT,GAAI;EANN,UAAA,CAQE,oBAAC,QAAD;GAAM,WAAU;GACd,UAAA,oBAACA,OAAc,uBAAf,EAAA,UACE,oBAAC,WAAD,CAAY,CAAA,EACuB,CAAA;EACjC,CAAA,GACL,QACyB;;AAEhC;AAEA,SAAS,cAAc,EAAE,WAAW,UAAU,GAAG,SAAwC;CACvF,OACE,qBAACA,OAAc,WAAf;EACE,WAAW,GACT,6GAA6G,mBAAmB,0HAChI,SACF;EACA,GAAI;EALN,UAAA,CAOE,oBAAC,QAAD;GAAM,WAAU;GACd,UAAA,oBAACA,OAAc,oBAAf,EAAA,UACE,oBAAC,YAAD,EAAY,WAAU,sBAAuB,CAAA,EACb,CAAA;EAC9B,CAAA,GACL,QACsB;;AAE7B;AAEA,SAAS,eAAe,EACtB,WACA,OACA,GAAG,SAGF;CACD,OACE,oBAACA,OAAc,YAAf;EACE,WAAW,GAAG,oBAAoB,gBAAgB,SAAS,QAAQ,SAAS;EAC5E,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,cAAc,EAAE,WAAW,GAAG,SAAwC;CAC7E,OACE,oBAACA,OAAc,WAAf;EAAyB,WAAW,GAAG,6BAA6B,SAAS;EAAG,GAAI;CAAQ,CAAA;AAEhG;AAEA,SAAS,aAAa,EAAE,WAAW,GAAG,SAAqC;CACzE,OAAO,oBAAC,KAAD;EAAK,WAAW,GAAG,WAAW,SAAS;EAAG,GAAI;CAAQ,CAAA;AAC/D"}
@@ -41,7 +41,7 @@ const navAffixClasses = "[&>*:first-child]:ml-0 [&>*:last-child]:mr-0";
41
41
  const navItem = variants({
42
42
  base: "w-full justify-start gap-2.5 px-2.5",
43
43
  variants: { variant: {
44
- link: ["py-2 text-muted-foreground text-sm/tight hover:bg-foreground/3 hover:text-foreground [&>svg:first-child]:ml-0.5 [&>svg]:mr-0.5 [&>svg]:size-4 [&>svg]:shrink-0", "data-[status=active]:bg-foreground/5 data-[status=active]:text-foreground data-[status=active]:hover:bg-foreground/5"],
44
+ link: ["py-2 pointer-coarse:min-h-11 text-muted-foreground text-sm/tight hover:bg-foreground/3 hover:text-foreground [&>svg:first-child]:ml-0.5 [&>svg]:mr-0.5 [&>svg]:size-4 [&>svg]:shrink-0", "data-[status=active]:bg-foreground/5 data-[status=active]:text-foreground data-[status=active]:hover:bg-foreground/5"],
45
45
  bordered: [navAffixClasses, "border-border bg-card hover:bg-card hover:border-ring"],
46
46
  flat: navAffixClasses
47
47
  } },
@@ -1 +1 @@
1
- {"version":3,"file":"nav.js","names":[],"sources":["../../src/components/nav.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\"\nimport { eyebrowClasses } from \"~/lib/classes\"\nimport { cn, variants } from \"~/lib/variants\"\n\nexport function Nav({ className, ...props }: ComponentProps<\"nav\">) {\n return <nav className={cn(\"flex flex-col gap-0.5 px-3 py-2\", className)} {...props} />\n}\n\n/**\n * Muted group label above a run of nav links.\n */\nexport function NavLabel({ className, ...props }: ComponentProps<\"p\">) {\n return <p className={cn(\"px-2.5 pt-1 pb-1.5\", eyebrowClasses, className)} {...props} />\n}\n\n/**\n * The affixes of a nav row that leads with its own visual (an avatar, a skeleton) sit flush\n * against the row's inset, so the Button's optical pull-in is zeroed on both ends.\n */\nconst navAffixClasses = \"[&>*:first-child]:ml-0 [&>*:last-child]:mr-0\"\n\n/**\n * The one nav row, applied to a `ghost` `md` `Button`: every item starts from the same width,\n * inset and gap, and each variant is the shape on top of it.\n *\n * `link` — a nav link or a settings tab: muted at rest, full color on hover, and a flat darker\n * tint with dark text when active. The 16px icon sits centered in a 20px box, so labels start on\n * the same column as a row that leads with a 20px avatar (10px inset + 20px avatar + 10px gap).\n * The active state keys off `data-status=\"active\"`, which router links set on their own and plain\n * buttons set by hand, so the active color always wins over the resting one. Its tints are flat\n * foreground over the canvas, so hover and active share the canvas hue exactly.\n *\n * `bordered` — a bordered row on a card surface, for a row that leads with its own visual and\n * opens a menu.\n *\n * `flat` — the same row without the border or the surface.\n */\nexport const navItem = variants({\n base: \"w-full justify-start gap-2.5 px-2.5\",\n\n variants: {\n variant: {\n link: [\n \"py-2 text-muted-foreground text-sm/tight hover:bg-foreground/3 hover:text-foreground [&>svg:first-child]:ml-0.5 [&>svg]:mr-0.5 [&>svg]:size-4 [&>svg]:shrink-0\",\n \"data-[status=active]:bg-foreground/5 data-[status=active]:text-foreground data-[status=active]:hover:bg-foreground/5\",\n ],\n bordered: [navAffixClasses, \"border-border bg-card hover:bg-card hover:border-ring\"],\n flat: navAffixClasses,\n },\n },\n\n defaultVariants: {\n variant: \"link\",\n },\n})\n"],"mappings":";;;;AAIA,SAAgB,IAAI,EAAE,WAAW,GAAG,SAAgC;CAClE,OAAO,oBAAC,OAAD;EAAK,WAAW,GAAG,mCAAmC,SAAS;EAAG,GAAI;CAAQ,CAAA;AACvF;;;;AAKA,SAAgB,SAAS,EAAE,WAAW,GAAG,SAA8B;CACrE,OAAO,oBAAC,KAAD;EAAG,WAAW,GAAG,sBAAsB,gBAAgB,SAAS;EAAG,GAAI;CAAQ,CAAA;AACxF;;;;;AAMA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;AAkBxB,MAAa,UAAU,SAAS;CAC9B,MAAM;CAEN,UAAU,EACR,SAAS;EACP,MAAM,CACJ,kKACA,sHACF;EACA,UAAU,CAAC,iBAAiB,uDAAuD;EACnF,MAAM;CACR,EACF;CAEA,iBAAiB,EACf,SAAS,OACX;AACF,CAAC"}
1
+ {"version":3,"file":"nav.js","names":[],"sources":["../../src/components/nav.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\"\nimport { eyebrowClasses } from \"~/lib/classes\"\nimport { cn, variants } from \"~/lib/variants\"\n\nexport function Nav({ className, ...props }: ComponentProps<\"nav\">) {\n return <nav className={cn(\"flex flex-col gap-0.5 px-3 py-2\", className)} {...props} />\n}\n\n/**\n * Muted group label above a run of nav links.\n */\nexport function NavLabel({ className, ...props }: ComponentProps<\"p\">) {\n return <p className={cn(\"px-2.5 pt-1 pb-1.5\", eyebrowClasses, className)} {...props} />\n}\n\n/**\n * The affixes of a nav row that leads with its own visual (an avatar, a skeleton) sit flush\n * against the row's inset, so the Button's optical pull-in is zeroed on both ends.\n */\nconst navAffixClasses = \"[&>*:first-child]:ml-0 [&>*:last-child]:mr-0\"\n\n/**\n * The one nav row, applied to a `ghost` `md` `Button`: every item starts from the same width,\n * inset and gap, and each variant is the shape on top of it.\n *\n * `link` — a nav link or a settings tab: muted at rest, full color on hover, and a flat darker\n * tint with dark text when active. The 16px icon sits centered in a 20px box, so labels start on\n * the same column as a row that leads with a 20px avatar (10px inset + 20px avatar + 10px gap).\n * The active state keys off `data-status=\"active\"`, which router links set on their own and plain\n * buttons set by hand, so the active color always wins over the resting one. Its tints are flat\n * foreground over the canvas, so hover and active share the canvas hue exactly.\n *\n * `bordered` — a bordered row on a card surface, for a row that leads with its own visual and\n * opens a menu.\n *\n * `flat` — the same row without the border or the surface.\n */\nexport const navItem = variants({\n base: \"w-full justify-start gap-2.5 px-2.5\",\n\n variants: {\n variant: {\n link: [\n \"py-2 pointer-coarse:min-h-11 text-muted-foreground text-sm/tight hover:bg-foreground/3 hover:text-foreground [&>svg:first-child]:ml-0.5 [&>svg]:mr-0.5 [&>svg]:size-4 [&>svg]:shrink-0\",\n \"data-[status=active]:bg-foreground/5 data-[status=active]:text-foreground data-[status=active]:hover:bg-foreground/5\",\n ],\n bordered: [navAffixClasses, \"border-border bg-card hover:bg-card hover:border-ring\"],\n flat: navAffixClasses,\n },\n },\n\n defaultVariants: {\n variant: \"link\",\n },\n})\n"],"mappings":";;;;AAIA,SAAgB,IAAI,EAAE,WAAW,GAAG,SAAgC;CAClE,OAAO,oBAAC,OAAD;EAAK,WAAW,GAAG,mCAAmC,SAAS;EAAG,GAAI;CAAQ,CAAA;AACvF;;;;AAKA,SAAgB,SAAS,EAAE,WAAW,GAAG,SAA8B;CACrE,OAAO,oBAAC,KAAD;EAAG,WAAW,GAAG,sBAAsB,gBAAgB,SAAS;EAAG,GAAI;CAAQ,CAAA;AACxF;;;;;AAMA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;AAkBxB,MAAa,UAAU,SAAS;CAC9B,MAAM;CAEN,UAAU,EACR,SAAS;EACP,MAAM,CACJ,0LACA,sHACF;EACA,UAAU,CAAC,iBAAiB,uDAAuD;EACnF,MAAM;CACR,EACF;CAEA,iBAAiB,EACf,SAAS,OACX;AACF,CAAC"}
@@ -10,8 +10,8 @@ declare const sectionHeaderVariants: import("tailwind-variants").TVReturnType<{
10
10
  center: "justify-center text-center";
11
11
  };
12
12
  size: {
13
- page: "gap-y-3 gap-x-6 lg:gap-x-12";
14
- card: "gap-y-2 gap-x-6";
13
+ page: "gap-y-3 gap-x-6 max-sm:flex-col max-sm:items-stretch lg:gap-x-12";
14
+ card: "gap-y-2 gap-x-6 max-sm:flex-col max-sm:items-stretch";
15
15
  panel: "gap-y-2 gap-x-6";
16
16
  hero: "gap-y-3 gap-x-6 lg:gap-x-12";
17
17
  };
@@ -25,8 +25,8 @@ declare const sectionHeaderVariants: import("tailwind-variants").TVReturnType<{
25
25
  center: "justify-center text-center";
26
26
  };
27
27
  size: {
28
- page: "gap-y-3 gap-x-6 lg:gap-x-12";
29
- card: "gap-y-2 gap-x-6";
28
+ page: "gap-y-3 gap-x-6 max-sm:flex-col max-sm:items-stretch lg:gap-x-12";
29
+ card: "gap-y-2 gap-x-6 max-sm:flex-col max-sm:items-stretch";
30
30
  panel: "gap-y-2 gap-x-6";
31
31
  hero: "gap-y-3 gap-x-6 lg:gap-x-12";
32
32
  };
@@ -40,8 +40,8 @@ declare const sectionHeaderVariants: import("tailwind-variants").TVReturnType<{
40
40
  center: "justify-center text-center";
41
41
  };
42
42
  size: {
43
- page: "gap-y-3 gap-x-6 lg:gap-x-12";
44
- card: "gap-y-2 gap-x-6";
43
+ page: "gap-y-3 gap-x-6 max-sm:flex-col max-sm:items-stretch lg:gap-x-12";
44
+ card: "gap-y-2 gap-x-6 max-sm:flex-col max-sm:items-stretch";
45
45
  panel: "gap-y-2 gap-x-6";
46
46
  hero: "gap-y-3 gap-x-6 lg:gap-x-12";
47
47
  };
@@ -14,8 +14,8 @@ const sectionHeaderVariants = variants({
14
14
  center: "justify-center text-center"
15
15
  },
16
16
  size: {
17
- page: "gap-y-3 gap-x-6 lg:gap-x-12",
18
- card: "gap-y-2 gap-x-6",
17
+ page: "gap-y-3 gap-x-6 max-sm:flex-col max-sm:items-stretch lg:gap-x-12",
18
+ card: "gap-y-2 gap-x-6 max-sm:flex-col max-sm:items-stretch",
19
19
  panel: "gap-y-2 gap-x-6",
20
20
  hero: "gap-y-3 gap-x-6 lg:gap-x-12"
21
21
  },
@@ -135,9 +135,9 @@ function SectionHeaderDescription({ className, ...props }) {
135
135
  * truncates instead of squeezing the buttons.
136
136
  */
137
137
  function SectionHeaderActions({ className, ...props }) {
138
- const { wrap } = use(SectionHeaderContext);
138
+ const { size, wrap } = use(SectionHeaderContext);
139
139
  return /* @__PURE__ */ jsx(Stack, {
140
- className: cn("-my-0.5", !wrap && "shrink-0", className),
140
+ className: cn("-my-0.5", !wrap && "shrink-0", size === "page" && "max-sm:flex-row-reverse max-sm:justify-end", className),
141
141
  ...props
142
142
  });
143
143
  }
@@ -1 +1 @@
1
- {"version":3,"file":"section-header.js","names":[],"sources":["../../src/components/section-header.tsx"],"sourcesContent":["import { useRender } from \"@base-ui/react/use-render\"\nimport { type ComponentProps, createContext, type ReactNode, use } from \"react\"\nimport { Heading, type HeadingProps } from \"~/components/heading\"\nimport { Stack } from \"~/components/stack\"\nimport { descriptionClasses } from \"~/lib/classes\"\nimport { variants, cn, type VariantProps } from \"~/lib/variants\"\n\nconst sectionHeaderVariants = variants({\n base: \"flex items-center w-full min-w-0\",\n\n variants: {\n alignment: {\n left: \"justify-between text-start\",\n center: \"justify-center text-center\",\n },\n size: {\n page: \"gap-y-3 gap-x-6 lg:gap-x-12\",\n card: \"gap-y-2 gap-x-6\",\n panel: \"gap-y-2 gap-x-6\",\n hero: \"gap-y-3 gap-x-6 lg:gap-x-12\",\n },\n wrap: {\n true: \"flex-wrap\",\n false: \"flex-nowrap\",\n },\n },\n\n defaultVariants: {\n alignment: \"left\",\n wrap: true,\n },\n})\n\ntype SectionHeaderSize = NonNullable<VariantProps<typeof sectionHeaderVariants>[\"size\"]>\n\n/**\n * The title each header scale gets, alongside the spacing the variant above gives it: `page`\n * for a route heading, `card` for a standalone card, `panel` for a dashboard or list panel,\n * `hero` for the centered auth and callback screens. The `as` entry keeps the heading outline\n * intact — one `<h1>` per route, `<h2>` for everything nested inside it.\n */\nconst sectionHeaderTitles = {\n page: { size: \"h3\", as: \"h1\" },\n card: { size: \"h4\", as: \"h2\" },\n panel: { size: \"h5\", as: \"h2\" },\n hero: { size: \"h2\", as: \"h1\" },\n} as const satisfies Record<SectionHeaderSize, { size: HeadingProps[\"size\"]; as: string }>\n\nconst SectionHeaderContext = createContext<{ size: SectionHeaderSize; wrap: boolean }>({\n size: \"page\",\n wrap: true,\n})\n\ntype SectionHeaderProps = Omit<useRender.ComponentProps<\"div\">, \"title\"> &\n Omit<VariantProps<typeof sectionHeaderVariants>, \"wrap\"> & {\n /**\n * Keep the title block and the actions on one line instead of wrapping them.\n */\n wrap?: boolean\n /**\n * Title block. Given any of `title`/`description`/`leading`/`actions`, the header renders\n * its own `SectionHeaderBody` (empty if there is nothing but actions, so they still sit right);\n * leave them all out to compose the body by hand, e.g. an entity header with dot-joined\n * meta or the dashboard greeting.\n */\n title?: ReactNode\n description?: ReactNode\n actions?: ReactNode\n /**\n * Leading visual beside the title, e.g. an entity avatar.\n */\n leading?: ReactNode\n }\n\n/**\n * The one header for every surface in the app: pages, cards, list panels, dialogs and settings\n * sections. Pass `title`/`description`/`actions` for the standard block, or compose\n * `SectionHeaderBody` + `SectionHeaderActions` as children when the content is bespoke.\n */\nfunction SectionHeader({\n className,\n alignment = \"left\",\n size = \"page\",\n wrap = true,\n title,\n description,\n actions,\n leading,\n children,\n render,\n ...props\n}: SectionHeaderProps) {\n const element = useRender({\n render,\n defaultTagName: \"div\",\n props: {\n className: cn(sectionHeaderVariants({ alignment, size, wrap, className })),\n children: (\n <>\n {(title || description || leading || actions) && (\n <SectionHeaderBody\n leading={leading}\n className={cn(alignment === \"center\" && \"justify-center\")}\n >\n {title && <SectionHeaderTitle>{title}</SectionHeaderTitle>}\n {description && <SectionHeaderDescription>{description}</SectionHeaderDescription>}\n </SectionHeaderBody>\n )}\n\n {actions && <SectionHeaderActions>{actions}</SectionHeaderActions>}\n\n {children}\n </>\n ),\n ...props,\n },\n })\n\n return <SectionHeaderContext value={{ size, wrap }}>{element}</SectionHeaderContext>\n}\n\n/**\n * The page title. Its visual size and semantic level both follow the header's `size`, so a\n * page heading stays the route's single `<h1>` while cards and panels render `<h2>`s. Pass\n * `size`/`as` to override either one on its own.\n */\nfunction SectionHeaderTitle({ size, as, ...props }: HeadingProps) {\n const preset = sectionHeaderTitles[use(SectionHeaderContext).size]\n\n return <Heading size={size ?? preset.size} as={as ?? preset.as} {...props} />\n}\n\ntype SectionHeaderBodyProps = ComponentProps<\"div\"> & {\n /**\n * Leading visual, e.g. an entity avatar.\n */\n leading?: ReactNode\n}\n\n/**\n * The one title block for every header in the app (pages, cards, dialogs, settings sections):\n * an optional leading visual beside a column of `SectionHeaderTitle` + `SectionHeaderDescription`. Change the\n * spacing here and it changes everywhere.\n */\nfunction SectionHeaderBody({ leading, children, className, ...props }: SectionHeaderBodyProps) {\n return (\n <div className={cn(\"flex min-w-0 flex-1 items-center gap-3\", className)} {...props}>\n {leading}\n <div className=\"flex min-w-0 flex-col gap-1.5\">{children}</div>\n </div>\n )\n}\n\n/**\n * The muted line under a title. Wraps by default; pass `truncate` for one-line entity meta.\n */\nfunction SectionHeaderDescription({ className, ...props }: ComponentProps<\"p\">) {\n return <p className={cn(descriptionClasses, className)} {...props} />\n}\n\n/**\n * Right-aligned actions. Stops shrinking when the header is `wrap={false}`, so a long title\n * truncates instead of squeezing the buttons.\n */\nfunction SectionHeaderActions({ className, ...props }: ComponentProps<typeof Stack>) {\n const { wrap } = use(SectionHeaderContext)\n\n return <Stack className={cn(\"-my-0.5\", !wrap && \"shrink-0\", className)} {...props} />\n}\n\nexport {\n SectionHeader,\n SectionHeaderTitle,\n SectionHeaderBody,\n SectionHeaderDescription,\n SectionHeaderActions,\n}\n"],"mappings":";;;;;;;;AAOA,MAAM,wBAAwB,SAAS;CACrC,MAAM;CAEN,UAAU;EACR,WAAW;GACT,MAAM;GACN,QAAQ;EACV;EACA,MAAM;GACJ,MAAM;GACN,MAAM;GACN,OAAO;GACP,MAAM;EACR;EACA,MAAM;GACJ,MAAM;GACN,OAAO;EACT;CACF;CAEA,iBAAiB;EACf,WAAW;EACX,MAAM;CACR;AACF,CAAC;;;;;;;AAUD,MAAM,sBAAsB;CAC1B,MAAM;EAAE,MAAM;EAAM,IAAI;CAAK;CAC7B,MAAM;EAAE,MAAM;EAAM,IAAI;CAAK;CAC7B,OAAO;EAAE,MAAM;EAAM,IAAI;CAAK;CAC9B,MAAM;EAAE,MAAM;EAAM,IAAI;CAAK;AAC/B;AAEA,MAAM,uBAAuB,cAA0D;CACrF,MAAM;CACN,MAAM;AACR,CAAC;;;;;;AA4BD,SAAS,cAAc,EACrB,WACA,YAAY,QACZ,OAAO,QACP,OAAO,MACP,OACA,aACA,SACA,SACA,UACA,QACA,GAAG,SACkB;CACrB,MAAM,UAAU,UAAU;EACxB;EACA,gBAAgB;EAChB,OAAO;GACL,WAAW,GAAG,sBAAsB;IAAE;IAAW;IAAM;IAAM;GAAU,CAAC,CAAC;GACzE,UACE,qBAAA,UAAA,EAAA,UAAA;KACI,SAAS,eAAe,WAAW,YACnC,qBAAC,mBAAD;KACW;KACT,WAAW,GAAG,cAAc,YAAY,gBAAgB;KAF1D,UAAA,CAIG,SAAS,oBAAC,oBAAD,EAAA,UAAqB,MAA0B,CAAA,GACxD,eAAe,oBAAC,0BAAD,EAAA,UAA2B,YAAsC,CAAA,CAChE;;IAGpB,WAAW,oBAAC,sBAAD,EAAA,UAAuB,QAA8B,CAAA;IAEhE;GACD,EAAA,CAAA;GAEJ,GAAG;EACL;CACF,CAAC;CAED,OAAO,oBAAC,sBAAD;EAAsB,OAAO;GAAE;GAAM;EAAK;EAAI,UAAA;CAA8B,CAAA;AACrF;;;;;;AAOA,SAAS,mBAAmB,EAAE,MAAM,IAAI,GAAG,SAAuB;CAChE,MAAM,SAAS,oBAAoB,IAAI,oBAAoB,CAAC,CAAC;CAE7D,OAAO,oBAAC,SAAD;EAAS,MAAM,QAAQ,OAAO;EAAM,IAAI,MAAM,OAAO;EAAI,GAAI;CAAQ,CAAA;AAC9E;;;;;;AAcA,SAAS,kBAAkB,EAAE,SAAS,UAAU,WAAW,GAAG,SAAiC;CAC7F,OACE,qBAAC,OAAD;EAAK,WAAW,GAAG,0CAA0C,SAAS;EAAG,GAAI;EAA7E,UAAA,CACG,SACD,oBAAC,OAAD;GAAK,WAAU;GAAiC;EAAc,CAAA,CAC3D;;AAET;;;;AAKA,SAAS,yBAAyB,EAAE,WAAW,GAAG,SAA8B;CAC9E,OAAO,oBAAC,KAAD;EAAG,WAAW,GAAG,oBAAoB,SAAS;EAAG,GAAI;CAAQ,CAAA;AACtE;;;;;AAMA,SAAS,qBAAqB,EAAE,WAAW,GAAG,SAAuC;CACnF,MAAM,EAAE,SAAS,IAAI,oBAAoB;CAEzC,OAAO,oBAAC,OAAD;EAAO,WAAW,GAAG,WAAW,CAAC,QAAQ,YAAY,SAAS;EAAG,GAAI;CAAQ,CAAA;AACtF"}
1
+ {"version":3,"file":"section-header.js","names":[],"sources":["../../src/components/section-header.tsx"],"sourcesContent":["import { useRender } from \"@base-ui/react/use-render\"\nimport { type ComponentProps, createContext, type ReactNode, use } from \"react\"\nimport { Heading, type HeadingProps } from \"~/components/heading\"\nimport { Stack } from \"~/components/stack\"\nimport { descriptionClasses } from \"~/lib/classes\"\nimport { variants, cn, type VariantProps } from \"~/lib/variants\"\n\nconst sectionHeaderVariants = variants({\n base: \"flex items-center w-full min-w-0\",\n\n variants: {\n alignment: {\n left: \"justify-between text-start\",\n center: \"justify-center text-center\",\n },\n size: {\n page: \"gap-y-3 gap-x-6 max-sm:flex-col max-sm:items-stretch lg:gap-x-12\",\n card: \"gap-y-2 gap-x-6 max-sm:flex-col max-sm:items-stretch\",\n panel: \"gap-y-2 gap-x-6\",\n hero: \"gap-y-3 gap-x-6 lg:gap-x-12\",\n },\n wrap: {\n true: \"flex-wrap\",\n false: \"flex-nowrap\",\n },\n },\n\n defaultVariants: {\n alignment: \"left\",\n wrap: true,\n },\n})\n\ntype SectionHeaderSize = NonNullable<VariantProps<typeof sectionHeaderVariants>[\"size\"]>\n\n/**\n * The title each header scale gets, alongside the spacing the variant above gives it: `page`\n * for a route heading, `card` for a standalone card, `panel` for a dashboard or list panel,\n * `hero` for the centered auth and callback screens. The `as` entry keeps the heading outline\n * intact — one `<h1>` per route, `<h2>` for everything nested inside it.\n */\nconst sectionHeaderTitles = {\n page: { size: \"h3\", as: \"h1\" },\n card: { size: \"h4\", as: \"h2\" },\n panel: { size: \"h5\", as: \"h2\" },\n hero: { size: \"h2\", as: \"h1\" },\n} as const satisfies Record<SectionHeaderSize, { size: HeadingProps[\"size\"]; as: string }>\n\nconst SectionHeaderContext = createContext<{ size: SectionHeaderSize; wrap: boolean }>({\n size: \"page\",\n wrap: true,\n})\n\ntype SectionHeaderProps = Omit<useRender.ComponentProps<\"div\">, \"title\"> &\n Omit<VariantProps<typeof sectionHeaderVariants>, \"wrap\"> & {\n /**\n * Keep the title block and the actions on one line instead of wrapping them.\n */\n wrap?: boolean\n /**\n * Title block. Given any of `title`/`description`/`leading`/`actions`, the header renders\n * its own `SectionHeaderBody` (empty if there is nothing but actions, so they still sit right);\n * leave them all out to compose the body by hand, e.g. an entity header with dot-joined\n * meta or the dashboard greeting.\n */\n title?: ReactNode\n description?: ReactNode\n actions?: ReactNode\n /**\n * Leading visual beside the title, e.g. an entity avatar.\n */\n leading?: ReactNode\n }\n\n/**\n * The one header for every surface in the app: pages, cards, list panels, dialogs and settings\n * sections. Pass `title`/`description`/`actions` for the standard block, or compose\n * `SectionHeaderBody` + `SectionHeaderActions` as children when the content is bespoke.\n */\nfunction SectionHeader({\n className,\n alignment = \"left\",\n size = \"page\",\n wrap = true,\n title,\n description,\n actions,\n leading,\n children,\n render,\n ...props\n}: SectionHeaderProps) {\n const element = useRender({\n render,\n defaultTagName: \"div\",\n props: {\n className: cn(sectionHeaderVariants({ alignment, size, wrap, className })),\n children: (\n <>\n {(title || description || leading || actions) && (\n <SectionHeaderBody\n leading={leading}\n className={cn(alignment === \"center\" && \"justify-center\")}\n >\n {title && <SectionHeaderTitle>{title}</SectionHeaderTitle>}\n {description && <SectionHeaderDescription>{description}</SectionHeaderDescription>}\n </SectionHeaderBody>\n )}\n\n {actions && <SectionHeaderActions>{actions}</SectionHeaderActions>}\n\n {children}\n </>\n ),\n ...props,\n },\n })\n\n return <SectionHeaderContext value={{ size, wrap }}>{element}</SectionHeaderContext>\n}\n\n/**\n * The page title. Its visual size and semantic level both follow the header's `size`, so a\n * page heading stays the route's single `<h1>` while cards and panels render `<h2>`s. Pass\n * `size`/`as` to override either one on its own.\n */\nfunction SectionHeaderTitle({ size, as, ...props }: HeadingProps) {\n const preset = sectionHeaderTitles[use(SectionHeaderContext).size]\n\n return <Heading size={size ?? preset.size} as={as ?? preset.as} {...props} />\n}\n\ntype SectionHeaderBodyProps = ComponentProps<\"div\"> & {\n /**\n * Leading visual, e.g. an entity avatar.\n */\n leading?: ReactNode\n}\n\n/**\n * The one title block for every header in the app (pages, cards, dialogs, settings sections):\n * an optional leading visual beside a column of `SectionHeaderTitle` + `SectionHeaderDescription`. Change the\n * spacing here and it changes everywhere.\n */\nfunction SectionHeaderBody({ leading, children, className, ...props }: SectionHeaderBodyProps) {\n return (\n <div className={cn(\"flex min-w-0 flex-1 items-center gap-3\", className)} {...props}>\n {leading}\n <div className=\"flex min-w-0 flex-col gap-1.5\">{children}</div>\n </div>\n )\n}\n\n/**\n * The muted line under a title. Wraps by default; pass `truncate` for one-line entity meta.\n */\nfunction SectionHeaderDescription({ className, ...props }: ComponentProps<\"p\">) {\n return <p className={cn(descriptionClasses, className)} {...props} />\n}\n\n/**\n * Right-aligned actions. Stops shrinking when the header is `wrap={false}`, so a long title\n * truncates instead of squeezing the buttons.\n */\nfunction SectionHeaderActions({ className, ...props }: ComponentProps<typeof Stack>) {\n const { size, wrap } = use(SectionHeaderContext)\n\n return (\n <Stack\n className={cn(\n \"-my-0.5\",\n !wrap && \"shrink-0\",\n // Stacked under a page title, the trailing (primary) action leads the row.\n size === \"page\" && \"max-sm:flex-row-reverse max-sm:justify-end\",\n className,\n )}\n {...props}\n />\n )\n}\n\nexport {\n SectionHeader,\n SectionHeaderTitle,\n SectionHeaderBody,\n SectionHeaderDescription,\n SectionHeaderActions,\n}\n"],"mappings":";;;;;;;;AAOA,MAAM,wBAAwB,SAAS;CACrC,MAAM;CAEN,UAAU;EACR,WAAW;GACT,MAAM;GACN,QAAQ;EACV;EACA,MAAM;GACJ,MAAM;GACN,MAAM;GACN,OAAO;GACP,MAAM;EACR;EACA,MAAM;GACJ,MAAM;GACN,OAAO;EACT;CACF;CAEA,iBAAiB;EACf,WAAW;EACX,MAAM;CACR;AACF,CAAC;;;;;;;AAUD,MAAM,sBAAsB;CAC1B,MAAM;EAAE,MAAM;EAAM,IAAI;CAAK;CAC7B,MAAM;EAAE,MAAM;EAAM,IAAI;CAAK;CAC7B,OAAO;EAAE,MAAM;EAAM,IAAI;CAAK;CAC9B,MAAM;EAAE,MAAM;EAAM,IAAI;CAAK;AAC/B;AAEA,MAAM,uBAAuB,cAA0D;CACrF,MAAM;CACN,MAAM;AACR,CAAC;;;;;;AA4BD,SAAS,cAAc,EACrB,WACA,YAAY,QACZ,OAAO,QACP,OAAO,MACP,OACA,aACA,SACA,SACA,UACA,QACA,GAAG,SACkB;CACrB,MAAM,UAAU,UAAU;EACxB;EACA,gBAAgB;EAChB,OAAO;GACL,WAAW,GAAG,sBAAsB;IAAE;IAAW;IAAM;IAAM;GAAU,CAAC,CAAC;GACzE,UACE,qBAAA,UAAA,EAAA,UAAA;KACI,SAAS,eAAe,WAAW,YACnC,qBAAC,mBAAD;KACW;KACT,WAAW,GAAG,cAAc,YAAY,gBAAgB;KAF1D,UAAA,CAIG,SAAS,oBAAC,oBAAD,EAAA,UAAqB,MAA0B,CAAA,GACxD,eAAe,oBAAC,0BAAD,EAAA,UAA2B,YAAsC,CAAA,CAChE;;IAGpB,WAAW,oBAAC,sBAAD,EAAA,UAAuB,QAA8B,CAAA;IAEhE;GACD,EAAA,CAAA;GAEJ,GAAG;EACL;CACF,CAAC;CAED,OAAO,oBAAC,sBAAD;EAAsB,OAAO;GAAE;GAAM;EAAK;EAAI,UAAA;CAA8B,CAAA;AACrF;;;;;;AAOA,SAAS,mBAAmB,EAAE,MAAM,IAAI,GAAG,SAAuB;CAChE,MAAM,SAAS,oBAAoB,IAAI,oBAAoB,CAAC,CAAC;CAE7D,OAAO,oBAAC,SAAD;EAAS,MAAM,QAAQ,OAAO;EAAM,IAAI,MAAM,OAAO;EAAI,GAAI;CAAQ,CAAA;AAC9E;;;;;;AAcA,SAAS,kBAAkB,EAAE,SAAS,UAAU,WAAW,GAAG,SAAiC;CAC7F,OACE,qBAAC,OAAD;EAAK,WAAW,GAAG,0CAA0C,SAAS;EAAG,GAAI;EAA7E,UAAA,CACG,SACD,oBAAC,OAAD;GAAK,WAAU;GAAiC;EAAc,CAAA,CAC3D;;AAET;;;;AAKA,SAAS,yBAAyB,EAAE,WAAW,GAAG,SAA8B;CAC9E,OAAO,oBAAC,KAAD;EAAG,WAAW,GAAG,oBAAoB,SAAS;EAAG,GAAI;CAAQ,CAAA;AACtE;;;;;AAMA,SAAS,qBAAqB,EAAE,WAAW,GAAG,SAAuC;CACnF,MAAM,EAAE,MAAM,SAAS,IAAI,oBAAoB;CAE/C,OACE,oBAAC,OAAD;EACE,WAAW,GACT,WACA,CAAC,QAAQ,YAET,SAAS,UAAU,8CACnB,SACF;EACA,GAAI;CACL,CAAA;AAEL"}
@@ -74,7 +74,7 @@ function SelectLabel({ className, ...props }) {
74
74
  function SelectItem({ className, children, label, ...props }) {
75
75
  return /* @__PURE__ */ jsxs(Select$1.Item, {
76
76
  label: label ?? (typeof children === "string" ? children : void 0),
77
- className: cn(`relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 ${controlTextClasses} text-secondary-foreground outline-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50`, className),
77
+ className: cn(`relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 pointer-coarse:min-h-10 ${controlTextClasses} text-secondary-foreground outline-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50`, className),
78
78
  ...props,
79
79
  children: [/* @__PURE__ */ jsx("span", {
80
80
  className: "absolute left-2 flex size-3.5 items-center justify-center",
@@ -1 +1 @@
1
- {"version":3,"file":"select.js","names":["SelectPrimitive"],"sources":["../../src/components/select.tsx"],"sourcesContent":["\"use client\"\n\nimport { Select as SelectPrimitive } from \"@base-ui/react/select\"\nimport { inputVariants } from \"~/components/input\"\nimport { controlTextClasses, popoverAnimationClasses } from \"~/lib/classes\"\nimport { CheckIcon, ChevronDownIcon, ChevronUpIcon, ChevronsUpDownIcon } from \"~/lib/icons\"\nimport { variants, cn, type VariantProps } from \"~/lib/variants\"\n\nconst Select = SelectPrimitive.Root\nconst SelectGroup = SelectPrimitive.Group\nconst SelectValue = SelectPrimitive.Value\n\nfunction SelectTrigger({\n children,\n className,\n hover = true,\n focus = true,\n size,\n ...props\n}: SelectPrimitive.Trigger.Props & VariantProps<typeof inputVariants>) {\n return (\n <SelectPrimitive.Trigger\n className={cn(\n inputVariants({ hover, focus, size }),\n \"flex items-center justify-between\",\n className,\n )}\n {...props}\n >\n <span className=\"truncate\">{children}</span>\n\n <SelectPrimitive.Icon\n render={<ChevronsUpDownIcon className=\"ml-1 size-4 shrink-0 opacity-50\" />}\n />\n </SelectPrimitive.Trigger>\n )\n}\n\nfunction SelectScrollUpButton({ className, ...props }: SelectPrimitive.ScrollUpArrow.Props) {\n return (\n <SelectPrimitive.ScrollUpArrow\n className={cn(\"flex cursor-pointer items-center justify-center py-1\", className)}\n {...props}\n >\n <ChevronUpIcon />\n </SelectPrimitive.ScrollUpArrow>\n )\n}\n\nfunction SelectScrollDownButton({ className, ...props }: SelectPrimitive.ScrollDownArrow.Props) {\n return (\n <SelectPrimitive.ScrollDownArrow\n className={cn(\"flex cursor-pointer items-center justify-center py-1\", className)}\n {...props}\n >\n <ChevronDownIcon />\n </SelectPrimitive.ScrollDownArrow>\n )\n}\n\nconst selectScrollButtonVariants = variants({\n base: \"absolute inset-x-0 z-10 bg-background animate-in fade-in-0 duration-300\",\n variants: {\n position: {\n top: \"top-0 mask-b-from-0\",\n bottom: \"bottom-0 mask-t-from-0\",\n },\n },\n})\n\nfunction SelectContent({\n className,\n children,\n align,\n side,\n sideOffset = 4,\n alignItemWithTrigger = false,\n ...props\n}: SelectPrimitive.Popup.Props &\n Pick<\n SelectPrimitive.Positioner.Props,\n \"align\" | \"side\" | \"sideOffset\" | \"alignItemWithTrigger\"\n >) {\n return (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Positioner\n sideOffset={sideOffset}\n align={align}\n side={side}\n alignItemWithTrigger={alignItemWithTrigger}\n className=\"isolate z-50\"\n >\n <SelectPrimitive.Popup\n className={cn(\n \"relative z-50 isolate max-h-(--available-height) min-w-(--anchor-width) overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md backdrop-blur-xs\",\n !alignItemWithTrigger && popoverAnimationClasses,\n className,\n )}\n {...props}\n >\n <SelectScrollUpButton className={selectScrollButtonVariants({ position: \"top\" })} />\n <SelectPrimitive.List className=\"p-1\">{children}</SelectPrimitive.List>\n <SelectScrollDownButton className={selectScrollButtonVariants({ position: \"bottom\" })} />\n </SelectPrimitive.Popup>\n </SelectPrimitive.Positioner>\n </SelectPrimitive.Portal>\n )\n}\n\nfunction SelectLabel({ className, ...props }: SelectPrimitive.GroupLabel.Props) {\n return (\n <SelectPrimitive.GroupLabel\n className={cn(\"px-2 py-1.5 text-sm font-medium\", className)}\n {...props}\n />\n )\n}\n\nfunction SelectItem({ className, children, label, ...props }: SelectPrimitive.Item.Props) {\n return (\n <SelectPrimitive.Item\n label={label ?? (typeof children === \"string\" ? children : undefined)}\n className={cn(\n `relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 ${controlTextClasses} text-secondary-foreground outline-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50`,\n className,\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex size-3.5 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n )\n}\n\nfunction SelectSeparator({ className, ...props }: SelectPrimitive.Separator.Props) {\n return (\n <SelectPrimitive.Separator className={cn(\"-mx-1 my-1 h-px bg-border\", className)} {...props} />\n )\n}\n\nexport {\n Select,\n SelectGroup,\n SelectValue,\n SelectTrigger,\n SelectContent,\n SelectLabel,\n SelectItem,\n SelectSeparator,\n SelectScrollUpButton,\n SelectScrollDownButton,\n}\n"],"mappings":";;;;;;;;AAQA,MAAM,SAASA,SAAgB;AAC/B,MAAM,cAAcA,SAAgB;AACpC,MAAM,cAAcA,SAAgB;AAEpC,SAAS,cAAc,EACrB,UACA,WACA,QAAQ,MACR,QAAQ,MACR,MACA,GAAG,SACkE;CACrE,OACE,qBAACA,SAAgB,SAAjB;EACE,WAAW,GACT,cAAc;GAAE;GAAO;GAAO;EAAK,CAAC,GACpC,qCACA,SACF;EACA,GAAI;EANN,UAAA,CAQE,oBAAC,QAAD;GAAM,WAAU;GAAY;EAAe,CAAA,GAE3C,oBAACA,SAAgB,MAAjB,EACE,QAAQ,oBAAC,oBAAD,EAAoB,WAAU,kCAAmC,CAAA,EAC1E,CAAA,CACsB;;AAE7B;AAEA,SAAS,qBAAqB,EAAE,WAAW,GAAG,SAA8C;CAC1F,OACE,oBAACA,SAAgB,eAAjB;EACE,WAAW,GAAG,wDAAwD,SAAS;EAC/E,GAAI;EAEJ,UAAA,oBAAC,eAAD,CAAgB,CAAA;CACa,CAAA;AAEnC;AAEA,SAAS,uBAAuB,EAAE,WAAW,GAAG,SAAgD;CAC9F,OACE,oBAACA,SAAgB,iBAAjB;EACE,WAAW,GAAG,wDAAwD,SAAS;EAC/E,GAAI;EAEJ,UAAA,oBAAC,iBAAD,CAAkB,CAAA;CACa,CAAA;AAErC;AAEA,MAAM,6BAA6B,SAAS;CAC1C,MAAM;CACN,UAAU,EACR,UAAU;EACR,KAAK;EACL,QAAQ;CACV,EACF;AACF,CAAC;AAED,SAAS,cAAc,EACrB,WACA,UACA,OACA,MACA,aAAa,GACb,uBAAuB,OACvB,GAAG,SAKA;CACH,OACE,oBAACA,SAAgB,QAAjB,EAAA,UACE,oBAACA,SAAgB,YAAjB;EACc;EACL;EACD;EACgB;EACtB,WAAU;EAEV,UAAA,qBAACA,SAAgB,OAAjB;GACE,WAAW,GACT,2KACA,CAAC,wBAAwB,yBACzB,SACF;GACA,GAAI;GANN,UAAA;IAQE,oBAAC,sBAAD,EAAsB,WAAW,2BAA2B,EAAE,UAAU,MAAM,CAAC,EAAI,CAAA;IACnF,oBAACA,SAAgB,MAAjB;KAAsB,WAAU;KAAO;IAA+B,CAAA;IACtE,oBAAC,wBAAD,EAAwB,WAAW,2BAA2B,EAAE,UAAU,SAAS,CAAC,EAAI,CAAA;GACnE;;CACG,CAAA,EACN,CAAA;AAE5B;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,SAA2C;CAC9E,OACE,oBAACA,SAAgB,YAAjB;EACE,WAAW,GAAG,mCAAmC,SAAS;EAC1D,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,WAAW,EAAE,WAAW,UAAU,OAAO,GAAG,SAAqC;CACxF,OACE,qBAACA,SAAgB,MAAjB;EACE,OAAO,UAAU,OAAO,aAAa,WAAW,WAAW,KAAA;EAC3D,WAAW,GACT,4FAA4F,mBAAmB,kJAC/G,SACF;EACA,GAAI;EANN,UAAA,CAQE,oBAAC,QAAD;GAAM,WAAU;GACd,UAAA,oBAACA,SAAgB,eAAjB,EAAA,UACE,oBAAC,WAAD,EAAW,WAAU,SAAU,CAAA,EACF,CAAA;EAC3B,CAAA,GACN,oBAACA,SAAgB,UAAjB,EAA2B,SAAmC,CAAA,CAC1C;;AAE1B;AAEA,SAAS,gBAAgB,EAAE,WAAW,GAAG,SAA0C;CACjF,OACE,oBAACA,SAAgB,WAAjB;EAA2B,WAAW,GAAG,6BAA6B,SAAS;EAAG,GAAI;CAAQ,CAAA;AAElG"}
1
+ {"version":3,"file":"select.js","names":["SelectPrimitive"],"sources":["../../src/components/select.tsx"],"sourcesContent":["\"use client\"\n\nimport { Select as SelectPrimitive } from \"@base-ui/react/select\"\nimport { inputVariants } from \"~/components/input\"\nimport { controlTextClasses, popoverAnimationClasses } from \"~/lib/classes\"\nimport { CheckIcon, ChevronDownIcon, ChevronUpIcon, ChevronsUpDownIcon } from \"~/lib/icons\"\nimport { variants, cn, type VariantProps } from \"~/lib/variants\"\n\nconst Select = SelectPrimitive.Root\nconst SelectGroup = SelectPrimitive.Group\nconst SelectValue = SelectPrimitive.Value\n\nfunction SelectTrigger({\n children,\n className,\n hover = true,\n focus = true,\n size,\n ...props\n}: SelectPrimitive.Trigger.Props & VariantProps<typeof inputVariants>) {\n return (\n <SelectPrimitive.Trigger\n className={cn(\n inputVariants({ hover, focus, size }),\n \"flex items-center justify-between\",\n className,\n )}\n {...props}\n >\n <span className=\"truncate\">{children}</span>\n\n <SelectPrimitive.Icon\n render={<ChevronsUpDownIcon className=\"ml-1 size-4 shrink-0 opacity-50\" />}\n />\n </SelectPrimitive.Trigger>\n )\n}\n\nfunction SelectScrollUpButton({ className, ...props }: SelectPrimitive.ScrollUpArrow.Props) {\n return (\n <SelectPrimitive.ScrollUpArrow\n className={cn(\"flex cursor-pointer items-center justify-center py-1\", className)}\n {...props}\n >\n <ChevronUpIcon />\n </SelectPrimitive.ScrollUpArrow>\n )\n}\n\nfunction SelectScrollDownButton({ className, ...props }: SelectPrimitive.ScrollDownArrow.Props) {\n return (\n <SelectPrimitive.ScrollDownArrow\n className={cn(\"flex cursor-pointer items-center justify-center py-1\", className)}\n {...props}\n >\n <ChevronDownIcon />\n </SelectPrimitive.ScrollDownArrow>\n )\n}\n\nconst selectScrollButtonVariants = variants({\n base: \"absolute inset-x-0 z-10 bg-background animate-in fade-in-0 duration-300\",\n variants: {\n position: {\n top: \"top-0 mask-b-from-0\",\n bottom: \"bottom-0 mask-t-from-0\",\n },\n },\n})\n\nfunction SelectContent({\n className,\n children,\n align,\n side,\n sideOffset = 4,\n alignItemWithTrigger = false,\n ...props\n}: SelectPrimitive.Popup.Props &\n Pick<\n SelectPrimitive.Positioner.Props,\n \"align\" | \"side\" | \"sideOffset\" | \"alignItemWithTrigger\"\n >) {\n return (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Positioner\n sideOffset={sideOffset}\n align={align}\n side={side}\n alignItemWithTrigger={alignItemWithTrigger}\n className=\"isolate z-50\"\n >\n <SelectPrimitive.Popup\n className={cn(\n \"relative z-50 isolate max-h-(--available-height) min-w-(--anchor-width) overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md backdrop-blur-xs\",\n !alignItemWithTrigger && popoverAnimationClasses,\n className,\n )}\n {...props}\n >\n <SelectScrollUpButton className={selectScrollButtonVariants({ position: \"top\" })} />\n <SelectPrimitive.List className=\"p-1\">{children}</SelectPrimitive.List>\n <SelectScrollDownButton className={selectScrollButtonVariants({ position: \"bottom\" })} />\n </SelectPrimitive.Popup>\n </SelectPrimitive.Positioner>\n </SelectPrimitive.Portal>\n )\n}\n\nfunction SelectLabel({ className, ...props }: SelectPrimitive.GroupLabel.Props) {\n return (\n <SelectPrimitive.GroupLabel\n className={cn(\"px-2 py-1.5 text-sm font-medium\", className)}\n {...props}\n />\n )\n}\n\nfunction SelectItem({ className, children, label, ...props }: SelectPrimitive.Item.Props) {\n return (\n <SelectPrimitive.Item\n label={label ?? (typeof children === \"string\" ? children : undefined)}\n className={cn(\n `relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 pointer-coarse:min-h-10 ${controlTextClasses} text-secondary-foreground outline-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50`,\n className,\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex size-3.5 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n )\n}\n\nfunction SelectSeparator({ className, ...props }: SelectPrimitive.Separator.Props) {\n return (\n <SelectPrimitive.Separator className={cn(\"-mx-1 my-1 h-px bg-border\", className)} {...props} />\n )\n}\n\nexport {\n Select,\n SelectGroup,\n SelectValue,\n SelectTrigger,\n SelectContent,\n SelectLabel,\n SelectItem,\n SelectSeparator,\n SelectScrollUpButton,\n SelectScrollDownButton,\n}\n"],"mappings":";;;;;;;;AAQA,MAAM,SAASA,SAAgB;AAC/B,MAAM,cAAcA,SAAgB;AACpC,MAAM,cAAcA,SAAgB;AAEpC,SAAS,cAAc,EACrB,UACA,WACA,QAAQ,MACR,QAAQ,MACR,MACA,GAAG,SACkE;CACrE,OACE,qBAACA,SAAgB,SAAjB;EACE,WAAW,GACT,cAAc;GAAE;GAAO;GAAO;EAAK,CAAC,GACpC,qCACA,SACF;EACA,GAAI;EANN,UAAA,CAQE,oBAAC,QAAD;GAAM,WAAU;GAAY;EAAe,CAAA,GAE3C,oBAACA,SAAgB,MAAjB,EACE,QAAQ,oBAAC,oBAAD,EAAoB,WAAU,kCAAmC,CAAA,EAC1E,CAAA,CACsB;;AAE7B;AAEA,SAAS,qBAAqB,EAAE,WAAW,GAAG,SAA8C;CAC1F,OACE,oBAACA,SAAgB,eAAjB;EACE,WAAW,GAAG,wDAAwD,SAAS;EAC/E,GAAI;EAEJ,UAAA,oBAAC,eAAD,CAAgB,CAAA;CACa,CAAA;AAEnC;AAEA,SAAS,uBAAuB,EAAE,WAAW,GAAG,SAAgD;CAC9F,OACE,oBAACA,SAAgB,iBAAjB;EACE,WAAW,GAAG,wDAAwD,SAAS;EAC/E,GAAI;EAEJ,UAAA,oBAAC,iBAAD,CAAkB,CAAA;CACa,CAAA;AAErC;AAEA,MAAM,6BAA6B,SAAS;CAC1C,MAAM;CACN,UAAU,EACR,UAAU;EACR,KAAK;EACL,QAAQ;CACV,EACF;AACF,CAAC;AAED,SAAS,cAAc,EACrB,WACA,UACA,OACA,MACA,aAAa,GACb,uBAAuB,OACvB,GAAG,SAKA;CACH,OACE,oBAACA,SAAgB,QAAjB,EAAA,UACE,oBAACA,SAAgB,YAAjB;EACc;EACL;EACD;EACgB;EACtB,WAAU;EAEV,UAAA,qBAACA,SAAgB,OAAjB;GACE,WAAW,GACT,2KACA,CAAC,wBAAwB,yBACzB,SACF;GACA,GAAI;GANN,UAAA;IAQE,oBAAC,sBAAD,EAAsB,WAAW,2BAA2B,EAAE,UAAU,MAAM,CAAC,EAAI,CAAA;IACnF,oBAACA,SAAgB,MAAjB;KAAsB,WAAU;KAAO;IAA+B,CAAA;IACtE,oBAAC,wBAAD,EAAwB,WAAW,2BAA2B,EAAE,UAAU,SAAS,CAAC,EAAI,CAAA;GACnE;;CACG,CAAA,EACN,CAAA;AAE5B;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,SAA2C;CAC9E,OACE,oBAACA,SAAgB,YAAjB;EACE,WAAW,GAAG,mCAAmC,SAAS;EAC1D,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,WAAW,EAAE,WAAW,UAAU,OAAO,GAAG,SAAqC;CACxF,OACE,qBAACA,SAAgB,MAAjB;EACE,OAAO,UAAU,OAAO,aAAa,WAAW,WAAW,KAAA;EAC3D,WAAW,GACT,oHAAoH,mBAAmB,kJACvI,SACF;EACA,GAAI;EANN,UAAA,CAQE,oBAAC,QAAD;GAAM,WAAU;GACd,UAAA,oBAACA,SAAgB,eAAjB,EAAA,UACE,oBAAC,WAAD,EAAW,WAAU,SAAU,CAAA,EACF,CAAA;EAC3B,CAAA,GACN,oBAACA,SAAgB,UAAjB,EAA2B,SAAmC,CAAA,CAC1C;;AAE1B;AAEA,SAAS,gBAAgB,EAAE,WAAW,GAAG,SAA0C;CACjF,OACE,oBAACA,SAAgB,WAAjB;EAA2B,WAAW,GAAG,6BAA6B,SAAS;EAAG,GAAI;CAAQ,CAAA;AAElG"}
@@ -31,19 +31,19 @@ function StatStripItem({ label, value, format, hint, selected, render, className
31
31
  className: cn("relative min-w-0 flex-1 rounded-lg border border-transparent px-4 py-3 text-start", "before:absolute before:inset-y-2 before:-left-px before:w-px before:bg-border first:before:hidden max-sm:before:hidden", "group-data-[variant=plain]/strip:data-selected:before:hidden group-data-[variant=plain]/strip:[[data-selected]+&]:before:hidden", "group-data-[variant=track]/strip:before:-left-[8px]", "[&:is(button)]:hover:bg-foreground/3", "group-data-[variant=plain]/strip:rounded-none group-data-[variant=plain]/strip:px-5 group-data-[variant=plain]/strip:py-4 group-data-[variant=plain]/strip:before:inset-y-0 group-data-[variant=plain]/strip:before:left-0", "group-data-[variant=plain]/strip:data-selected:bg-muted", "group-data-[variant=track]/strip:data-selected:border-border group-data-[variant=track]/strip:data-selected:bg-card group-data-[variant=track]/strip:data-selected:shadow-xs", "group-data-[variant=track]/strip:not-data-selected:[&_[data-value]]:text-muted-foreground", "group-data-[variant=track]/strip:[&:is(button)]:hover:not-data-selected:bg-card/60", className),
32
32
  children: /* @__PURE__ */ jsxs(Fragment, { children: [
33
33
  /* @__PURE__ */ jsx("p", {
34
- className: "truncate text-muted-foreground text-sm",
34
+ className: "text-muted-foreground text-sm sm:truncate",
35
35
  children: label
36
36
  }),
37
37
  /* @__PURE__ */ jsx("p", {
38
38
  "data-value": true,
39
- className: "mt-0.5 truncate font-display font-semibold text-2xl tabular-nums",
39
+ className: "mt-0.5 truncate font-display font-semibold text-xl tabular-nums sm:text-2xl",
40
40
  children: /* @__PURE__ */ jsx(AnimatedNumber, {
41
41
  value,
42
42
  format
43
43
  })
44
44
  }),
45
45
  hint && /* @__PURE__ */ jsx("p", {
46
- className: "truncate text-muted-foreground text-xs",
46
+ className: "text-muted-foreground text-xs sm:truncate",
47
47
  children: hint
48
48
  })
49
49
  ] }),
@@ -1 +1 @@
1
- {"version":3,"file":"stat-strip.js","names":[],"sources":["../../src/components/stat-strip.tsx"],"sourcesContent":["import { useRender } from \"@base-ui/react/use-render\"\nimport type { Format } from \"@number-flow/react\"\nimport type { ComponentProps } from \"react\"\nimport { AnimatedNumber } from \"~/components/animated-number\"\nimport { cardSurfaceClasses } from \"~/components/card\"\nimport { cn, variants, type VariantProps } from \"~/lib/variants\"\n\nconst stripVariants = variants({\n base: \"group/strip grid grid-cols-2 gap-y-1 max-sm:[&>*:last-child:nth-child(odd)]:col-span-2 sm:flex sm:flex-wrap sm:gap-y-0\",\n\n variants: {\n variant: {\n // A white card of figures, divided by full-height hairlines.\n plain: cardSurfaceClasses,\n // A canvas-toned track where the selected figure sits on a raised card, like a\n // segmented control; for strips that drive the panel beside them.\n // 13px gap: a 1px divider then sits 6px from the card on either side, matching the 6px inset.\n track: \"rounded-xl bg-background p-1.5 sm:gap-x-[13px]\",\n },\n },\n\n defaultVariants: {\n variant: \"plain\",\n },\n})\n\ntype StatStripProps = ComponentProps<\"div\"> & VariantProps<typeof stripVariants>\n\nfunction StatStrip({ className, variant, ...props }: StatStripProps) {\n return (\n <div\n data-variant={variant ?? \"plain\"}\n className={cn(stripVariants({ variant, className }))}\n {...props}\n />\n )\n}\n\ntype StatStripItemProps = Omit<useRender.ComponentProps<\"div\">, \"children\"> & {\n label: string\n value: string | number\n format?: Format\n hint?: string\n /**\n * Marks the item as the one driving the panel beside it. Pass `render={<button ... />}`\n * to make it selectable.\n */\n selected?: boolean\n}\n\nfunction StatStripItem({\n label,\n value,\n format,\n hint,\n selected,\n render,\n className,\n ...props\n}: StatStripItemProps) {\n return useRender({\n render,\n defaultTagName: \"div\",\n props: {\n \"data-selected\": selected || undefined,\n className: cn(\n // The border is always laid out and only colored when selected, so toggling\n // never shifts the row.\n \"relative min-w-0 flex-1 rounded-lg border border-transparent px-4 py-3 text-start\",\n // Hairline divider between neighbours. The plain strip hides it around the\n // selected fill; the track keeps it, centered in the gap between cards.\n \"before:absolute before:inset-y-2 before:-left-px before:w-px before:bg-border first:before:hidden max-sm:before:hidden\",\n \"group-data-[variant=plain]/strip:data-selected:before:hidden group-data-[variant=plain]/strip:[[data-selected]+&]:before:hidden\",\n // Offsets are measured from the padding box, so the 1px border is added to land\n // exactly halfway across the 13px gap.\n \"group-data-[variant=track]/strip:before:-left-[8px]\",\n \"[&:is(button)]:hover:bg-foreground/3\",\n // Plain card: roomier cells, full-height dividers, selected item on a soft fill.\n \"group-data-[variant=plain]/strip:rounded-none group-data-[variant=plain]/strip:px-5 group-data-[variant=plain]/strip:py-4 group-data-[variant=plain]/strip:before:inset-y-0 group-data-[variant=plain]/strip:before:left-0\",\n \"group-data-[variant=plain]/strip:data-selected:bg-muted\",\n // Track: the selected item is a raised card; unselected figures recede.\n \"group-data-[variant=track]/strip:data-selected:border-border group-data-[variant=track]/strip:data-selected:bg-card group-data-[variant=track]/strip:data-selected:shadow-xs\",\n \"group-data-[variant=track]/strip:not-data-selected:[&_[data-value]]:text-muted-foreground\",\n \"group-data-[variant=track]/strip:[&:is(button)]:hover:not-data-selected:bg-card/60\",\n className,\n ),\n children: (\n <>\n <p className=\"truncate text-muted-foreground text-sm\">{label}</p>\n\n <p\n data-value\n className=\"mt-0.5 truncate font-display font-semibold text-2xl tabular-nums\"\n >\n <AnimatedNumber value={value} format={format} />\n </p>\n\n {hint && <p className=\"truncate text-muted-foreground text-xs\">{hint}</p>}\n </>\n ),\n ...props,\n },\n })\n}\n\nexport { StatStrip, StatStripItem }\n"],"mappings":";;;;;;AAOA,MAAM,gBAAgB,SAAS;CAC7B,MAAM;CAEN,UAAU,EACR,SAAS;EAEP,OAAO;EAIP,OAAO;CACT,EACF;CAEA,iBAAiB,EACf,SAAS,QACX;AACF,CAAC;AAID,SAAS,UAAU,EAAE,WAAW,SAAS,GAAG,SAAyB;CACnE,OACE,oBAAC,OAAD;EACE,gBAAc,WAAW;EACzB,WAAW,GAAG,cAAc;GAAE;GAAS;EAAU,CAAC,CAAC;EACnD,GAAI;CACL,CAAA;AAEL;AAcA,SAAS,cAAc,EACrB,OACA,OACA,QACA,MACA,UACA,QACA,WACA,GAAG,SACkB;CACrB,OAAO,UAAU;EACf;EACA,gBAAgB;EAChB,OAAO;GACL,iBAAiB,YAAY,KAAA;GAC7B,WAAW,GAGT,qFAGA,0HACA,mIAGA,uDACA,wCAEA,8NACA,2DAEA,gLACA,6FACA,sFACA,SACF;GACA,UACE,qBAAA,UAAA,EAAA,UAAA;IACE,oBAAC,KAAD;KAAG,WAAU;KAA0C,UAAA;IAAS,CAAA;IAEhE,oBAAC,KAAD;KACE,cAAA;KACA,WAAU;KAEV,UAAA,oBAAC,gBAAD;MAAuB;MAAe;KAAS,CAAA;IAC9C,CAAA;IAEF,QAAQ,oBAAC,KAAD;KAAG,WAAU;KAA0C,UAAA;IAAQ,CAAA;GACxE,EAAA,CAAA;GAEJ,GAAG;EACL;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"stat-strip.js","names":[],"sources":["../../src/components/stat-strip.tsx"],"sourcesContent":["import { useRender } from \"@base-ui/react/use-render\"\nimport type { Format } from \"@number-flow/react\"\nimport type { ComponentProps } from \"react\"\nimport { AnimatedNumber } from \"~/components/animated-number\"\nimport { cardSurfaceClasses } from \"~/components/card\"\nimport { cn, variants, type VariantProps } from \"~/lib/variants\"\n\nconst stripVariants = variants({\n base: \"group/strip grid grid-cols-2 gap-y-1 max-sm:[&>*:last-child:nth-child(odd)]:col-span-2 sm:flex sm:flex-wrap sm:gap-y-0\",\n\n variants: {\n variant: {\n // A white card of figures, divided by full-height hairlines.\n plain: cardSurfaceClasses,\n // A canvas-toned track where the selected figure sits on a raised card, like a\n // segmented control; for strips that drive the panel beside them.\n // 13px gap: a 1px divider then sits 6px from the card on either side, matching the 6px inset.\n track: \"rounded-xl bg-background p-1.5 sm:gap-x-[13px]\",\n },\n },\n\n defaultVariants: {\n variant: \"plain\",\n },\n})\n\ntype StatStripProps = ComponentProps<\"div\"> & VariantProps<typeof stripVariants>\n\nfunction StatStrip({ className, variant, ...props }: StatStripProps) {\n return (\n <div\n data-variant={variant ?? \"plain\"}\n className={cn(stripVariants({ variant, className }))}\n {...props}\n />\n )\n}\n\ntype StatStripItemProps = Omit<useRender.ComponentProps<\"div\">, \"children\"> & {\n label: string\n value: string | number\n format?: Format\n hint?: string\n /**\n * Marks the item as the one driving the panel beside it. Pass `render={<button ... />}`\n * to make it selectable.\n */\n selected?: boolean\n}\n\nfunction StatStripItem({\n label,\n value,\n format,\n hint,\n selected,\n render,\n className,\n ...props\n}: StatStripItemProps) {\n return useRender({\n render,\n defaultTagName: \"div\",\n props: {\n \"data-selected\": selected || undefined,\n className: cn(\n // The border is always laid out and only colored when selected, so toggling\n // never shifts the row.\n \"relative min-w-0 flex-1 rounded-lg border border-transparent px-4 py-3 text-start\",\n // Hairline divider between neighbours. The plain strip hides it around the\n // selected fill; the track keeps it, centered in the gap between cards.\n \"before:absolute before:inset-y-2 before:-left-px before:w-px before:bg-border first:before:hidden max-sm:before:hidden\",\n \"group-data-[variant=plain]/strip:data-selected:before:hidden group-data-[variant=plain]/strip:[[data-selected]+&]:before:hidden\",\n // Offsets are measured from the padding box, so the 1px border is added to land\n // exactly halfway across the 13px gap.\n \"group-data-[variant=track]/strip:before:-left-[8px]\",\n \"[&:is(button)]:hover:bg-foreground/3\",\n // Plain card: roomier cells, full-height dividers, selected item on a soft fill.\n \"group-data-[variant=plain]/strip:rounded-none group-data-[variant=plain]/strip:px-5 group-data-[variant=plain]/strip:py-4 group-data-[variant=plain]/strip:before:inset-y-0 group-data-[variant=plain]/strip:before:left-0\",\n \"group-data-[variant=plain]/strip:data-selected:bg-muted\",\n // Track: the selected item is a raised card; unselected figures recede.\n \"group-data-[variant=track]/strip:data-selected:border-border group-data-[variant=track]/strip:data-selected:bg-card group-data-[variant=track]/strip:data-selected:shadow-xs\",\n \"group-data-[variant=track]/strip:not-data-selected:[&_[data-value]]:text-muted-foreground\",\n \"group-data-[variant=track]/strip:[&:is(button)]:hover:not-data-selected:bg-card/60\",\n className,\n ),\n children: (\n <>\n <p className=\"text-muted-foreground text-sm sm:truncate\">{label}</p>\n\n <p\n data-value\n className=\"mt-0.5 truncate font-display font-semibold text-xl tabular-nums sm:text-2xl\"\n >\n <AnimatedNumber value={value} format={format} />\n </p>\n\n {hint && <p className=\"text-muted-foreground text-xs sm:truncate\">{hint}</p>}\n </>\n ),\n ...props,\n },\n })\n}\n\nexport { StatStrip, StatStripItem }\n"],"mappings":";;;;;;AAOA,MAAM,gBAAgB,SAAS;CAC7B,MAAM;CAEN,UAAU,EACR,SAAS;EAEP,OAAO;EAIP,OAAO;CACT,EACF;CAEA,iBAAiB,EACf,SAAS,QACX;AACF,CAAC;AAID,SAAS,UAAU,EAAE,WAAW,SAAS,GAAG,SAAyB;CACnE,OACE,oBAAC,OAAD;EACE,gBAAc,WAAW;EACzB,WAAW,GAAG,cAAc;GAAE;GAAS;EAAU,CAAC,CAAC;EACnD,GAAI;CACL,CAAA;AAEL;AAcA,SAAS,cAAc,EACrB,OACA,OACA,QACA,MACA,UACA,QACA,WACA,GAAG,SACkB;CACrB,OAAO,UAAU;EACf;EACA,gBAAgB;EAChB,OAAO;GACL,iBAAiB,YAAY,KAAA;GAC7B,WAAW,GAGT,qFAGA,0HACA,mIAGA,uDACA,wCAEA,8NACA,2DAEA,gLACA,6FACA,sFACA,SACF;GACA,UACE,qBAAA,UAAA,EAAA,UAAA;IACE,oBAAC,KAAD;KAAG,WAAU;KAA6C,UAAA;IAAS,CAAA;IAEnE,oBAAC,KAAD;KACE,cAAA;KACA,WAAU;KAEV,UAAA,oBAAC,gBAAD;MAAuB;MAAe;KAAS,CAAA;IAC9C,CAAA;IAEF,QAAQ,oBAAC,KAAD;KAAG,WAAU;KAA6C,UAAA;IAAQ,CAAA;GAC3E,EAAA,CAAA;GAEJ,GAAG;EACL;CACF,CAAC;AACH"}
@@ -10,6 +10,11 @@ function Toaster({ className, ...props }) {
10
10
  return /* @__PURE__ */ jsx(Toaster$1, {
11
11
  className: "pointer-events-auto",
12
12
  offset: 16,
13
+ mobileOffset: {
14
+ bottom: "calc(16px + env(safe-area-inset-bottom))",
15
+ left: 16,
16
+ right: 16
17
+ },
13
18
  toastOptions: {
14
19
  unstyled: true,
15
20
  classNames: {
@@ -1 +1 @@
1
- {"version":3,"file":"toaster.js","names":["Sonner"],"sources":["../../src/components/toaster.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\"\nimport { Toaster as Sonner } from \"sonner\"\nimport { controlTextClasses } from \"~/lib/classes\"\nimport { CircleCheckIcon, CircleHelpIcon, CircleXIcon, LoaderIcon } from \"~/lib/icons\"\nimport { cn } from \"~/lib/variants\"\n\n/** The halo that lifts a solid-colored toast off whatever it floats over. */\nconst toastRingClasses = \"ring ring-background/50\"\n\nexport function Toaster({ className, ...props }: ComponentProps<typeof Sonner>) {\n return (\n <Sonner\n className=\"pointer-events-auto\"\n offset={16}\n toastOptions={{\n unstyled: true,\n classNames: {\n /*\n * Sonner joins `toast` with exactly one type key per toast (`default` for untyped\n * ones) as a plain string, so tailwind-merge never sees the pair. Anything that\n * differs per type — background, text, halo — therefore lives on the type keys\n * only, and never gets cancelled here.\n */\n toast: cn(\n `flex items-start gap-2 p-4 ${controlTextClasses} font-medium rounded-lg shadow-sm`,\n className,\n ),\n default: \"bg-background border border-border text-foreground\",\n info: cn(\"bg-foreground text-background\", toastRingClasses),\n success: cn(\"bg-success text-white\", toastRingClasses),\n error: cn(\"bg-danger text-white\", toastRingClasses),\n content: \"w-full\",\n description: \"font-normal opacity-80\",\n icon: \"mt-0.5\",\n },\n }}\n icons={{\n info: <CircleHelpIcon className=\"size-4\" />,\n success: <CircleCheckIcon className=\"size-4\" />,\n error: <CircleXIcon className=\"size-4\" />,\n loading: <LoaderIcon className=\"size-4 animate-spin\" />,\n }}\n {...props}\n />\n )\n}\n"],"mappings":";;;;;;;AAOA,MAAM,mBAAmB;AAEzB,SAAgB,QAAQ,EAAE,WAAW,GAAG,SAAwC;CAC9E,OACE,oBAACA,WAAD;EACE,WAAU;EACV,QAAQ;EACR,cAAc;GACZ,UAAU;GACV,YAAY;IAOV,OAAO,GACL,8BAA8B,mBAAmB,oCACjD,SACF;IACA,SAAS;IACT,MAAM,GAAG,iCAAiC,gBAAgB;IAC1D,SAAS,GAAG,yBAAyB,gBAAgB;IACrD,OAAO,GAAG,wBAAwB,gBAAgB;IAClD,SAAS;IACT,aAAa;IACb,MAAM;GACR;EACF;EACA,OAAO;GACL,MAAM,oBAAC,gBAAD,EAAgB,WAAU,SAAU,CAAA;GAC1C,SAAS,oBAAC,iBAAD,EAAiB,WAAU,SAAU,CAAA;GAC9C,OAAO,oBAAC,aAAD,EAAa,WAAU,SAAU,CAAA;GACxC,SAAS,oBAAC,YAAD,EAAY,WAAU,sBAAuB,CAAA;EACxD;EACA,GAAI;CACL,CAAA;AAEL"}
1
+ {"version":3,"file":"toaster.js","names":["Sonner"],"sources":["../../src/components/toaster.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\"\nimport { Toaster as Sonner } from \"sonner\"\nimport { controlTextClasses } from \"~/lib/classes\"\nimport { CircleCheckIcon, CircleHelpIcon, CircleXIcon, LoaderIcon } from \"~/lib/icons\"\nimport { cn } from \"~/lib/variants\"\n\n/** The halo that lifts a solid-colored toast off whatever it floats over. */\nconst toastRingClasses = \"ring ring-background/50\"\n\nexport function Toaster({ className, ...props }: ComponentProps<typeof Sonner>) {\n return (\n <Sonner\n className=\"pointer-events-auto\"\n offset={16}\n mobileOffset={{ bottom: \"calc(16px + env(safe-area-inset-bottom))\", left: 16, right: 16 }}\n toastOptions={{\n unstyled: true,\n classNames: {\n /*\n * Sonner joins `toast` with exactly one type key per toast (`default` for untyped\n * ones) as a plain string, so tailwind-merge never sees the pair. Anything that\n * differs per type — background, text, halo — therefore lives on the type keys\n * only, and never gets cancelled here.\n */\n toast: cn(\n `flex items-start gap-2 p-4 ${controlTextClasses} font-medium rounded-lg shadow-sm`,\n className,\n ),\n default: \"bg-background border border-border text-foreground\",\n info: cn(\"bg-foreground text-background\", toastRingClasses),\n success: cn(\"bg-success text-white\", toastRingClasses),\n error: cn(\"bg-danger text-white\", toastRingClasses),\n content: \"w-full\",\n description: \"font-normal opacity-80\",\n icon: \"mt-0.5\",\n },\n }}\n icons={{\n info: <CircleHelpIcon className=\"size-4\" />,\n success: <CircleCheckIcon className=\"size-4\" />,\n error: <CircleXIcon className=\"size-4\" />,\n loading: <LoaderIcon className=\"size-4 animate-spin\" />,\n }}\n {...props}\n />\n )\n}\n"],"mappings":";;;;;;;AAOA,MAAM,mBAAmB;AAEzB,SAAgB,QAAQ,EAAE,WAAW,GAAG,SAAwC;CAC9E,OACE,oBAACA,WAAD;EACE,WAAU;EACV,QAAQ;EACR,cAAc;GAAE,QAAQ;GAA4C,MAAM;GAAI,OAAO;EAAG;EACxF,cAAc;GACZ,UAAU;GACV,YAAY;IAOV,OAAO,GACL,8BAA8B,mBAAmB,oCACjD,SACF;IACA,SAAS;IACT,MAAM,GAAG,iCAAiC,gBAAgB;IAC1D,SAAS,GAAG,yBAAyB,gBAAgB;IACrD,OAAO,GAAG,wBAAwB,gBAAgB;IAClD,SAAS;IACT,aAAa;IACb,MAAM;GACR;EACF;EACA,OAAO;GACL,MAAM,oBAAC,gBAAD,EAAgB,WAAU,SAAU,CAAA;GAC1C,SAAS,oBAAC,iBAAD,EAAiB,WAAU,SAAU,CAAA;GAC9C,OAAO,oBAAC,aAAD,EAAa,WAAU,SAAU,CAAA;GACxC,SAAS,oBAAC,YAAD,EAAY,WAAU,sBAAuB,CAAA;EACxD;EACA,GAAI;CACL,CAAA;AAEL"}
package/dist/styles.css CHANGED
@@ -110,6 +110,30 @@
110
110
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
111
111
 
112
112
  --animate-fill-bar: fill-bar 0.5s var(--ease-out-expo);
113
+ --animate-slide-up-and-fade: slide-up-and-fade 0.5s var(--ease-out-expo);
114
+ --animate-slide-down-and-fade: slide-down-and-fade 0.5s var(--ease-out-expo);
115
+
116
+ @keyframes slide-up-and-fade {
117
+ from {
118
+ opacity: 0;
119
+ transform: translateY(8px);
120
+ }
121
+ to {
122
+ opacity: 1;
123
+ transform: translateY(0);
124
+ }
125
+ }
126
+
127
+ @keyframes slide-down-and-fade {
128
+ from {
129
+ opacity: 0;
130
+ transform: translateY(-8px);
131
+ }
132
+ to {
133
+ opacity: 1;
134
+ transform: translateY(0);
135
+ }
136
+ }
113
137
 
114
138
  @keyframes fill-bar {
115
139
  from {
package/package.json CHANGED
@@ -108,5 +108,5 @@
108
108
  "engines": {
109
109
  "node": ">=18"
110
110
  },
111
- "version": "1.0.1"
111
+ "version": "1.1.0"
112
112
  }