@avenue-ticketing/ui 0.1.0 → 0.3.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.
@@ -0,0 +1,55 @@
1
+ import * as React from 'react';
2
+
3
+ /** Matches `button.tsx` primary / secondary — no separate outline variants. */
4
+ declare const variantClass: {
5
+ readonly primary: "border border-transparent bg-primary text-background";
6
+ readonly secondary: "border border-primary/10 bg-transparent text-primary";
7
+ };
8
+ /** Same keys as `button.tsx` `roundedClass`. */
9
+ declare const roundedClass: {
10
+ readonly full: "rounded-full";
11
+ readonly lg: "rounded-lg";
12
+ readonly md: "rounded-md";
13
+ };
14
+ declare const sizeClass: {
15
+ readonly md: "min-h-6 min-w-6 px-2 text-xs";
16
+ readonly lg: "min-h-7 min-w-7 px-2.5 text-sm";
17
+ };
18
+ type BadgeProps = React.HTMLAttributes<HTMLSpanElement> & {
19
+ /** @default secondary — same as Button default. */
20
+ variant?: keyof typeof variantClass;
21
+ /** @default md */
22
+ size?: keyof typeof sizeClass;
23
+ /**
24
+ * Corner radius — same options as `Button` (`full` | `lg` | `md`).
25
+ * @default full (pill)
26
+ */
27
+ rounded?: keyof typeof roundedClass;
28
+ /**
29
+ * When set, the label is the number capped at `max` with a "+" suffix
30
+ * (e.g. `max={99}` → `99+`). Ignores `children` for the visible text.
31
+ */
32
+ count?: number;
33
+ /** Upper bound before showing `{max}+`. Default `99`. */
34
+ max?: number;
35
+ };
36
+ declare const Badge: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLSpanElement> & {
37
+ /** @default secondary — same as Button default. */
38
+ variant?: keyof typeof variantClass;
39
+ /** @default md */
40
+ size?: keyof typeof sizeClass;
41
+ /**
42
+ * Corner radius — same options as `Button` (`full` | `lg` | `md`).
43
+ * @default full (pill)
44
+ */
45
+ rounded?: keyof typeof roundedClass;
46
+ /**
47
+ * When set, the label is the number capped at `max` with a "+" suffix
48
+ * (e.g. `max={99}` → `99+`). Ignores `children` for the visible text.
49
+ */
50
+ count?: number;
51
+ /** Upper bound before showing `{max}+`. Default `99`. */
52
+ max?: number;
53
+ } & React.RefAttributes<HTMLSpanElement>>;
54
+
55
+ export { Badge, type BadgeProps };
@@ -0,0 +1,64 @@
1
+ import * as React from 'react';
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ function cn(...inputs) {
7
+ return twMerge(clsx(inputs));
8
+ }
9
+ var variantClass = {
10
+ primary: "border border-transparent bg-primary text-background",
11
+ secondary: "border border-primary/10 bg-transparent text-primary"
12
+ };
13
+ var roundedClass = {
14
+ full: "rounded-full",
15
+ lg: "rounded-lg",
16
+ md: "rounded-md"
17
+ };
18
+ var sizeClass = {
19
+ md: "min-h-6 min-w-6 px-2 text-xs",
20
+ lg: "min-h-7 min-w-7 px-2.5 text-sm"
21
+ };
22
+ function formatCount(count, max) {
23
+ if (count > max) return `${max}+`;
24
+ return String(count);
25
+ }
26
+ var Badge = React.forwardRef(
27
+ ({
28
+ className,
29
+ variant = "secondary",
30
+ size = "md",
31
+ rounded = "full",
32
+ count,
33
+ max = 99,
34
+ children,
35
+ "aria-label": ariaLabelProp,
36
+ ...props
37
+ }, ref) => {
38
+ const content = count !== void 0 ? formatCount(count, max) : children;
39
+ return /* @__PURE__ */ jsx(
40
+ "span",
41
+ {
42
+ ref,
43
+ "data-slot": "badge",
44
+ "data-size": size,
45
+ "data-rounded": rounded,
46
+ "aria-label": ariaLabelProp,
47
+ className: cn(
48
+ "inline-flex shrink-0 items-center justify-center font-semibold leading-none tabular-nums",
49
+ roundedClass[rounded],
50
+ sizeClass[size],
51
+ variantClass[variant],
52
+ className
53
+ ),
54
+ ...props,
55
+ children: content
56
+ }
57
+ );
58
+ }
59
+ );
60
+ Badge.displayName = "Badge";
61
+
62
+ export { Badge };
63
+ //# sourceMappingURL=badge.js.map
64
+ //# sourceMappingURL=badge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/utils.ts","../../src/react/badge.tsx"],"names":[],"mappings":";;;;;AAGO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACEA,IAAM,YAAA,GAAe;AAAA,EACnB,OAAA,EAAS,sDAAA;AAAA,EACT,SAAA,EAAW;AACb,CAAA;AAGA,IAAM,YAAA,GAAe;AAAA,EACnB,IAAA,EAAM,cAAA;AAAA,EACN,EAAA,EAAI,YAAA;AAAA,EACJ,EAAA,EAAI;AACN,CAAA;AAEA,IAAM,SAAA,GAAY;AAAA,EAChB,EAAA,EAAI,8BAAA;AAAA,EACJ,EAAA,EAAI;AACN,CAAA;AAqBA,SAAS,WAAA,CAAY,OAAe,GAAA,EAAqB;AACvD,EAAA,IAAI,KAAA,GAAQ,GAAA,EAAK,OAAO,CAAA,EAAG,GAAG,CAAA,CAAA,CAAA;AAC9B,EAAA,OAAO,OAAO,KAAK,CAAA;AACrB;AAEA,IAAM,KAAA,GAAc,KAAA,CAAA,UAAA;AAAA,EAClB,CACE;AAAA,IACE,SAAA;AAAA,IACA,OAAA,GAAU,WAAA;AAAA,IACV,IAAA,GAAO,IAAA;AAAA,IACP,OAAA,GAAU,MAAA;AAAA,IACV,KAAA;AAAA,IACA,GAAA,GAAM,EAAA;AAAA,IACN,QAAA;AAAA,IACA,YAAA,EAAc,aAAA;AAAA,IACd,GAAG;AAAA,KAEL,GAAA,KACG;AACH,IAAA,MAAM,UAAU,KAAA,KAAU,MAAA,GAAY,WAAA,CAAY,KAAA,EAAO,GAAG,CAAA,GAAI,QAAA;AAEhE,IAAA,uBACE,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,WAAA,EAAU,OAAA;AAAA,QACV,WAAA,EAAW,IAAA;AAAA,QACX,cAAA,EAAc,OAAA;AAAA,QACd,YAAA,EAAY,aAAA;AAAA,QACZ,SAAA,EAAW,EAAA;AAAA,UACT,0FAAA;AAAA,UACA,aAAa,OAAO,CAAA;AAAA,UACpB,UAAU,IAAI,CAAA;AAAA,UACd,aAAa,OAAO,CAAA;AAAA,UACpB;AAAA,SACF;AAAA,QACC,GAAG,KAAA;AAAA,QAEH,QAAA,EAAA;AAAA;AAAA,KACH;AAAA,EAEJ;AACF;AAEA,KAAA,CAAM,WAAA,GAAc,OAAA","file":"badge.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/** Matches `button.tsx` primary / secondary — no separate outline variants. */\nconst variantClass = {\n primary: \"border border-transparent bg-primary text-background\",\n secondary: \"border border-primary/10 bg-transparent text-primary\",\n} as const;\n\n/** Same keys as `button.tsx` `roundedClass`. */\nconst roundedClass = {\n full: \"rounded-full\",\n lg: \"rounded-lg\",\n md: \"rounded-md\",\n} as const;\n\nconst sizeClass = {\n md: \"min-h-6 min-w-6 px-2 text-xs\",\n lg: \"min-h-7 min-w-7 px-2.5 text-sm\",\n} as const;\n\nexport type BadgeProps = React.HTMLAttributes<HTMLSpanElement> & {\n /** @default secondary — same as Button default. */\n variant?: keyof typeof variantClass;\n /** @default md */\n size?: keyof typeof sizeClass;\n /**\n * Corner radius — same options as `Button` (`full` | `lg` | `md`).\n * @default full (pill)\n */\n rounded?: keyof typeof roundedClass;\n /**\n * When set, the label is the number capped at `max` with a \"+\" suffix\n * (e.g. `max={99}` → `99+`). Ignores `children` for the visible text.\n */\n count?: number;\n /** Upper bound before showing `{max}+`. Default `99`. */\n max?: number;\n};\n\nfunction formatCount(count: number, max: number): string {\n if (count > max) return `${max}+`;\n return String(count);\n}\n\nconst Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(\n (\n {\n className,\n variant = \"secondary\",\n size = \"md\",\n rounded = \"full\",\n count,\n max = 99,\n children,\n \"aria-label\": ariaLabelProp,\n ...props\n },\n ref,\n ) => {\n const content = count !== undefined ? formatCount(count, max) : children;\n\n return (\n <span\n ref={ref}\n data-slot=\"badge\"\n data-size={size}\n data-rounded={rounded}\n aria-label={ariaLabelProp}\n className={cn(\n \"inline-flex shrink-0 items-center justify-center font-semibold leading-none tabular-nums\",\n roundedClass[rounded],\n sizeClass[size],\n variantClass[variant],\n className,\n )}\n {...props}\n >\n {content}\n </span>\n );\n },\n);\n\nBadge.displayName = \"Badge\";\n\nexport { Badge };\n"]}
@@ -0,0 +1,41 @@
1
+ import * as React from 'react';
2
+
3
+ declare const sizeClass: {
4
+ readonly xs: "h-8 min-h-8 gap-2 px-4 text-xs has-[>svg]:px-3 [&_svg:not([class*='size-'])]:size-3";
5
+ readonly default: "h-10 min-h-10 gap-2 px-5 text-sm has-[>svg]:px-4 [&_svg:not([class*='size-'])]:size-4";
6
+ readonly lg: "h-11 min-h-11 gap-2 px-6 text-base has-[>svg]:px-5 [&_svg:not([class*='size-'])]:size-5";
7
+ };
8
+ declare const roundedClass: {
9
+ readonly full: "rounded-full";
10
+ readonly lg: "rounded-lg";
11
+ readonly md: "rounded-md";
12
+ };
13
+ declare const variantClass: {
14
+ readonly primary: "bg-primary text-background border border-transparent hover:bg-primary/90 active:bg-primary/85";
15
+ readonly secondary: "bg-background text-primary border border-primary/10 hover:bg-primary/5";
16
+ readonly destructive: "bg-background text-red-500 border border-red-500/25 hover:bg-red-500/5";
17
+ readonly success: "bg-background text-green-500 border border-green-500/25 hover:bg-green-500/5";
18
+ };
19
+ type ButtonProps = React.ComponentProps<"button"> & {
20
+ /**
21
+ * Visual style: neutral (`secondary`, `primary`), or outline (`destructive`,
22
+ * `success`) using Tailwind `red-500` / `green-500` text and matching borders on
23
+ * `bg-background` (no solid fill).
24
+ */
25
+ variant?: keyof typeof variantClass;
26
+ /**
27
+ * Corner radius. Labeled buttons default to `full` (pill). `iconOnly` buttons
28
+ * default to `md` (square corners) unless you pass `rounded` explicitly.
29
+ */
30
+ rounded?: keyof typeof roundedClass;
31
+ /** Height and horizontal padding: `default` or `lg` only. */
32
+ size?: keyof typeof sizeClass;
33
+ /**
34
+ * Square icon-only control; same `size` presets (`default` | `lg`). Pair with
35
+ * `aria-label` (or `title`) when there is no visible text.
36
+ */
37
+ iconOnly?: boolean;
38
+ };
39
+ declare const Button: React.ForwardRefExoticComponent<Omit<ButtonProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
40
+
41
+ export { Button, type ButtonProps };
@@ -0,0 +1,69 @@
1
+ import * as React from 'react';
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ // src/react/button.tsx
7
+ function cn(...inputs) {
8
+ return twMerge(clsx(inputs));
9
+ }
10
+ var sizeClass = {
11
+ xs: "h-8 min-h-8 gap-2 px-4 text-xs has-[>svg]:px-3 [&_svg:not([class*='size-'])]:size-3",
12
+ default: "h-10 min-h-10 gap-2 px-5 text-sm has-[>svg]:px-4 [&_svg:not([class*='size-'])]:size-4",
13
+ lg: "h-11 min-h-11 gap-2 px-6 text-base has-[>svg]:px-5 [&_svg:not([class*='size-'])]:size-5"
14
+ };
15
+ var iconOnlySizeClass = {
16
+ xs: "size-8 min-h-8 min-w-8 gap-0 p-0 [&_svg:not([class*='size-'])]:size-3",
17
+ default: "size-10 min-h-10 min-w-10 gap-0 p-0 [&_svg:not([class*='size-'])]:size-4",
18
+ lg: "size-11 min-h-11 min-w-11 gap-0 p-0 [&_svg:not([class*='size-'])]:size-5"
19
+ };
20
+ var roundedClass = {
21
+ full: "rounded-full",
22
+ lg: "rounded-lg",
23
+ md: "rounded-md"
24
+ };
25
+ var variantClass = {
26
+ primary: "bg-primary text-background border border-transparent hover:bg-primary/90 active:bg-primary/85",
27
+ secondary: "bg-background text-primary border border-primary/10 hover:bg-primary/5",
28
+ destructive: "bg-background text-red-500 border border-red-500/25 hover:bg-red-500/5",
29
+ success: "bg-background text-green-500 border border-green-500/25 hover:bg-green-500/5"
30
+ };
31
+ var Button = React.forwardRef(
32
+ ({
33
+ className,
34
+ type = "button",
35
+ variant = "secondary",
36
+ rounded: roundedProp,
37
+ size = "default",
38
+ iconOnly = false,
39
+ disabled,
40
+ ...props
41
+ }, ref) => {
42
+ const rounded = roundedProp ?? (iconOnly ? "md" : "full");
43
+ return /* @__PURE__ */ jsx(
44
+ "button",
45
+ {
46
+ type,
47
+ disabled,
48
+ "data-slot": "button",
49
+ "data-icon-only": iconOnly ? "" : void 0,
50
+ className: cn(
51
+ "inline-flex shrink-0 cursor-pointer items-center justify-center whitespace-nowrap outline-none transition-[color,background-color,box-shadow,transform] duration-150 ease-out active:scale-[0.98] [&_svg]:pointer-events-none [&_svg]:shrink-0",
52
+ "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
53
+ "focus-visible:border-ring font-bold tracking-wide focus-visible:ring-ring/50 focus-visible:ring-[3px]",
54
+ iconOnly ? iconOnlySizeClass[size] : sizeClass[size],
55
+ roundedClass[rounded],
56
+ variantClass[variant],
57
+ className
58
+ ),
59
+ ref,
60
+ ...props
61
+ }
62
+ );
63
+ }
64
+ );
65
+ Button.displayName = "Button";
66
+
67
+ export { Button };
68
+ //# sourceMappingURL=button.js.map
69
+ //# sourceMappingURL=button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/utils.ts","../../src/react/button.tsx"],"names":[],"mappings":";;;;;;AAGO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACDA,IAAM,SAAA,GAAY;AAAA,EAChB,EAAA,EAAI,qFAAA;AAAA,EACJ,OAAA,EACE,uFAAA;AAAA,EACF,EAAA,EAAI;AACN,CAAA;AAGA,IAAM,iBAAA,GAAoB;AAAA,EACxB,EAAA,EAAI,uEAAA;AAAA,EACJ,OAAA,EACE,0EAAA;AAAA,EACF,EAAA,EAAI;AACN,CAAA;AAEA,IAAM,YAAA,GAAe;AAAA,EACnB,IAAA,EAAM,cAAA;AAAA,EACN,EAAA,EAAI,YAAA;AAAA,EACJ,EAAA,EAAI;AACN,CAAA;AAEA,IAAM,YAAA,GAAe;AAAA,EACnB,OAAA,EACE,+FAAA;AAAA,EACF,SAAA,EACE,wEAAA;AAAA,EACF,WAAA,EACE,wEAAA;AAAA,EACF,OAAA,EACE;AACJ,CAAA;AAuBA,IAAM,MAAA,GAAe,KAAA,CAAA,UAAA;AAAA,EACnB,CACE;AAAA,IACE,SAAA;AAAA,IACA,IAAA,GAAO,QAAA;AAAA,IACP,OAAA,GAAU,WAAA;AAAA,IACV,OAAA,EAAS,WAAA;AAAA,IACT,IAAA,GAAO,SAAA;AAAA,IACP,QAAA,GAAW,KAAA;AAAA,IACX,QAAA;AAAA,IACA,GAAG;AAAA,KAEL,GAAA,KACG;AACH,IAAA,MAAM,OAAA,GAAU,WAAA,KAAgB,QAAA,GAAW,IAAA,GAAO,MAAA,CAAA;AAElD,IAAA,uBACE,GAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,IAAA;AAAA,QACA,QAAA;AAAA,QACA,WAAA,EAAU,QAAA;AAAA,QACV,gBAAA,EAAgB,WAAW,EAAA,GAAK,MAAA;AAAA,QAChC,SAAA,EAAW,EAAA;AAAA,UACT,gPAAA;AAAA,UACA,8EAAA;AAAA,UACA,uGAAA;AAAA,UACA,QAAA,GAAW,iBAAA,CAAkB,IAAI,CAAA,GAAI,UAAU,IAAI,CAAA;AAAA,UACnD,aAAa,OAAO,CAAA;AAAA,UACpB,aAAa,OAAO,CAAA;AAAA,UACpB;AAAA,SACF;AAAA,QACA,GAAA;AAAA,QACC,GAAG;AAAA;AAAA,KACN;AAAA,EAEJ;AACF;AACA,MAAA,CAAO,WAAA,GAAc,QAAA","file":"button.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst sizeClass = {\n xs: \"h-8 min-h-8 gap-2 px-4 text-xs has-[>svg]:px-3 [&_svg:not([class*='size-'])]:size-3\",\n default:\n \"h-10 min-h-10 gap-2 px-5 text-sm has-[>svg]:px-4 [&_svg:not([class*='size-'])]:size-4\",\n lg: \"h-11 min-h-11 gap-2 px-6 text-base has-[>svg]:px-5 [&_svg:not([class*='size-'])]:size-5\",\n} as const;\n\n/** Square hit targets for `iconOnly` — same keys as `sizeClass` (`default` | `lg`). */\nconst iconOnlySizeClass = {\n xs: \"size-8 min-h-8 min-w-8 gap-0 p-0 [&_svg:not([class*='size-'])]:size-3\",\n default:\n \"size-10 min-h-10 min-w-10 gap-0 p-0 [&_svg:not([class*='size-'])]:size-4\",\n lg: \"size-11 min-h-11 min-w-11 gap-0 p-0 [&_svg:not([class*='size-'])]:size-5\",\n} as const;\n\nconst roundedClass = {\n full: \"rounded-full\",\n lg: \"rounded-lg\",\n md: \"rounded-md\",\n} as const;\n\nconst variantClass = {\n primary:\n \"bg-primary text-background border border-transparent hover:bg-primary/90 active:bg-primary/85\",\n secondary:\n \"bg-background text-primary border border-primary/10 hover:bg-primary/5\",\n destructive:\n \"bg-background text-red-500 border border-red-500/25 hover:bg-red-500/5\",\n success:\n \"bg-background text-green-500 border border-green-500/25 hover:bg-green-500/5\",\n} as const;\n\nexport type ButtonProps = React.ComponentProps<\"button\"> & {\n /**\n * Visual style: neutral (`secondary`, `primary`), or outline (`destructive`,\n * `success`) using Tailwind `red-500` / `green-500` text and matching borders on\n * `bg-background` (no solid fill).\n */\n variant?: keyof typeof variantClass;\n /**\n * Corner radius. Labeled buttons default to `full` (pill). `iconOnly` buttons\n * default to `md` (square corners) unless you pass `rounded` explicitly.\n */\n rounded?: keyof typeof roundedClass;\n /** Height and horizontal padding: `default` or `lg` only. */\n size?: keyof typeof sizeClass;\n /**\n * Square icon-only control; same `size` presets (`default` | `lg`). Pair with\n * `aria-label` (or `title`) when there is no visible text.\n */\n iconOnly?: boolean;\n};\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n type = \"button\",\n variant = \"secondary\",\n rounded: roundedProp,\n size = \"default\",\n iconOnly = false,\n disabled,\n ...props\n },\n ref,\n ) => {\n const rounded = roundedProp ?? (iconOnly ? \"md\" : \"full\");\n\n return (\n <button\n type={type}\n disabled={disabled}\n data-slot=\"button\"\n data-icon-only={iconOnly ? \"\" : undefined}\n className={cn(\n \"inline-flex shrink-0 cursor-pointer items-center justify-center whitespace-nowrap outline-none transition-[color,background-color,box-shadow,transform] duration-150 ease-out active:scale-[0.98] [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n \"disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50\",\n \"focus-visible:border-ring font-bold tracking-wide focus-visible:ring-ring/50 focus-visible:ring-[3px]\",\n iconOnly ? iconOnlySizeClass[size] : sizeClass[size],\n roundedClass[rounded],\n variantClass[variant],\n className,\n )}\n ref={ref}\n {...props}\n />\n );\n },\n);\nButton.displayName = \"Button\";\n\nexport { Button };\n"]}
@@ -1,20 +1,28 @@
1
- import React from 'react';
1
+ import React__default from 'react';
2
2
 
3
3
  interface DialogProps {
4
- children: React.ReactNode;
4
+ children: React__default.ReactNode;
5
5
  open?: boolean;
6
6
  onOpenChange?: (open: boolean) => void;
7
7
  }
8
- declare const Dialog: React.FC<DialogProps>;
9
- declare const DialogTrigger: React.FC<{
10
- children: React.ReactNode;
8
+ declare const Dialog: React__default.FC<DialogProps>;
9
+ declare const DialogTrigger: React__default.FC<{
10
+ children: React__default.ReactNode;
11
11
  asChild?: boolean;
12
12
  }>;
13
- declare const DialogClose: React.FC<{
14
- children: React.ReactNode;
13
+ declare const DialogClose: React__default.FC<{
14
+ children: React__default.ReactNode;
15
15
  asChild?: boolean;
16
16
  }>;
17
- interface DialogContentProps extends React.HTMLAttributes<HTMLDivElement> {
17
+ interface DialogCloseButtonProps extends React__default.ButtonHTMLAttributes<HTMLButtonElement> {
18
+ }
19
+ /**
20
+ * Default dismiss control for {@link DialogContent}. Includes absolute top-right
21
+ * placement; pass `className` to adjust or replace positioning.
22
+ * When `onClick` is omitted, closes via the surrounding `Dialog` context (`setOpen(false)`).
23
+ */
24
+ declare const DialogCloseButton: React__default.ForwardRefExoticComponent<DialogCloseButtonProps & React__default.RefAttributes<HTMLButtonElement>>;
25
+ interface DialogContentProps extends React__default.HTMLAttributes<HTMLDivElement> {
18
26
  size?: "sm" | "md" | "lg" | "xl" | "full";
19
27
  duration?: number;
20
28
  closeOnOverlayClick?: boolean;
@@ -27,14 +35,14 @@ interface DialogContentProps extends React.HTMLAttributes<HTMLDivElement> {
27
35
  /** Override slide distance (px); default follows `size` via `SLIDE_ENTRANCE_OFFSET_PX`. */
28
36
  slideEntranceOffsetPx?: number;
29
37
  }
30
- declare const DialogContent: React.FC<DialogContentProps>;
31
- declare const DialogHeader: React.FC<React.HTMLAttributes<HTMLDivElement> & {
38
+ declare const DialogContent: React__default.FC<DialogContentProps>;
39
+ declare const DialogHeader: React__default.FC<React__default.HTMLAttributes<HTMLDivElement> & {
32
40
  fixed?: boolean;
33
41
  }>;
34
- declare const DialogFooter: React.FC<React.HTMLAttributes<HTMLDivElement> & {
42
+ declare const DialogFooter: React__default.FC<React__default.HTMLAttributes<HTMLDivElement> & {
35
43
  fixed?: boolean;
36
44
  }>;
37
- declare const DialogTitle: React.FC<React.HTMLAttributes<HTMLHeadingElement>>;
38
- declare const DialogDescription: React.FC<React.HTMLAttributes<HTMLParagraphElement>>;
45
+ declare const DialogTitle: React__default.FC<React__default.HTMLAttributes<HTMLHeadingElement>>;
46
+ declare const DialogDescription: React__default.FC<React__default.HTMLAttributes<HTMLParagraphElement>>;
39
47
 
40
- export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger };
48
+ export { Dialog, DialogClose, DialogCloseButton, type DialogCloseButtonProps, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger };
@@ -1,9 +1,9 @@
1
- import React, { useState, useCallback, useEffect } from 'react';
1
+ import React, { useCallback, useState, useEffect } from 'react';
2
2
  import { clsx } from 'clsx';
3
3
  import { twMerge } from 'tailwind-merge';
4
4
  import { createPortal } from 'react-dom';
5
5
  import { X } from 'lucide-react';
6
- import { jsx, jsxs } from 'react/jsx-runtime';
6
+ import { jsxs, jsx } from 'react/jsx-runtime';
7
7
 
8
8
  function cn(...inputs) {
9
9
  return twMerge(clsx(inputs));
@@ -98,6 +98,36 @@ var DialogClose = ({ children, asChild }) => {
98
98
  }
99
99
  return /* @__PURE__ */ jsx("button", { type: "button", onClick: handleClick, children });
100
100
  };
101
+ var DialogCloseButton = React.forwardRef(({ className, type = "button", onClick, ...props }, ref) => {
102
+ const { setOpen } = useDialog();
103
+ const handleClick = useCallback(
104
+ (e) => {
105
+ onClick?.(e);
106
+ if (onClick == null) {
107
+ setOpen(false);
108
+ }
109
+ },
110
+ [onClick, setOpen]
111
+ );
112
+ return /* @__PURE__ */ jsxs(
113
+ "button",
114
+ {
115
+ ref,
116
+ type,
117
+ className: cn(
118
+ "z-100 flex size-12 cursor-pointer items-center justify-center rounded-full transition-all hover:bg-secondary-background active:scale-[0.96]",
119
+ className
120
+ ),
121
+ onClick: handleClick,
122
+ ...props,
123
+ children: [
124
+ /* @__PURE__ */ jsx(X, { className: "size-5.5" }),
125
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
126
+ ]
127
+ }
128
+ );
129
+ });
130
+ DialogCloseButton.displayName = "DialogCloseButton";
101
131
  var DialogContent = ({
102
132
  children,
103
133
  size = "md",
@@ -110,7 +140,6 @@ var DialogContent = ({
110
140
  ...props
111
141
  }) => {
112
142
  const { open, setOpen } = useDialog();
113
- const closeDialog = useCallback(() => setOpen(false), [setOpen]);
114
143
  const slideOffsetPx = slideEntranceOffsetPxProp ?? SLIDE_ENTRANCE_OFFSET_PX[size];
115
144
  const panelCloseMs = slideEntrance ? DIALOG_MOTION_MS : duration;
116
145
  const { shouldRender, isAnimating } = useDialogRenderLifecycle(
@@ -196,18 +225,10 @@ var DialogContent = ({
196
225
  },
197
226
  children: [
198
227
  children,
199
- showClose && /* @__PURE__ */ jsxs(
200
- "button",
228
+ showClose && /* @__PURE__ */ jsx(
229
+ DialogCloseButton,
201
230
  {
202
- type: "button",
203
- onClick: closeDialog,
204
- className: cn(
205
- "absolute flex size-12 cursor-pointer items-center justify-center rounded-full bg-background transition-all hover:bg-secondary-background active:scale-[0.96] top-4 right-4 md:top-5 md:right-5"
206
- ),
207
- children: [
208
- /* @__PURE__ */ jsx(X, { className: "size-5.5" }),
209
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
210
- ]
231
+ className: cn("absolute top-4 right-4 md:top-4 md:right-4")
211
232
  }
212
233
  )
213
234
  ]
@@ -261,6 +282,6 @@ var DialogDescription = ({ className, ...props }) => {
261
282
  return /* @__PURE__ */ jsx("p", { className: cn("text-muted-foreground text-sm", className), ...props });
262
283
  };
263
284
 
264
- export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger };
285
+ export { Dialog, DialogClose, DialogCloseButton, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger };
265
286
  //# sourceMappingURL=dialog.js.map
266
287
  //# sourceMappingURL=dialog.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/utils.ts","../../src/react/dialog.tsx"],"names":[],"mappings":"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","file":"dialog.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\nimport React, { useCallback, useEffect, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { createPortal } from \"react-dom\";\nimport { X } from \"lucide-react\";\n\n/** Used when `slideEntrance` is set — duration + entry/exit easing (see `slideMotionEasing`). */\nconst DIALOG_MOTION_MS = 200;\nconst DIALOG_ENTRY_MOTION_EASING = \"cubic-bezier(0.85, 0, 0.15, 1)\";\nconst DIALOG_EXIT_MOTION_EASING = \"cubic-bezier(0.85, 0, 1, 0.15)\";\n\ntype DialogSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n\n/**\n * Slide distance for `slideEntrance`, scaled to `size` (full keeps 120px like a\n * mobile sheet; smaller presets use shorter travel so the motion matches width).\n */\nconst SLIDE_ENTRANCE_OFFSET_PX: Record<DialogSize, number> = {\n sm: 16,\n md: 16,\n lg: 16,\n xl: 16,\n full: 120,\n};\n\n/** Same pattern as `useSheetRenderLifecycle` in sheet.tsx (portal + enter/exit timing). */\nfunction useDialogRenderLifecycle(open: boolean, panelCloseMs: number) {\n const [shouldRender, setShouldRender] = useState(open);\n const [isAnimating, setIsAnimating] = useState(false);\n\n useEffect(() => {\n if (open) {\n setShouldRender(true);\n } else {\n setIsAnimating(false);\n const timer = setTimeout(() => setShouldRender(false), panelCloseMs);\n return () => clearTimeout(timer);\n }\n }, [open, panelCloseMs]);\n\n useEffect(() => {\n if (!shouldRender || !open) return;\n\n let raf2 = 0;\n const raf1 = requestAnimationFrame(() => {\n raf2 = requestAnimationFrame(() => setIsAnimating(true));\n });\n return () => {\n cancelAnimationFrame(raf1);\n if (raf2) cancelAnimationFrame(raf2);\n };\n }, [shouldRender, open]);\n\n return { shouldRender, isAnimating };\n}\n\n//////////////////////////////////////////////// Context\n\nconst DialogContext = React.createContext<\n | {\n open: boolean;\n setOpen: (open: boolean) => void;\n }\n | undefined\n>(undefined);\n\nfunction useDialog() {\n const context = React.useContext(DialogContext);\n if (!context) {\n throw new Error(\"Dialog components must be used within a <Dialog />\");\n }\n return context;\n}\n\n//////////////////////////////////////////////// Main\n\ninterface DialogProps {\n children: React.ReactNode;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\nexport const Dialog: React.FC<DialogProps> = ({\n children,\n open: controlledOpen,\n onOpenChange,\n}) => {\n const [internalOpen, setInternalOpen] = useState(false);\n const isControlled = controlledOpen !== undefined;\n const open = isControlled ? controlledOpen : internalOpen;\n\n const setOpen = React.useCallback(\n (value: boolean) => {\n if (!isControlled) {\n setInternalOpen(value);\n }\n onOpenChange?.(value);\n },\n [isControlled, onOpenChange],\n );\n\n return (\n <DialogContext.Provider value={{ open, setOpen }}>\n {children}\n </DialogContext.Provider>\n );\n};\n\nexport const DialogTrigger: React.FC<{\n children: React.ReactNode;\n asChild?: boolean;\n}> = ({ children, asChild }) => {\n const { setOpen } = useDialog();\n\n const handleClick = () => setOpen(true);\n\n if (asChild && React.isValidElement(children)) {\n const child = children as React.ReactElement<{\n onClick?: React.MouseEventHandler;\n }>;\n return React.cloneElement(child, {\n onClick: (e: React.MouseEvent) => {\n child.props.onClick?.(e);\n handleClick();\n },\n });\n }\n\n return (\n <button type=\"button\" onClick={handleClick}>\n {children}\n </button>\n );\n};\n\nexport const DialogClose: React.FC<{\n children: React.ReactNode;\n asChild?: boolean;\n}> = ({ children, asChild }) => {\n const { setOpen } = useDialog();\n\n const handleClick = () => setOpen(false);\n\n if (asChild && React.isValidElement(children)) {\n const child = children as React.ReactElement<{\n onClick?: React.MouseEventHandler;\n }>;\n return React.cloneElement(child, {\n onClick: (e: React.MouseEvent) => {\n child.props.onClick?.(e);\n handleClick();\n },\n });\n }\n\n return (\n <button type=\"button\" onClick={handleClick}>\n {children}\n </button>\n );\n};\n\n//////////////////////////////////////////////// Content\n\ninterface DialogContentProps extends React.HTMLAttributes<HTMLDivElement> {\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n duration?: number;\n closeOnOverlayClick?: boolean;\n showClose?: boolean;\n /**\n * When `true`, panel uses translateY + opacity with fixed timing and separate\n * entry/exit curves (works with any `size`). Default `false` uses scale + fade.\n */\n slideEntrance?: boolean;\n /** Override slide distance (px); default follows `size` via `SLIDE_ENTRANCE_OFFSET_PX`. */\n slideEntranceOffsetPx?: number;\n}\n\nexport const DialogContent: React.FC<DialogContentProps> = ({\n children,\n size = \"md\",\n className,\n duration = 200,\n closeOnOverlayClick = true,\n showClose = true,\n slideEntrance = false,\n slideEntranceOffsetPx: slideEntranceOffsetPxProp,\n ...props\n}) => {\n const { open, setOpen } = useDialog();\n const closeDialog = useCallback(() => setOpen(false), [setOpen]);\n\n const slideOffsetPx =\n slideEntranceOffsetPxProp ?? SLIDE_ENTRANCE_OFFSET_PX[size];\n\n const panelCloseMs = slideEntrance ? DIALOG_MOTION_MS : duration;\n const { shouldRender, isAnimating } = useDialogRenderLifecycle(\n open,\n panelCloseMs,\n );\n\n /** Slide entrance: entry easing while opening, exit easing while closing. */\n const slideMotionEasing = open\n ? DIALOG_ENTRY_MOTION_EASING\n : DIALOG_EXIT_MOTION_EASING;\n\n useEffect(() => {\n const handleEsc = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setOpen(false);\n };\n if (open) {\n if (typeof document !== \"undefined\") {\n window.addEventListener(\"keydown\", handleEsc);\n document.body.style.overflow = \"hidden\";\n }\n }\n return () => {\n if (typeof document !== \"undefined\") {\n window.removeEventListener(\"keydown\", handleEsc);\n document.body.style.overflow = \"\";\n }\n };\n }, [open, setOpen]);\n\n if (!shouldRender) return null;\n\n const sizeClasses: Record<\"sm\" | \"md\" | \"lg\" | \"xl\", string> = {\n sm: \"sm:max-w-sm\",\n md: \"sm:max-w-md\",\n lg: \"sm:max-w-lg\",\n xl: \"sm:max-w-xl\",\n };\n\n return createPortal(\n <div\n className={cn(\n \"fixed inset-0 z-50 flex\",\n size === \"full\"\n ? \"h-dvh w-full flex-col p-0\"\n : \"items-center justify-center p-4\",\n )}\n >\n {/* Overlay */}\n <div\n className={cn(\n \"fixed inset-0 bg-black/40 dark:bg-black/60\",\n !slideEntrance && \"transition-opacity ease-in-out\",\n isAnimating ? \"opacity-100\" : \"opacity-0\",\n )}\n style={{\n transitionDuration: `${panelCloseMs}ms`,\n ...(slideEntrance\n ? {\n transitionProperty: \"opacity\",\n transitionTimingFunction: slideMotionEasing,\n }\n : {}),\n }}\n onClick={() => closeOnOverlayClick && setOpen(false)}\n />\n\n {/* Panel */}\n <div\n {...props}\n className={cn(\n \"bg-background relative z-50 w-full overflow-hidden shadow-2xl\",\n size === \"full\"\n ? \"flex min-h-0 flex-1 flex-col max-w-none rounded-none\"\n : cn(\"border-primary/10 rounded-2xl border\", sizeClasses[size]),\n !slideEntrance &&\n cn(\n \"transition-all ease-in-out\",\n isAnimating ? \"scale-100 opacity-100\" : \"scale-95 opacity-0\",\n ),\n className,\n )}\n style={{\n ...props.style,\n ...(slideEntrance\n ? {\n transform: isAnimating\n ? \"translateY(0)\"\n : `translateY(${slideOffsetPx}px)`,\n opacity: isAnimating ? 1 : 0,\n transitionProperty: \"transform, opacity\",\n transitionDuration: `${DIALOG_MOTION_MS}ms`,\n transitionTimingFunction: slideMotionEasing,\n }\n : {\n transitionDuration: `${duration}ms`,\n }),\n }}\n >\n {children}\n\n {showClose && (\n <button\n type=\"button\"\n onClick={closeDialog}\n className={cn(\n \"absolute flex size-12 cursor-pointer items-center justify-center rounded-full bg-background transition-all hover:bg-secondary-background active:scale-[0.96] top-4 right-4 md:top-5 md:right-5\",\n )}\n >\n <X className=\"size-5.5\" />\n <span className=\"sr-only\">Close</span>\n </button>\n )}\n </div>\n </div>,\n document.body,\n );\n};\n\nexport const DialogHeader: React.FC<\n React.HTMLAttributes<HTMLDivElement> & { fixed?: boolean }\n> = ({ className, fixed, ...props }) => {\n return (\n <div\n className={cn(\n \"flex flex-col space-y-1.5 p-6 text-center sm:text-left\",\n fixed && \"bg-background border-primary/10 sticky top-0 z-10 border-b\",\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const DialogFooter: React.FC<\n React.HTMLAttributes<HTMLDivElement> & { fixed?: boolean }\n> = ({ className, fixed, ...props }) => {\n return (\n <div\n className={cn(\n \"flex flex-col-reverse p-6 sm:flex-row sm:justify-end sm:space-x-2\",\n fixed &&\n \"bg-background border-primary/10 sticky bottom-0 z-10 border-t\",\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const DialogTitle: React.FC<\n React.HTMLAttributes<HTMLHeadingElement>\n> = ({ className, ...props }) => {\n return (\n <h3\n className={cn(\n \"text-primary text-xl leading-none font-semibold\",\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const DialogDescription: React.FC<\n React.HTMLAttributes<HTMLParagraphElement>\n> = ({ className, ...props }) => {\n return (\n <p className={cn(\"text-muted-foreground text-sm\", className)} {...props} />\n );\n};\n"]}
1
+ {"version":3,"sources":["../../src/lib/utils.ts","../../src/react/dialog.tsx"],"names":[],"mappings":"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","file":"dialog.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\nimport React, { useCallback, useEffect, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { createPortal } from \"react-dom\";\nimport { X } from \"lucide-react\";\n\n/** Used when `slideEntrance` is set — duration + entry/exit easing (see `slideMotionEasing`). */\nconst DIALOG_MOTION_MS = 200;\nconst DIALOG_ENTRY_MOTION_EASING = \"cubic-bezier(0.85, 0, 0.15, 1)\";\nconst DIALOG_EXIT_MOTION_EASING = \"cubic-bezier(0.85, 0, 1, 0.15)\";\n\ntype DialogSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n\n/**\n * Slide distance for `slideEntrance`, scaled to `size` (full keeps 120px like a\n * mobile sheet; smaller presets use shorter travel so the motion matches width).\n */\nconst SLIDE_ENTRANCE_OFFSET_PX: Record<DialogSize, number> = {\n sm: 16,\n md: 16,\n lg: 16,\n xl: 16,\n full: 120,\n};\n\n/** Same pattern as `useSheetRenderLifecycle` in sheet.tsx (portal + enter/exit timing). */\nfunction useDialogRenderLifecycle(open: boolean, panelCloseMs: number) {\n const [shouldRender, setShouldRender] = useState(open);\n const [isAnimating, setIsAnimating] = useState(false);\n\n useEffect(() => {\n if (open) {\n setShouldRender(true);\n } else {\n setIsAnimating(false);\n const timer = setTimeout(() => setShouldRender(false), panelCloseMs);\n return () => clearTimeout(timer);\n }\n }, [open, panelCloseMs]);\n\n useEffect(() => {\n if (!shouldRender || !open) return;\n\n let raf2 = 0;\n const raf1 = requestAnimationFrame(() => {\n raf2 = requestAnimationFrame(() => setIsAnimating(true));\n });\n return () => {\n cancelAnimationFrame(raf1);\n if (raf2) cancelAnimationFrame(raf2);\n };\n }, [shouldRender, open]);\n\n return { shouldRender, isAnimating };\n}\n\n//////////////////////////////////////////////// Context\n\nconst DialogContext = React.createContext<\n | {\n open: boolean;\n setOpen: (open: boolean) => void;\n }\n | undefined\n>(undefined);\n\nfunction useDialog() {\n const context = React.useContext(DialogContext);\n if (!context) {\n throw new Error(\"Dialog components must be used within a <Dialog />\");\n }\n return context;\n}\n\n//////////////////////////////////////////////// Main\n\ninterface DialogProps {\n children: React.ReactNode;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\nexport const Dialog: React.FC<DialogProps> = ({\n children,\n open: controlledOpen,\n onOpenChange,\n}) => {\n const [internalOpen, setInternalOpen] = useState(false);\n const isControlled = controlledOpen !== undefined;\n const open = isControlled ? controlledOpen : internalOpen;\n\n const setOpen = React.useCallback(\n (value: boolean) => {\n if (!isControlled) {\n setInternalOpen(value);\n }\n onOpenChange?.(value);\n },\n [isControlled, onOpenChange],\n );\n\n return (\n <DialogContext.Provider value={{ open, setOpen }}>\n {children}\n </DialogContext.Provider>\n );\n};\n\nexport const DialogTrigger: React.FC<{\n children: React.ReactNode;\n asChild?: boolean;\n}> = ({ children, asChild }) => {\n const { setOpen } = useDialog();\n\n const handleClick = () => setOpen(true);\n\n if (asChild && React.isValidElement(children)) {\n const child = children as React.ReactElement<{\n onClick?: React.MouseEventHandler;\n }>;\n return React.cloneElement(child, {\n onClick: (e: React.MouseEvent) => {\n child.props.onClick?.(e);\n handleClick();\n },\n });\n }\n\n return (\n <button type=\"button\" onClick={handleClick}>\n {children}\n </button>\n );\n};\n\nexport const DialogClose: React.FC<{\n children: React.ReactNode;\n asChild?: boolean;\n}> = ({ children, asChild }) => {\n const { setOpen } = useDialog();\n\n const handleClick = () => setOpen(false);\n\n if (asChild && React.isValidElement(children)) {\n const child = children as React.ReactElement<{\n onClick?: React.MouseEventHandler;\n }>;\n return React.cloneElement(child, {\n onClick: (e: React.MouseEvent) => {\n child.props.onClick?.(e);\n handleClick();\n },\n });\n }\n\n return (\n <button type=\"button\" onClick={handleClick}>\n {children}\n </button>\n );\n};\n\n//////////////////////////////////////////////// Close\n\nexport interface DialogCloseButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {}\n\n/**\n * Default dismiss control for {@link DialogContent}. Includes absolute top-right\n * placement; pass `className` to adjust or replace positioning.\n * When `onClick` is omitted, closes via the surrounding `Dialog` context (`setOpen(false)`).\n */\nexport const DialogCloseButton = React.forwardRef<\n HTMLButtonElement,\n DialogCloseButtonProps\n>(({ className, type = \"button\", onClick, ...props }, ref) => {\n const { setOpen } = useDialog();\n const handleClick = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n if (onClick == null) {\n setOpen(false);\n }\n },\n [onClick, setOpen],\n );\n\n return (\n <button\n ref={ref}\n type={type}\n className={cn(\n \"z-100 flex size-12 cursor-pointer items-center justify-center rounded-full transition-all hover:bg-secondary-background active:scale-[0.96]\",\n className,\n )}\n onClick={handleClick}\n {...props}\n >\n <X className=\"size-5.5\" />\n <span className=\"sr-only\">Close</span>\n </button>\n );\n});\nDialogCloseButton.displayName = \"DialogCloseButton\";\n\n//////////////////////////////////////////////// Content\n\ninterface DialogContentProps extends React.HTMLAttributes<HTMLDivElement> {\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n duration?: number;\n closeOnOverlayClick?: boolean;\n showClose?: boolean;\n /**\n * When `true`, panel uses translateY + opacity with fixed timing and separate\n * entry/exit curves (works with any `size`). Default `false` uses scale + fade.\n */\n slideEntrance?: boolean;\n /** Override slide distance (px); default follows `size` via `SLIDE_ENTRANCE_OFFSET_PX`. */\n slideEntranceOffsetPx?: number;\n}\n\nexport const DialogContent: React.FC<DialogContentProps> = ({\n children,\n size = \"md\",\n className,\n duration = 200,\n closeOnOverlayClick = true,\n showClose = true,\n slideEntrance = false,\n slideEntranceOffsetPx: slideEntranceOffsetPxProp,\n ...props\n}) => {\n const { open, setOpen } = useDialog();\n\n const slideOffsetPx =\n slideEntranceOffsetPxProp ?? SLIDE_ENTRANCE_OFFSET_PX[size];\n\n const panelCloseMs = slideEntrance ? DIALOG_MOTION_MS : duration;\n const { shouldRender, isAnimating } = useDialogRenderLifecycle(\n open,\n panelCloseMs,\n );\n\n /** Slide entrance: entry easing while opening, exit easing while closing. */\n const slideMotionEasing = open\n ? DIALOG_ENTRY_MOTION_EASING\n : DIALOG_EXIT_MOTION_EASING;\n\n useEffect(() => {\n const handleEsc = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setOpen(false);\n };\n if (open) {\n if (typeof document !== \"undefined\") {\n window.addEventListener(\"keydown\", handleEsc);\n document.body.style.overflow = \"hidden\";\n }\n }\n return () => {\n if (typeof document !== \"undefined\") {\n window.removeEventListener(\"keydown\", handleEsc);\n document.body.style.overflow = \"\";\n }\n };\n }, [open, setOpen]);\n\n if (!shouldRender) return null;\n\n const sizeClasses: Record<\"sm\" | \"md\" | \"lg\" | \"xl\", string> = {\n sm: \"sm:max-w-sm\",\n md: \"sm:max-w-md\",\n lg: \"sm:max-w-lg\",\n xl: \"sm:max-w-xl\",\n };\n\n return createPortal(\n <div\n className={cn(\n \"fixed inset-0 z-50 flex\",\n size === \"full\"\n ? \"h-dvh w-full flex-col p-0\"\n : \"items-center justify-center p-4\",\n )}\n >\n {/* Overlay */}\n <div\n className={cn(\n \"fixed inset-0 bg-black/40 dark:bg-black/60\",\n !slideEntrance && \"transition-opacity ease-in-out\",\n isAnimating ? \"opacity-100\" : \"opacity-0\",\n )}\n style={{\n transitionDuration: `${panelCloseMs}ms`,\n ...(slideEntrance\n ? {\n transitionProperty: \"opacity\",\n transitionTimingFunction: slideMotionEasing,\n }\n : {}),\n }}\n onClick={() => closeOnOverlayClick && setOpen(false)}\n />\n\n {/* Panel */}\n <div\n {...props}\n className={cn(\n \"bg-background relative z-50 w-full overflow-hidden shadow-2xl\",\n size === \"full\"\n ? \"flex min-h-0 flex-1 flex-col max-w-none rounded-none\"\n : cn(\"border-primary/10 rounded-2xl border\", sizeClasses[size]),\n !slideEntrance &&\n cn(\n \"transition-all ease-in-out\",\n isAnimating ? \"scale-100 opacity-100\" : \"scale-95 opacity-0\",\n ),\n className,\n )}\n style={{\n ...props.style,\n ...(slideEntrance\n ? {\n transform: isAnimating\n ? \"translateY(0)\"\n : `translateY(${slideOffsetPx}px)`,\n opacity: isAnimating ? 1 : 0,\n transitionProperty: \"transform, opacity\",\n transitionDuration: `${DIALOG_MOTION_MS}ms`,\n transitionTimingFunction: slideMotionEasing,\n }\n : {\n transitionDuration: `${duration}ms`,\n }),\n }}\n >\n {children}\n\n {showClose && (\n <DialogCloseButton\n className={cn(\"absolute top-4 right-4 md:top-4 md:right-4\")}\n />\n )}\n </div>\n </div>,\n document.body,\n );\n};\n\nexport const DialogHeader: React.FC<\n React.HTMLAttributes<HTMLDivElement> & { fixed?: boolean }\n> = ({ className, fixed, ...props }) => {\n return (\n <div\n className={cn(\n \"flex flex-col space-y-1.5 p-6 text-center sm:text-left\",\n fixed && \"bg-background border-primary/10 sticky top-0 z-10 border-b\",\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const DialogFooter: React.FC<\n React.HTMLAttributes<HTMLDivElement> & { fixed?: boolean }\n> = ({ className, fixed, ...props }) => {\n return (\n <div\n className={cn(\n \"flex flex-col-reverse p-6 sm:flex-row sm:justify-end sm:space-x-2\",\n fixed &&\n \"bg-background border-primary/10 sticky bottom-0 z-10 border-t\",\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const DialogTitle: React.FC<\n React.HTMLAttributes<HTMLHeadingElement>\n> = ({ className, ...props }) => {\n return (\n <h3\n className={cn(\n \"text-primary text-xl leading-none font-semibold\",\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const DialogDescription: React.FC<\n React.HTMLAttributes<HTMLParagraphElement>\n> = ({ className, ...props }) => {\n return (\n <p className={cn(\"text-muted-foreground text-sm\", className)} {...props} />\n );\n};\n"]}
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+
3
+ declare const sizeClass: {
4
+ readonly sm: "h-9 min-h-9 px-3 text-sm";
5
+ readonly default: "h-10 min-h-10 px-3.5 text-sm";
6
+ readonly lg: "h-11 min-h-11 px-4 text-base file:h-8 file:text-base";
7
+ };
8
+ type InputProps = Omit<React.ComponentProps<"input">, "size"> & {
9
+ /**
10
+ * Visual height and padding. This replaces the native HTML `size` attribute
11
+ * (character width); that numeric attribute is not supported on this
12
+ * component.
13
+ *
14
+ * Defaults to `"lg"` (large). Use `"default"` for a denser medium height.
15
+ */
16
+ size?: keyof typeof sizeClass;
17
+ };
18
+ declare const Input: React.ForwardRefExoticComponent<Omit<InputProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
19
+
20
+ export { Input, type InputProps };
@@ -0,0 +1,42 @@
1
+ import * as React from 'react';
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ // src/react/input.tsx
7
+ function cn(...inputs) {
8
+ return twMerge(clsx(inputs));
9
+ }
10
+ var sizeClass = {
11
+ sm: "h-9 min-h-9 px-3 text-sm",
12
+ default: "h-10 min-h-10 px-3.5 text-sm",
13
+ lg: "h-11 min-h-11 px-4 text-base file:h-8 file:text-base"
14
+ };
15
+ var Input = React.forwardRef(
16
+ ({ className, type = "text", size = "lg", disabled, ...props }, ref) => {
17
+ return /* @__PURE__ */ jsx(
18
+ "input",
19
+ {
20
+ type,
21
+ disabled,
22
+ "data-slot": "input",
23
+ className: cn(
24
+ "border-input bg-background text-foreground placeholder:text-primary/30 selection:bg-primary selection:text-primary-foreground flex w-full min-w-0 rounded-md border outline-none transition-[color,box-shadow]",
25
+ "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
26
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
27
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
28
+ "file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground",
29
+ sizeClass[size],
30
+ className
31
+ ),
32
+ ref,
33
+ ...props
34
+ }
35
+ );
36
+ }
37
+ );
38
+ Input.displayName = "Input";
39
+
40
+ export { Input };
41
+ //# sourceMappingURL=input.js.map
42
+ //# sourceMappingURL=input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/utils.ts","../../src/react/input.tsx"],"names":[],"mappings":";;;;;;AAGO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACDA,IAAM,SAAA,GAAY;AAAA,EAChB,EAAA,EAAI,0BAAA;AAAA,EACJ,OAAA,EAAS,8BAAA;AAAA,EACT,EAAA,EAAI;AACN,CAAA;AAaA,IAAM,KAAA,GAAc,KAAA,CAAA,UAAA;AAAA,EAClB,CAAC,EAAE,SAAA,EAAW,IAAA,GAAO,MAAA,EAAQ,IAAA,GAAO,IAAA,EAAM,QAAA,EAAU,GAAG,KAAA,EAAM,EAAG,GAAA,KAAQ;AACtE,IAAA,uBACE,GAAA;AAAA,MAAC,OAAA;AAAA,MAAA;AAAA,QACC,IAAA;AAAA,QACA,QAAA;AAAA,QACA,WAAA,EAAU,OAAA;AAAA,QACV,SAAA,EAAW,EAAA;AAAA,UACT,gNAAA;AAAA,UACA,8EAAA;AAAA,UACA,+EAAA;AAAA,UACA,wGAAA;AAAA,UACA,gHAAA;AAAA,UACA,UAAU,IAAI,CAAA;AAAA,UACd;AAAA,SACF;AAAA,QACA,GAAA;AAAA,QACC,GAAG;AAAA;AAAA,KACN;AAAA,EAEJ;AACF;AACA,KAAA,CAAM,WAAA,GAAc,OAAA","file":"input.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst sizeClass = {\n sm: \"h-9 min-h-9 px-3 text-sm\",\n default: \"h-10 min-h-10 px-3.5 text-sm\",\n lg: \"h-11 min-h-11 px-4 text-base file:h-8 file:text-base\",\n} as const;\n\nexport type InputProps = Omit<React.ComponentProps<\"input\">, \"size\"> & {\n /**\n * Visual height and padding. This replaces the native HTML `size` attribute\n * (character width); that numeric attribute is not supported on this\n * component.\n *\n * Defaults to `\"lg\"` (large). Use `\"default\"` for a denser medium height.\n */\n size?: keyof typeof sizeClass;\n};\n\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n ({ className, type = \"text\", size = \"lg\", disabled, ...props }, ref) => {\n return (\n <input\n type={type}\n disabled={disabled}\n data-slot=\"input\"\n className={cn(\n \"border-input bg-background text-foreground placeholder:text-primary/30 selection:bg-primary selection:text-primary-foreground flex w-full min-w-0 rounded-md border outline-none transition-[color,box-shadow]\",\n \"disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50\",\n \"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]\",\n \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40\",\n \"file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground\",\n sizeClass[size],\n className,\n )}\n ref={ref}\n {...props}\n />\n );\n },\n);\nInput.displayName = \"Input\";\n\nexport { Input };\n"]}
@@ -0,0 +1,28 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ type ScrollHeaderRevealAt = "start" | "center" | "end";
4
+ interface ScrollHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
5
+ children: ReactNode;
6
+ /**
7
+ * Which part of `ScrollHeaderTop` must leave the scrollport before the sticky bar shows.
8
+ * `start` — top edge (earliest). `center` — vertical midpoint. `end` — bottom edge (latest).
9
+ * If omitted: defaults to `center` when `slideIn` is true, and `start` when `slideIn` is false (in-flow bar).
10
+ */
11
+ revealAt?: ScrollHeaderRevealAt;
12
+ /**
13
+ * When `true` (default), `ScrollHeaderSticky` overlays without layout height and slides in with opacity.
14
+ * When `false`, the sticky bar stays in document flow (takes vertical space); use `onRevealChange` and
15
+ * your own styles for any fade or toolbar behavior.
16
+ */
17
+ slideIn?: boolean;
18
+ /**
19
+ * Called when the sticky header reveal state changes after scroll.
20
+ * `true` once the top section has crossed the reveal threshold (same moment as `data-scrolled="true"`).
21
+ */
22
+ onRevealChange?: (revealed: boolean) => void;
23
+ }
24
+ declare const ScrollHeader: React.FC<ScrollHeaderProps>;
25
+ declare const ScrollHeaderTop: React.FC<React.HTMLAttributes<HTMLDivElement>>;
26
+ declare const ScrollHeaderSticky: React.FC<React.HTMLAttributes<HTMLDivElement>>;
27
+
28
+ export { ScrollHeader, type ScrollHeaderRevealAt, ScrollHeaderSticky, ScrollHeaderTop };