@ngrok/mantle 0.66.16 → 0.66.17

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.
@@ -147,9 +147,9 @@ declare const AlertDialog: {
147
147
  * ```
148
148
  */
149
149
  readonly Action: react.ForwardRefExoticComponent<(Omit<react.ClassAttributes<HTMLButtonElement> & react.ButtonHTMLAttributes<HTMLButtonElement> & Partial<DeepNonNullable<class_variance_authority0.VariantProps<(props?: ({
150
- appearance?: "link" | "ghost" | "outlined" | "filled" | null | undefined;
150
+ appearance?: "filled" | "link" | "ghost" | "outlined" | null | undefined;
151
151
  isLoading?: boolean | null | undefined;
152
- priority?: "danger" | "neutral" | "default" | null | undefined;
152
+ priority?: "default" | "danger" | "neutral" | null | undefined;
153
153
  } & class_variance_authority_types0.ClassProp) | undefined) => string>>> & {
154
154
  icon?: ReactNode;
155
155
  iconPlacement?: "start" | "end";
@@ -157,9 +157,9 @@ declare const AlertDialog: {
157
157
  asChild: true;
158
158
  type?: ComponentProps<"button">["type"];
159
159
  }, "ref"> | Omit<react.ClassAttributes<HTMLButtonElement> & react.ButtonHTMLAttributes<HTMLButtonElement> & Partial<DeepNonNullable<class_variance_authority0.VariantProps<(props?: ({
160
- appearance?: "link" | "ghost" | "outlined" | "filled" | null | undefined;
160
+ appearance?: "filled" | "link" | "ghost" | "outlined" | null | undefined;
161
161
  isLoading?: boolean | null | undefined;
162
- priority?: "danger" | "neutral" | "default" | null | undefined;
162
+ priority?: "default" | "danger" | "neutral" | null | undefined;
163
163
  } & class_variance_authority_types0.ClassProp) | undefined) => string>>> & {
164
164
  icon?: ReactNode;
165
165
  iconPlacement?: "start" | "end";
@@ -204,9 +204,9 @@ declare const AlertDialog: {
204
204
  * ```
205
205
  */
206
206
  readonly Cancel: react.ForwardRefExoticComponent<(Omit<react.ClassAttributes<HTMLButtonElement> & react.ButtonHTMLAttributes<HTMLButtonElement> & Partial<DeepNonNullable<class_variance_authority0.VariantProps<(props?: ({
207
- appearance?: "link" | "ghost" | "outlined" | "filled" | null | undefined;
207
+ appearance?: "filled" | "link" | "ghost" | "outlined" | null | undefined;
208
208
  isLoading?: boolean | null | undefined;
209
- priority?: "danger" | "neutral" | "default" | null | undefined;
209
+ priority?: "default" | "danger" | "neutral" | null | undefined;
210
210
  } & class_variance_authority_types0.ClassProp) | undefined) => string>>> & {
211
211
  icon?: ReactNode;
212
212
  iconPlacement?: "start" | "end";
@@ -214,9 +214,9 @@ declare const AlertDialog: {
214
214
  asChild: true;
215
215
  type?: ComponentProps<"button">["type"];
216
216
  }, "ref"> | Omit<react.ClassAttributes<HTMLButtonElement> & react.ButtonHTMLAttributes<HTMLButtonElement> & Partial<DeepNonNullable<class_variance_authority0.VariantProps<(props?: ({
217
- appearance?: "link" | "ghost" | "outlined" | "filled" | null | undefined;
217
+ appearance?: "filled" | "link" | "ghost" | "outlined" | null | undefined;
218
218
  isLoading?: boolean | null | undefined;
219
- priority?: "danger" | "neutral" | "default" | null | undefined;
219
+ priority?: "default" | "danger" | "neutral" | null | undefined;
220
220
  } & class_variance_authority_types0.ClassProp) | undefined) => string>>> & {
221
221
  icon?: ReactNode;
222
222
  iconPlacement?: "start" | "end";
@@ -7,9 +7,9 @@ import * as class_variance_authority_types0 from "class-variance-authority/types
7
7
 
8
8
  //#region src/components/button/button.d.ts
9
9
  declare const buttonVariants: (props?: ({
10
- appearance?: "link" | "ghost" | "outlined" | "filled" | null | undefined;
10
+ appearance?: "filled" | "link" | "ghost" | "outlined" | null | undefined;
11
11
  isLoading?: boolean | null | undefined;
12
- priority?: "danger" | "neutral" | "default" | null | undefined;
12
+ priority?: "default" | "danger" | "neutral" | null | undefined;
13
13
  } & class_variance_authority_types0.ClassProp) | undefined) => string;
14
14
  type ButtonVariants = VariantProps$1<typeof buttonVariants>;
15
15
  /**
@@ -91,9 +91,9 @@ type ButtonProps = ComponentProps<"button"> & ButtonVariants & {
91
91
  * ```
92
92
  */
93
93
  declare const Button: react.ForwardRefExoticComponent<(Omit<react.ClassAttributes<HTMLButtonElement> & react.ButtonHTMLAttributes<HTMLButtonElement> & Partial<DeepNonNullable<class_variance_authority0.VariantProps<(props?: ({
94
- appearance?: "link" | "ghost" | "outlined" | "filled" | null | undefined;
94
+ appearance?: "filled" | "link" | "ghost" | "outlined" | null | undefined;
95
95
  isLoading?: boolean | null | undefined;
96
- priority?: "danger" | "neutral" | "default" | null | undefined;
96
+ priority?: "default" | "danger" | "neutral" | null | undefined;
97
97
  } & class_variance_authority_types0.ClassProp) | undefined) => string>>> & {
98
98
  /**
99
99
  * An icon to render inside the button. If the `state` is `"pending"`, then
@@ -137,9 +137,9 @@ declare const Button: react.ForwardRefExoticComponent<(Omit<react.ClassAttribute
137
137
  */
138
138
  type?: ComponentProps<"button">["type"];
139
139
  }, "ref"> | Omit<react.ClassAttributes<HTMLButtonElement> & react.ButtonHTMLAttributes<HTMLButtonElement> & Partial<DeepNonNullable<class_variance_authority0.VariantProps<(props?: ({
140
- appearance?: "link" | "ghost" | "outlined" | "filled" | null | undefined;
140
+ appearance?: "filled" | "link" | "ghost" | "outlined" | null | undefined;
141
141
  isLoading?: boolean | null | undefined;
142
- priority?: "danger" | "neutral" | "default" | null | undefined;
142
+ priority?: "default" | "danger" | "neutral" | null | undefined;
143
143
  } & class_variance_authority_types0.ClassProp) | undefined) => string>>> & {
144
144
  /**
145
145
  * An icon to render inside the button. If the `state` is `"pending"`, then
@@ -172,4 +172,4 @@ declare const Button: react.ForwardRefExoticComponent<(Omit<react.ClassAttribute
172
172
  }, "ref">) & react.RefAttributes<HTMLButtonElement>>;
173
173
  //#endregion
174
174
  export { ButtonProps as n, Button as t };
175
- //# sourceMappingURL=button-B6StZJsz.d.ts.map
175
+ //# sourceMappingURL=button-ByK1wG1b.d.ts.map
package/dist/button.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import { n as IconButtonProps, t as IconButton } from "./icon-button-2r6S3HVA.js";
2
- import { n as ButtonProps, t as Button } from "./button-B6StZJsz.js";
2
+ import { n as ButtonProps, t as Button } from "./button-ByK1wG1b.js";
3
3
  import { n as ButtonGroupProps, t as ButtonGroup } from "./index-ViSCOUrU.js";
4
4
  export { Button, ButtonGroup, ButtonGroupProps, ButtonProps, IconButton, IconButtonProps };
@@ -19,7 +19,7 @@ type CheckedState = boolean | "indeterminate";
19
19
  * </form>
20
20
  * ```
21
21
  */
22
- declare const Checkbox: react.ForwardRefExoticComponent<Omit<Omit<react.DetailedHTMLProps<react.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "defaultChecked" | "type" | "checked"> & WithValidation & {
22
+ declare const Checkbox: react.ForwardRefExoticComponent<Omit<Omit<react.DetailedHTMLProps<react.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "type" | "defaultChecked" | "checked"> & WithValidation & {
23
23
  /**
24
24
  * The controlled checked state of the checkbox. Must be used in conjunction with onChange.
25
25
  */
@@ -27,7 +27,7 @@ declare const isSupportedLanguage: (value: unknown) => value is SupportedLanguag
27
27
  * Formats a language name into a class name that Prism.js can understand.
28
28
  * @default "language-sh"
29
29
  */
30
- declare function formatLanguageClassName(language?: SupportedLanguage | undefined): "language-html" | "language-ruby" | "language-text" | "language-py" | "language-go" | "language-plaintext" | "language-bash" | "language-cs" | "language-csharp" | "language-css" | "language-dotnet" | "language-java" | "language-javascript" | "language-js" | "language-json" | "language-jsx" | "language-markup" | "language-plain" | "language-python" | "language-rb" | "language-rust" | "language-sh" | "language-shell" | "language-ts" | "language-tsx" | "language-txt" | "language-typescript" | "language-xml" | "language-yaml" | "language-yml";
30
+ declare function formatLanguageClassName(language?: SupportedLanguage | undefined): "language-text" | "language-ruby" | "language-plaintext" | "language-html" | "language-go" | "language-py" | "language-bash" | "language-cs" | "language-csharp" | "language-css" | "language-dotnet" | "language-java" | "language-javascript" | "language-js" | "language-json" | "language-jsx" | "language-markup" | "language-plain" | "language-python" | "language-rb" | "language-rust" | "language-sh" | "language-shell" | "language-ts" | "language-tsx" | "language-txt" | "language-typescript" | "language-xml" | "language-yaml" | "language-yml";
31
31
  //#endregion
32
32
  //#region src/components/code-block/indentation.d.ts
33
33
  declare const indentations: readonly ["tabs", "spaces"];
package/dist/command.d.ts CHANGED
@@ -97,11 +97,11 @@ declare const Command: {
97
97
  */
98
98
  readonly Root: react.ForwardRefExoticComponent<Omit<{
99
99
  children?: React.ReactNode;
100
- } & Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof react.HTMLAttributes<HTMLDivElement> | "key"> & {
100
+ } & Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof react.HTMLAttributes<HTMLDivElement>> & {
101
101
  ref?: React.Ref<HTMLDivElement>;
102
102
  } & {
103
103
  asChild?: boolean;
104
- }, keyof react.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & {
104
+ }, "key" | "asChild" | keyof react.HTMLAttributes<HTMLDivElement>> & {
105
105
  label?: string;
106
106
  shouldFilter?: boolean;
107
107
  filter?: (value: string, search: string, keywords?: string[]) => number;
@@ -178,7 +178,7 @@ declare const Command: {
178
178
  ref?: React.Ref<HTMLInputElement>;
179
179
  } & {
180
180
  asChild?: boolean;
181
- }, "asChild" | "key" | keyof react.InputHTMLAttributes<HTMLInputElement>>, "onChange" | "type" | "value"> & {
181
+ }, "key" | "asChild" | keyof react.InputHTMLAttributes<HTMLInputElement>>, "type" | "value" | "onChange"> & {
182
182
  value?: string;
183
183
  onValueChange?: (search: string) => void;
184
184
  } & react.RefAttributes<HTMLInputElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
@@ -195,11 +195,11 @@ declare const Command: {
195
195
  */
196
196
  readonly List: react.ForwardRefExoticComponent<Omit<{
197
197
  children?: React.ReactNode;
198
- } & Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof react.HTMLAttributes<HTMLDivElement> | "key"> & {
198
+ } & Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof react.HTMLAttributes<HTMLDivElement>> & {
199
199
  ref?: React.Ref<HTMLDivElement>;
200
200
  } & {
201
201
  asChild?: boolean;
202
- }, keyof react.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & {
202
+ }, "key" | "asChild" | keyof react.HTMLAttributes<HTMLDivElement>> & {
203
203
  label?: string;
204
204
  } & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
205
205
  /**
@@ -214,11 +214,11 @@ declare const Command: {
214
214
  */
215
215
  readonly Empty: react.ForwardRefExoticComponent<Omit<{
216
216
  children?: React.ReactNode;
217
- } & Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof react.HTMLAttributes<HTMLDivElement> | "key"> & {
217
+ } & Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof react.HTMLAttributes<HTMLDivElement>> & {
218
218
  ref?: React.Ref<HTMLDivElement>;
219
219
  } & {
220
220
  asChild?: boolean;
221
- }, keyof react.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
221
+ }, "key" | "asChild" | keyof react.HTMLAttributes<HTMLDivElement>> & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
222
222
  /**
223
223
  * The group component for the Command component.
224
224
  *
@@ -235,11 +235,11 @@ declare const Command: {
235
235
  */
236
236
  readonly Group: react.ForwardRefExoticComponent<Omit<{
237
237
  children?: React.ReactNode;
238
- } & Omit<Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof react.HTMLAttributes<HTMLDivElement> | "key"> & {
238
+ } & Omit<Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof react.HTMLAttributes<HTMLDivElement>> & {
239
239
  ref?: React.Ref<HTMLDivElement>;
240
240
  } & {
241
241
  asChild?: boolean;
242
- }, keyof react.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "value" | "heading"> & {
242
+ }, "key" | "asChild" | keyof react.HTMLAttributes<HTMLDivElement>>, "value" | "heading"> & {
243
243
  heading?: React.ReactNode;
244
244
  value?: string;
245
245
  forceMount?: boolean;
@@ -258,11 +258,11 @@ declare const Command: {
258
258
  */
259
259
  readonly Item: react.ForwardRefExoticComponent<Omit<{
260
260
  children?: React.ReactNode;
261
- } & Omit<Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof react.HTMLAttributes<HTMLDivElement> | "key"> & {
261
+ } & Omit<Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof react.HTMLAttributes<HTMLDivElement>> & {
262
262
  ref?: React.Ref<HTMLDivElement>;
263
263
  } & {
264
264
  asChild?: boolean;
265
- }, keyof react.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "onSelect" | "disabled" | "value"> & {
265
+ }, "key" | "asChild" | keyof react.HTMLAttributes<HTMLDivElement>>, "disabled" | "value" | "onSelect"> & {
266
266
  disabled?: boolean;
267
267
  onSelect?: (value: string) => void;
268
268
  value?: string;
@@ -290,11 +290,11 @@ declare const Command: {
290
290
  * <Command.Separator />
291
291
  * ```
292
292
  */
293
- readonly Separator: react.ForwardRefExoticComponent<Omit<Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof react.HTMLAttributes<HTMLDivElement> | "key"> & {
293
+ readonly Separator: react.ForwardRefExoticComponent<Omit<Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof react.HTMLAttributes<HTMLDivElement>> & {
294
294
  ref?: React.Ref<HTMLDivElement>;
295
295
  } & {
296
296
  asChild?: boolean;
297
- }, keyof react.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & {
297
+ }, "key" | "asChild" | keyof react.HTMLAttributes<HTMLDivElement>> & {
298
298
  alwaysRender?: boolean;
299
299
  } & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
300
300
  };
@@ -1,4 +1,4 @@
1
- import { t as Button } from "./button-B6StZJsz.js";
1
+ import { t as Button } from "./button-ByK1wG1b.js";
2
2
  import { s as SortingMode } from "./direction-DYYpi-JC.js";
3
3
  import { t as Table$1 } from "./table-Bs1D5Aj7.js";
4
4
  import * as react from "react";
package/dist/hooks.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","names":[],"sources":["../src/hooks/use-breakpoint.tsx","../src/hooks/use-callback-ref.tsx","../src/hooks/use-debounced-callback.tsx","../src/hooks/use-isomorphic-layout-effect.tsx","../src/hooks/use-random-stable-id.tsx","../src/hooks/use-scroll-behavior.tsx","../src/hooks/use-in-view.tsx","../src/hooks/use-undo-redo.tsx"],"sourcesContent":["import { useSyncExternalStore } from \"react\";\n\n/**\n * Tailwind CSS breakpoints in descending order (largest → smallest).\n *\n * These correspond to Tailwind’s default `theme.screens` config and are used\n * to determine the current viewport size.\n *\n * @see https://tailwindcss.com/docs/screens\n *\n * @example\n * \"2xl\" // ≥96rem (1536px)\n * \"xl\" // ≥80rem (1280px)\n * \"lg\" // ≥64rem (1024px)\n * \"md\" // ≥48rem (768px)\n * \"sm\" // ≥40rem (640px)\n * \"xs\" // ≥30rem (480px)\n * \"2xs\" // ≥22.5rem (360px)\n */\nconst tailwindBreakpoints = [\"2xl\", \"xl\", \"lg\", \"md\", \"sm\", \"xs\", \"2xs\"] as const;\n\n/**\n * A valid Tailwind CSS breakpoint identifier.\n *\n * @example\n * const bp: TailwindBreakpoint = \"md\"; // ≥48rem (768px)\n *\n * @example\n * \"2xl\" // ≥96rem (1536px)\n * \"xl\" // ≥80rem (1280px)\n * \"lg\" // ≥64rem (1024px)\n * \"md\" // ≥48rem (768px)\n * \"sm\" // ≥40rem (640px)\n * \"xs\" // ≥30rem (480px)\n * \"2xs\" // ≥22.5rem (360px)\n */\ntype TailwindBreakpoint = (typeof tailwindBreakpoints)[number];\n\n/**\n * Mantle’s breakpoint set, extending Tailwind’s with `\"default\"`.\n *\n * `\"default\"` represents the base (0px and up) viewport,\n * useful for defining fallbacks or mobile-first styles.\n *\n * @example\n * \"default\" // ≥0rem (0px)\n * \"2xs\" // ≥22.5rem (360px)\n * \"xs\" // ≥30rem (480px)\n * \"sm\" // ≥40rem (640px)\n * \"md\" // ≥48rem (768px)\n * \"lg\" // ≥64rem (1024px)\n * \"xl\" // ≥80rem (1280px)\n * \"2xl\" // ≥96rem (1536px)\n */\nconst breakpoints = [\"default\", ...tailwindBreakpoints] as const;\n\n/**\n * A valid Mantle breakpoint identifier.\n *\n * Includes Tailwind’s standard breakpoints plus `\"default\"` for 0px+.\n *\n * @example\n * const bp: Breakpoint = \"default\"; // ≥0px\n *\n * @example\n * \"default\" // ≥0rem (0px)\n * \"2xs\" // ≥22.5rem (360px)\n * \"xs\" // ≥30rem (480px)\n * \"sm\" // ≥40rem (640px)\n * \"md\" // ≥48rem (768px)\n * \"lg\" // ≥64rem (1024px)\n * \"xl\" // ≥80rem (1280px)\n * \"2xl\" // ≥96rem (1536px)\n */\ntype Breakpoint = (typeof breakpoints)[number];\n\n/**\n * React hook that returns the current breakpoint based on the viewport width.\n *\n * Uses a singleton subscription to a set of min-width media queries and returns\n * the largest matching breakpoint. Designed for React 18+ with\n * `useSyncExternalStore`.\n *\n * @returns {Breakpoint} The current breakpoint that matches the viewport width.\n *\n * @example\n * const breakpoint = useBreakpoint();\n * if (breakpoint === \"lg\") {\n * // Do something for large screens and above\n * }\n */\nfunction useBreakpoint(): Breakpoint {\n\treturn useSyncExternalStore(\n\t\tsubscribeToBreakpointChanges,\n\t\tgetCurrentBreakpointSnapshot,\n\t\t() => \"default\", // SSR fallback\n\t);\n}\n\n/**\n * React hook that returns true if the current viewport width is below the specified breakpoint.\n *\n * This hook uses `window.matchMedia` with a max-width media query and leverages\n * `useSyncExternalStore` to stay compliant with React's concurrent rendering model.\n *\n * @param {TailwindBreakpoint} breakpoint - The breakpoint to check against (e.g., \"md\", \"lg\").\n *\n * @returns {boolean} `true` if the viewport width is below the breakpoint, otherwise `false`.\n *\n * @example\n * // Check if viewport is below medium (768px)\n * const isBelowMd = useIsBelowBreakpoint(\"md\");\n */\nfunction useIsBelowBreakpoint(breakpoint: TailwindBreakpoint): boolean {\n\treturn useSyncExternalStore(\n\t\tcreateBelowBreakpointSubscribe(breakpoint),\n\t\tcreateBelowBreakpointGetSnapshot(breakpoint),\n\t\t() => false, // SSR fallback - assume desktop\n\t);\n}\n\nexport {\n\t//,\n\tbreakpoints,\n\tuseBreakpoint,\n\tuseIsBelowBreakpoint,\n};\n\nexport type {\n\t//,\n\tBreakpoint,\n\tTailwindBreakpoint,\n};\n\n/**\n * A CSS media query string representing a minimum width in `rem` units.\n *\n * @example\n * const query: MinWidthQuery = \"(min-width: 48rem)\";\n *\n * @private\n */\ntype MinWidthQuery = `(min-width: ${number}rem)`;\n\n/**\n * A CSS media query string representing a maximum width in `rem` units.\n *\n * @example\n * const query: MaxWidthQuery = \"(max-width: 47.99rem)\";\n *\n * @private\n */\ntype MaxWidthQuery = `(max-width: ${number}rem)`;\n\n/**\n * Precomputed min-width media query strings for each Tailwind breakpoint.\n *\n * Using constants avoids template string work in hot paths and ensures type\n * safety against the `MinWidthQuery` template literal type.\n *\n * @remarks\n * These are expressed in `rem`. If your CSS breakpoints are in `px`, consider\n * aligning units to avoid JS/CSS drift when `html{font-size}` changes.\n *\n * @private\n */\nconst breakpointQueries = {\n\t\"2xl\": \"(min-width: 96rem)\" as const,\n\txl: \"(min-width: 80rem)\" as const,\n\tlg: \"(min-width: 64rem)\" as const,\n\tmd: \"(min-width: 48rem)\" as const,\n\tsm: \"(min-width: 40rem)\" as const,\n\txs: \"(min-width: 30rem)\" as const,\n\t\"2xs\": \"(min-width: 22.5rem)\" as const,\n} as const satisfies Record<TailwindBreakpoint, MinWidthQuery>;\n\n/**\n * Precomputed max-width media query strings used by `useIsBelowBreakpoint`.\n *\n * The `-0.01rem` offset avoids overlap at exact boundaries.\n *\n * @private\n */\nconst belowBreakpointQueries = {\n\t\"2xl\": \"(max-width: 95.99rem)\" as const, // 96 - 0.01\n\txl: \"(max-width: 79.99rem)\" as const, // 80 - 0.01\n\tlg: \"(max-width: 63.99rem)\" as const, // 64 - 0.01\n\tmd: \"(max-width: 47.99rem)\" as const, // 48 - 0.01\n\tsm: \"(max-width: 39.99rem)\" as const, // 40 - 0.01\n\txs: \"(max-width: 29.99rem)\" as const, // 30 - 0.01\n\t\"2xs\": \"(max-width: 22.49rem)\" as const, // 22.5 - 0.01\n} as const satisfies Record<TailwindBreakpoint, MaxWidthQuery>;\n\n/**\n * Lazily-initialized cache of `MediaQueryList` objects for min-width queries.\n *\n * Initialized on first access to remain SSR-safe (no `window` at import time).\n *\n * @private\n */\nlet minWidthMQLs: Record<TailwindBreakpoint, MediaQueryList> | null = null;\n\n/**\n * Lazily-initialized cache of `MediaQueryList` objects for max-width queries.\n *\n * Used by `useIsBelowBreakpoint`. Also SSR-safe by lazy access.\n *\n * @private\n */\nlet maxWidthMQLs: Record<TailwindBreakpoint, MediaQueryList> | null = null;\n\n/**\n * Get (and lazily create) the cached `MediaQueryList` objects for min-width queries.\n *\n * @returns A record of `MediaQueryList` keyed by Tailwind breakpoint.\n * @private\n */\nfunction getMinWidthMQLs(): Record<TailwindBreakpoint, MediaQueryList> {\n\tif (!minWidthMQLs) {\n\t\tminWidthMQLs = {\n\t\t\t\"2xl\": window.matchMedia(breakpointQueries[\"2xl\"]),\n\t\t\txl: window.matchMedia(breakpointQueries.xl),\n\t\t\tlg: window.matchMedia(breakpointQueries.lg),\n\t\t\tmd: window.matchMedia(breakpointQueries.md),\n\t\t\tsm: window.matchMedia(breakpointQueries.sm),\n\t\t\txs: window.matchMedia(breakpointQueries.xs),\n\t\t\t\"2xs\": window.matchMedia(breakpointQueries[\"2xs\"]),\n\t\t};\n\t}\n\treturn minWidthMQLs;\n}\n\n/**\n * Get (and lazily create) the cached `MediaQueryList` for a specific max-width breakpoint.\n *\n * @param breakpoint - Tailwind breakpoint identifier (e.g., \"md\").\n * @returns The corresponding `MediaQueryList`.\n * @private\n */\nfunction getMaxWidthMQL(breakpoint: TailwindBreakpoint): MediaQueryList {\n\tif (!maxWidthMQLs) {\n\t\tmaxWidthMQLs = {\n\t\t\t\"2xl\": window.matchMedia(belowBreakpointQueries[\"2xl\"]),\n\t\t\txl: window.matchMedia(belowBreakpointQueries.xl),\n\t\t\tlg: window.matchMedia(belowBreakpointQueries.lg),\n\t\t\tmd: window.matchMedia(belowBreakpointQueries.md),\n\t\t\tsm: window.matchMedia(belowBreakpointQueries.sm),\n\t\t\txs: window.matchMedia(belowBreakpointQueries.xs),\n\t\t\t\"2xs\": window.matchMedia(belowBreakpointQueries[\"2xs\"]),\n\t\t};\n\t}\n\treturn maxWidthMQLs[breakpoint];\n}\n\n/**\n * Current breakpoint value used by the singleton store backing `useBreakpoint`.\n *\n * Initialized to `\"default\"` and updated on media-query change events.\n *\n * @private\n */\nlet currentBreakpointValue: Breakpoint = \"default\";\n\n/**\n * Set of component listeners subscribed to the singleton breakpoint store.\n *\n * Each listener is invoked when the current breakpoint value changes.\n *\n * @private\n */\nconst breakpointListeners = new Set<() => void>();\n\n/**\n * Flag indicating whether global media-query listeners are currently attached.\n *\n * Prevents duplicate registrations and enables full teardown when unused.\n *\n * @private\n */\nlet breakpointSubscriptionActive = false;\n\n/**\n * Compute the current breakpoint by checking cached min-width MQLs\n * from largest to smallest.\n *\n * @returns {Breakpoint} The largest matching breakpoint, or `\"default\"`.\n * @private\n */\nfunction getCurrentBreakpoint(): Breakpoint {\n\tconst mqls = getMinWidthMQLs();\n\tfor (const breakpoint of tailwindBreakpoints) {\n\t\tif (mqls[breakpoint].matches) {\n\t\t\treturn breakpoint;\n\t\t}\n\t}\n\treturn \"default\";\n}\n\n/**\n * Update the current breakpoint value and notify all listeners.\n *\n * Uses `requestAnimationFrame` to coalesce rapid resize events and minimize\n * re-renders during active window resizing.\n *\n * @private\n */\nlet breakpointUpdatePending = false;\nfunction updateCurrentBreakpoint() {\n\tif (!breakpointUpdatePending) {\n\t\tbreakpointUpdatePending = true;\n\t\trequestAnimationFrame(() => {\n\t\t\tbreakpointUpdatePending = false;\n\t\t\tconst newBreakpoint = getCurrentBreakpoint();\n\t\t\tif (newBreakpoint !== currentBreakpointValue) {\n\t\t\t\tcurrentBreakpointValue = newBreakpoint;\n\t\t\t\tfor (const listener of breakpointListeners) {\n\t\t\t\t\tlistener();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n}\n\n/**\n * Subscribe a component to breakpoint changes (singleton pattern).\n *\n * Ensures only one set of MQL listeners exists app-wide. Also reconciles the\n * `useSyncExternalStore` initial snapshot/subscribe race by invoking the\n * subscriber once on mount.\n *\n * @param callback - Listener invoked when the breakpoint value may have changed.\n * @returns Cleanup function to unsubscribe the listener.\n * @private\n */\nfunction subscribeToBreakpointChanges(callback: () => void) {\n\tbreakpointListeners.add(callback);\n\n\t// Attach global listeners once\n\tif (!breakpointSubscriptionActive) {\n\t\tbreakpointSubscriptionActive = true;\n\t\tconst mqls = getMinWidthMQLs();\n\n\t\t// Initialize current value synchronously\n\t\tcurrentBreakpointValue = getCurrentBreakpoint();\n\n\t\t// Attach listeners to all breakpoint MQLs\n\t\tfor (const mql of Object.values(mqls)) {\n\t\t\tmql.addEventListener(\"change\", updateCurrentBreakpoint);\n\t\t}\n\t}\n\n\t// Reconcile initial getSnapshot vs subscribe ordering\n\tcallback();\n\n\t// Cleanup\n\treturn () => {\n\t\tbreakpointListeners.delete(callback);\n\n\t\t// Tear down global listeners when no one is listening\n\t\tif (breakpointListeners.size === 0 && breakpointSubscriptionActive) {\n\t\t\tbreakpointSubscriptionActive = false;\n\t\t\tconst mqls = getMinWidthMQLs();\n\t\t\tfor (const mql of Object.values(mqls)) {\n\t\t\t\tmql.removeEventListener(\"change\", updateCurrentBreakpoint);\n\t\t\t}\n\t\t}\n\t};\n}\n\n/**\n * Return the current breakpoint value from the singleton store.\n *\n * Used as the `getSnapshot` for `useSyncExternalStore`.\n *\n * @returns {Breakpoint} The latest computed breakpoint.\n * @private\n */\nfunction getCurrentBreakpointSnapshot(): Breakpoint {\n\treturn currentBreakpointValue;\n}\n\n/**\n * Cached `subscribe` functions keyed by breakpoint.\n *\n * Without caching, `useSyncExternalStore` receives a new function reference on\n * every render, causing it to tear down and re-attach the MQL listener each\n * time — the primary source of resize sluggishness.\n *\n * @private\n */\nconst belowBreakpointSubscribeCache = new Map<\n\tTailwindBreakpoint,\n\t(callback: () => void) => () => void\n>();\n\n/**\n * Get (or create and cache) a `subscribe` function for a specific \"below\" breakpoint.\n *\n * Uses a cached `MediaQueryList` and rAF-throttled change handler to avoid\n * bursty updates during resize.\n *\n * @param breakpoint - Tailwind breakpoint identifier (e.g., \"lg\").\n * @returns A stable `subscribe` function suitable for `useSyncExternalStore`.\n * @private\n */\nfunction createBelowBreakpointSubscribe(breakpoint: TailwindBreakpoint) {\n\tlet cached = belowBreakpointSubscribeCache.get(breakpoint);\n\tif (cached) {\n\t\treturn cached;\n\t}\n\n\tcached = (callback: () => void) => {\n\t\tconst mediaQuery = getMaxWidthMQL(breakpoint);\n\n\t\t// rAF throttle the change callback during active resize\n\t\tlet pending = false;\n\t\tconst onChange = () => {\n\t\t\tif (!pending) {\n\t\t\t\tpending = true;\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tpending = false;\n\t\t\t\t\tcallback();\n\t\t\t\t});\n\t\t\t}\n\t\t};\n\n\t\tmediaQuery.addEventListener(\"change\", onChange);\n\t\treturn () => {\n\t\t\tmediaQuery.removeEventListener(\"change\", onChange);\n\t\t};\n\t};\n\n\tbelowBreakpointSubscribeCache.set(breakpoint, cached);\n\treturn cached;\n}\n\n/**\n * Cached `getSnapshot` functions keyed by breakpoint.\n *\n * Ensures `useSyncExternalStore` receives a referentially stable function,\n * preventing unnecessary subscription churn.\n *\n * @private\n */\nconst belowBreakpointSnapshotCache = new Map<TailwindBreakpoint, () => boolean>();\n\n/**\n * Get (or create and cache) a `getSnapshot` function for a specific \"below\" breakpoint.\n *\n * Uses the cached `MediaQueryList` for the target breakpoint.\n *\n * @param breakpoint - Tailwind breakpoint identifier (e.g., \"lg\").\n * @returns A stable function that returns `true` when the viewport is below the breakpoint.\n * @private\n */\nfunction createBelowBreakpointGetSnapshot(breakpoint: TailwindBreakpoint) {\n\tlet cached = belowBreakpointSnapshotCache.get(breakpoint);\n\tif (cached) {\n\t\treturn cached;\n\t}\n\n\tcached = () => {\n\t\tconst mediaQuery = getMaxWidthMQL(breakpoint);\n\t\treturn mediaQuery.matches;\n\t};\n\n\tbelowBreakpointSnapshotCache.set(breakpoint, cached);\n\treturn cached;\n}\n","import { useEffect, useMemo, useRef } from \"react\";\n\n/**\n * Returns a memoized callback that will always refer to the latest callback\n * passed to the hook.\n *\n * This is useful when you want to pass a callback which may or may not be\n * memoized (have a stable identity) to a child component that will be updated\n * without causing the child component to re-render.\n */\nfunction useCallbackRef<T extends (...args: unknown[]) => unknown>(callback: T | undefined): T {\n\tconst callbackRef = useRef(callback);\n\n\tuseEffect(() => {\n\t\tcallbackRef.current = callback;\n\t});\n\n\treturn useMemo(() => ((...args) => callbackRef.current?.(...args)) as T, []);\n}\n\nexport {\n\t//,\n\tuseCallbackRef,\n};\n","import { useCallback, useEffect, useRef } from \"react\";\nimport { useCallbackRef } from \"./use-callback-ref.js\";\n\ntype Options = {\n\t/**\n\t * The delay in milliseconds to wait before calling the callback.\n\t */\n\twaitMs: number;\n};\n\n/**\n * Create a debounced version of a callback function.\n *\n * It allows you to delay the execution of a function until a certain period of\n * inactivity has passed (the `options.waitMs`), which can be useful for limiting rapid\n * invocations of a function (like in search inputs or button clicks)\n *\n * Note: The debounced callback will always refer to the latest callback passed\n * even without memoization, so it's stable and safe to use in dependency arrays.\n */\nfunction useDebouncedCallback<T extends (...args: unknown[]) => unknown>(\n\tcallbackFn: T,\n\toptions: Options,\n) {\n\tconst stableCallbackFn = useCallbackRef(callbackFn);\n\tconst debounceTimerRef = useRef(0);\n\tuseEffect(() => () => window.clearTimeout(debounceTimerRef.current), []);\n\n\treturn useCallback(\n\t\t(...args: Parameters<T>) => {\n\t\t\twindow.clearTimeout(debounceTimerRef.current);\n\t\t\tdebounceTimerRef.current = window.setTimeout(() => stableCallbackFn(...args), options.waitMs);\n\t\t},\n\t\t[stableCallbackFn, options.waitMs],\n\t);\n}\n\nexport {\n\t//,\n\tuseDebouncedCallback,\n};\n","import { useEffect, useLayoutEffect } from \"react\";\n\n/**\n * useIsomorphicLayoutEffect is a hook that uses useLayoutEffect on the client and useEffect on the server.\n */\nexport const useIsomorphicLayoutEffect =\n\ttypeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n","import { useMemo } from \"react\";\n\n/**\n * Hook to generate a random, stable id.\n * This is similar to `useId`, but generates a stable id client side which can also\n * be used for css selectors and element ids.\n */\nconst useRandomStableId = (prefix = \"mantle\") => useMemo(() => randomStableId(prefix), [prefix]);\n\nexport {\n\t//,\n\tuseRandomStableId,\n};\n\nfunction randomStableId(prefix = \"mantle\") {\n\tconst _prefix = prefix.trim() || \"mantle\";\n\treturn [_prefix, randomPostfix()].join(\"-\");\n}\n\nfunction randomPostfix() {\n\treturn Math.random().toString(36).substring(2, 9);\n}\n","import { useMemo } from \"react\";\nimport { usePrefersReducedMotion } from \"./use-prefers-reduced-motion.js\";\n\n/**\n * `scroll-behavior` values:\n *\n * - `\"auto\"` — scrolling happens instantly (no animation).\n * - `\"smooth\"` — scrolling animates smoothly using a user-agent–defined easing and duration.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior#values\n */\ntype ScrollBehavior = \"auto\" | \"smooth\";\n\n/**\n * Returns a `ScrollBehavior` that respects the user's motion preference via `usePrefersReducedMotion`.\n *\n * - When `usePrefersReducedMotion()` is `true`, returns `\"auto\"` (no animated scroll).\n * - Otherwise returns `\"smooth\"`.\n *\n * Use this with `window.scrollTo`, `Element.scrollIntoView`, etc. It prevents\n * smooth-scrolling for users who opt out of motion and avoids SSR “first paint”\n * animations thanks to the hook’s conservative server default.\n *\n * @example\n * // Scroll to top\n * const behavior = useScrollBehavior();\n * window.scrollTo({ top: 0, behavior });\n *\n * @example\n * // Bring a section into view\n * const behavior = useScrollBehavior();\n * sectionRef.current?.scrollIntoView({ behavior, block: \"start\" });\n *\n * @see {@link usePrefersReducedMotion}\n * @see CSS `scroll-behavior` property (values: `\"auto\"`, `\"smooth\"`).\n */\nexport function useScrollBehavior(): ScrollBehavior {\n\tconst prefersReducedMotion = usePrefersReducedMotion();\n\n\treturn useMemo(() => (prefersReducedMotion ? \"auto\" : \"smooth\"), [prefersReducedMotion]);\n}\n","\"use client\";\n\nimport { type RefObject, useEffect, useState } from \"react\";\nimport type { InViewOptions, MarginType } from \"../utils/in-view.js\";\nimport { inView } from \"../utils/in-view.js\";\n\n/**\n * Options for the `useInView` hook.\n */\ntype UseInViewOptions = {\n\t/**\n\t * A ref to a scrollable container element to use as the intersection root.\n\t * Defaults to the browser viewport.\n\t */\n\troot?: RefObject<Element | null>;\n\n\t/**\n\t * Expand or contract the detected area from each side of the root's bounding box.\n\t * Uses the same syntax as the CSS `margin` shorthand (e.g. `\"10px\"`, `\"10% 20px\"`).\n\t */\n\tmargin?: MarginType;\n\n\t/**\n\t * How much of the element must be visible before it is considered in view.\n\t * - `\"some\"` (default): Any part of the element is visible.\n\t * - `\"all\"`: The entire element is visible.\n\t * - `number`: An intersection ratio between `0` and `1` (e.g. `0.5` for 50%).\n\t */\n\tamount?: \"some\" | \"all\" | number;\n\n\t/**\n\t * If `true`, stop observing once the element enters the viewport for the\n\t * first time. Useful for one-shot entrance animations.\n\t * Defaults to `false`.\n\t */\n\tonce?: boolean;\n\n\t/**\n\t * The initial visibility state returned before the observer has attached.\n\t * Defaults to `false`.\n\t */\n\tinitial?: boolean;\n};\n\n/**\n * React hook that tracks whether a DOM element is visible within the viewport\n * (or a scrollable container) using the `IntersectionObserver` API.\n *\n * @param ref - A ref attached to the element to observe.\n * @param options - Options controlling the scroll root, margin, threshold,\n * initial state, and one-time detection.\n * @returns `true` if the element is currently in view, otherwise `false`.\n *\n * @example\n * // Basic usage\n * const ref = useRef<HTMLDivElement>(null);\n * const isInView = useInView(ref);\n *\n * return <div ref={ref}>{isInView ? \"Visible!\" : \"Hidden\"}</div>;\n *\n * @example\n * // Trigger once when the element first enters the viewport\n * const ref = useRef<HTMLDivElement>(null);\n * const isInView = useInView(ref, { once: true });\n *\n * return (\n * <div\n * ref={ref}\n * style={{ opacity: isInView ? 1 : 0, transition: \"opacity 0.5s\" }}\n * />\n * );\n *\n * @example\n * // Require 50% of the element to be visible\n * const ref = useRef<HTMLDivElement>(null);\n * const isInView = useInView(ref, { amount: 0.5 });\n */\nfunction useInView(\n\tref: RefObject<Element | null>,\n\t{ root, margin, amount, once = false, initial = false }: UseInViewOptions = {},\n): boolean {\n\tconst [isInView, setInView] = useState(initial);\n\n\tuseEffect(() => {\n\t\tif (!ref.current || (once && isInView)) {\n\t\t\treturn;\n\t\t}\n\n\t\tfunction onEnter() {\n\t\t\tsetInView(true);\n\t\t\treturn once ? undefined : () => setInView(false);\n\t\t}\n\n\t\tconst options: InViewOptions = {\n\t\t\troot: (root && root.current) || undefined,\n\t\t\tmargin,\n\t\t\tamount,\n\t\t};\n\n\t\treturn inView(ref.current, onEnter, options);\n\t\t/**\n\t\t * Intentionally omit `isInView` from deps. The effect must only re-run\n\t\t * when the observation parameters change, not when visibility changes.\n\t\t * Including `isInView` would restart the observer (disconnect + reconnect)\n\t\t * on every enter/leave event, causing wasteful churn for the common\n\t\t * `once=false` case.\n\t\t */\n\t\t// oxlint-disable-next-line react-hooks/exhaustive-deps\n\t}, [root, ref, margin, once, amount]);\n\n\treturn isInView;\n}\n\nexport { useInView };\nexport type { UseInViewOptions };\n","import { useCallback, useMemo, useReducer } from \"react\";\n\ntype UndoRedoState<T> = {\n\tundoStack: T[];\n\tredoStack: T[];\n};\n\ntype UndoRedoAction<T> =\n\t| { type: \"push\"; snapshot: T }\n\t| { type: \"undo\"; current: T }\n\t| { type: \"redo\"; current: T };\n\nfunction undoRedoReducer<T>(state: UndoRedoState<T>, action: UndoRedoAction<T>): UndoRedoState<T> {\n\tswitch (action.type) {\n\t\tcase \"push\": {\n\t\t\treturn {\n\t\t\t\tundoStack: [...state.undoStack, action.snapshot],\n\t\t\t\tredoStack: [],\n\t\t\t};\n\t\t}\n\t\tcase \"undo\": {\n\t\t\tif (state.undoStack.length === 0) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\tconst undoStack = state.undoStack.slice(0, -1);\n\t\t\tconst previous = state.undoStack[state.undoStack.length - 1];\n\t\t\tif (previous === undefined) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tundoStack,\n\t\t\t\tredoStack: [...state.redoStack, action.current],\n\t\t\t};\n\t\t}\n\t\tcase \"redo\": {\n\t\t\tif (state.redoStack.length === 0) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\tconst redoStack = state.redoStack.slice(0, -1);\n\t\t\tconst next = state.redoStack[state.redoStack.length - 1];\n\t\t\tif (next === undefined) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tundoStack: [...state.undoStack, action.current],\n\t\t\t\tredoStack,\n\t\t\t};\n\t\t}\n\t}\n}\n\ntype UseUndoRedoReturn<T> = {\n\t/** Whether there are actions to undo. */\n\tcanUndo: boolean;\n\t/** Whether there are actions to redo. */\n\tcanRedo: boolean;\n\t/** Push a snapshot onto the undo stack. Clears the redo stack. */\n\tpush: (snapshot: T) => void;\n\t/** Pop the last snapshot from the undo stack. Returns `undefined` if empty. */\n\tundo: (current: T) => T | undefined;\n\t/** Pop the last snapshot from the redo stack. Returns `undefined` if empty. */\n\tredo: (current: T) => T | undefined;\n};\n\n/**\n * A generic undo/redo hook backed by a reducer.\n *\n * Maintains two stacks (undo and redo). Call `push` before mutating state\n * to snapshot the current value. Call `undo`/`redo` with the current value\n * to swap it with the previous/next snapshot.\n *\n * @example\n * ```tsx\n * const { push, undo, redo, canUndo, canRedo } = useUndoRedo<string[]>();\n *\n * function removeItem(item: string) {\n * push([...items]); // snapshot before mutation\n * setItems(items.filter((i) => i !== item));\n * }\n *\n * function handleKeyDown(event: KeyboardEvent) {\n * if ((event.metaKey || event.ctrlKey) && event.key === \"z\" && !event.shiftKey) {\n * const previous = undo(items);\n * if (previous) setItems(previous);\n * }\n * if ((event.metaKey || event.ctrlKey) && (event.shiftKey && event.key === \"z\" || event.key === \"y\")) {\n * const next = redo(items);\n * if (next) setItems(next);\n * }\n * }\n * ```\n */\nfunction useUndoRedo<T>(): UseUndoRedoReturn<T> {\n\tconst [state, dispatch] = useReducer(undoRedoReducer<T>, {\n\t\tundoStack: [],\n\t\tredoStack: [],\n\t});\n\n\tconst push = useCallback((snapshot: T) => {\n\t\tdispatch({ type: \"push\", snapshot });\n\t}, []);\n\n\tconst undo = useCallback(\n\t\t(current: T): T | undefined => {\n\t\t\tconst previous = state.undoStack[state.undoStack.length - 1];\n\t\t\tif (previous === undefined) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tdispatch({ type: \"undo\", current });\n\t\t\treturn previous;\n\t\t},\n\t\t[state.undoStack],\n\t);\n\n\tconst redo = useCallback(\n\t\t(current: T): T | undefined => {\n\t\t\tconst next = state.redoStack[state.redoStack.length - 1];\n\t\t\tif (next === undefined) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tdispatch({ type: \"redo\", current });\n\t\t\treturn next;\n\t\t},\n\t\t[state.redoStack],\n\t);\n\n\treturn useMemo(\n\t\t() => ({\n\t\t\tcanUndo: state.undoStack.length > 0,\n\t\t\tcanRedo: state.redoStack.length > 0,\n\t\t\tpush,\n\t\t\tundo,\n\t\t\tredo,\n\t\t}),\n\t\t[state.undoStack.length, state.redoStack.length, push, undo, redo],\n\t);\n}\n\nexport {\n\t//,\n\tuseUndoRedo,\n};\n\nexport type {\n\t//,\n\tUseUndoRedoReturn,\n};\n"],"mappings":"kdAmBA,MAAM,EAAsB,CAAC,MAAO,KAAM,KAAM,KAAM,KAAM,KAAM,MAAM,CAmClE,EAAc,CAAC,UAAW,GAAG,EAAoB,CAqCvD,SAAS,GAA4B,CACpC,OAAO,EACN,EACA,MACM,UACN,CAiBF,SAAS,EAAqB,EAAyC,CACtE,OAAO,EACN,EAA+B,EAAW,CAC1C,EAAiC,EAAW,KACtC,GACN,CAgDF,MAAM,EAAoB,CACzB,MAAO,qBACP,GAAI,qBACJ,GAAI,qBACJ,GAAI,qBACJ,GAAI,qBACJ,GAAI,qBACJ,MAAO,uBACP,CASK,EAAyB,CAC9B,MAAO,wBACP,GAAI,wBACJ,GAAI,wBACJ,GAAI,wBACJ,GAAI,wBACJ,GAAI,wBACJ,MAAO,wBACP,CASD,IAAI,EAAkE,KASlE,EAAkE,KAQtE,SAAS,GAA8D,CAYtE,MAXA,CACC,IAAe,CACd,MAAO,OAAO,WAAW,EAAkB,OAAO,CAClD,GAAI,OAAO,WAAW,EAAkB,GAAG,CAC3C,GAAI,OAAO,WAAW,EAAkB,GAAG,CAC3C,GAAI,OAAO,WAAW,EAAkB,GAAG,CAC3C,GAAI,OAAO,WAAW,EAAkB,GAAG,CAC3C,GAAI,OAAO,WAAW,EAAkB,GAAG,CAC3C,MAAO,OAAO,WAAW,EAAkB,OAAO,CAClD,CAEK,EAUR,SAAS,EAAe,EAAgD,CAYvE,MAXA,CACC,IAAe,CACd,MAAO,OAAO,WAAW,EAAuB,OAAO,CACvD,GAAI,OAAO,WAAW,EAAuB,GAAG,CAChD,GAAI,OAAO,WAAW,EAAuB,GAAG,CAChD,GAAI,OAAO,WAAW,EAAuB,GAAG,CAChD,GAAI,OAAO,WAAW,EAAuB,GAAG,CAChD,GAAI,OAAO,WAAW,EAAuB,GAAG,CAChD,MAAO,OAAO,WAAW,EAAuB,OAAO,CACvD,CAEK,EAAa,GAUrB,IAAI,EAAqC,UASzC,MAAM,EAAsB,IAAI,IAShC,IAAI,EAA+B,GASnC,SAAS,GAAmC,CAC3C,IAAM,EAAO,GAAiB,CAC9B,IAAK,IAAM,KAAc,EACxB,GAAI,EAAK,GAAY,QACpB,OAAO,EAGT,MAAO,UAWR,IAAI,EAA0B,GAC9B,SAAS,GAA0B,CAC7B,IACJ,EAA0B,GAC1B,0BAA4B,CAC3B,EAA0B,GAC1B,IAAM,EAAgB,GAAsB,CAC5C,GAAI,IAAkB,EAAwB,CAC7C,EAAyB,EACzB,IAAK,IAAM,KAAY,EACtB,GAAU,GAGX,EAeJ,SAAS,EAA6B,EAAsB,CAI3D,GAHA,EAAoB,IAAI,EAAS,CAG7B,CAAC,EAA8B,CAClC,EAA+B,GAC/B,IAAM,EAAO,GAAiB,CAG9B,EAAyB,GAAsB,CAG/C,IAAK,IAAM,KAAO,OAAO,OAAO,EAAK,CACpC,EAAI,iBAAiB,SAAU,EAAwB,CAQzD,OAHA,GAAU,KAGG,CAIZ,GAHA,EAAoB,OAAO,EAAS,CAGhC,EAAoB,OAAS,GAAK,EAA8B,CACnE,EAA+B,GAC/B,IAAM,EAAO,GAAiB,CAC9B,IAAK,IAAM,KAAO,OAAO,OAAO,EAAK,CACpC,EAAI,oBAAoB,SAAU,EAAwB,GAc9D,SAAS,GAA2C,CACnD,OAAO,EAYR,MAAM,EAAgC,IAAI,IAe1C,SAAS,EAA+B,EAAgC,CACvE,IAAI,EAAS,EAA8B,IAAI,EAAW,CA2B1D,OA1BI,IAIJ,EAAU,GAAyB,CAClC,IAAM,EAAa,EAAe,EAAW,CAGzC,EAAU,GACR,MAAiB,CACjB,IACJ,EAAU,GACV,0BAA4B,CAC3B,EAAU,GACV,GAAU,EACT,GAKJ,OADA,EAAW,iBAAiB,SAAU,EAAS,KAClC,CACZ,EAAW,oBAAoB,SAAU,EAAS,GAIpD,EAA8B,IAAI,EAAY,EAAO,CAC9C,GAWR,MAAM,EAA+B,IAAI,IAWzC,SAAS,EAAiC,EAAgC,CACzE,IAAI,EAAS,EAA6B,IAAI,EAAW,CAWzD,OAVI,IAIJ,MACoB,EAAe,EAAW,CAC3B,QAGnB,EAA6B,IAAI,EAAY,EAAO,CAC7C,GCzcR,SAAS,EAA0D,EAA4B,CAC9F,IAAM,EAAc,EAAO,EAAS,CAMpC,OAJA,MAAgB,CACf,EAAY,QAAU,GACrB,CAEK,QAAgB,GAAG,IAAS,EAAY,UAAU,GAAG,EAAK,EAAQ,EAAE,CAAC,CCG7E,SAAS,EACR,EACA,EACC,CACD,IAAM,EAAmB,EAAe,EAAW,CAC7C,EAAmB,EAAO,EAAE,CAGlC,OAFA,UAAsB,OAAO,aAAa,EAAiB,QAAQ,CAAE,EAAE,CAAC,CAEjE,GACL,GAAG,IAAwB,CAC3B,OAAO,aAAa,EAAiB,QAAQ,CAC7C,EAAiB,QAAU,OAAO,eAAiB,EAAiB,GAAG,EAAK,CAAE,EAAQ,OAAO,EAE9F,CAAC,EAAkB,EAAQ,OAAO,CAClC,CC7BF,MAAa,EACZ,OAAO,OAAW,IAAc,EAAkB,ECC7C,GAAqB,EAAS,WAAa,MAAc,EAAe,EAAO,CAAE,CAAC,EAAO,CAAC,CAOhG,SAAS,EAAe,EAAS,SAAU,CAE1C,MAAO,CADS,EAAO,MAAM,EAAI,SAChB,GAAe,CAAC,CAAC,KAAK,IAAI,CAG5C,SAAS,GAAgB,CACxB,OAAO,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,UAAU,EAAG,EAAE,CCgBlD,SAAgB,GAAoC,CACnD,IAAM,EAAuB,GAAyB,CAEtD,OAAO,MAAe,EAAuB,OAAS,SAAW,CAAC,EAAqB,CAAC,CCsCzF,SAAS,EACR,EACA,CAAE,OAAM,SAAQ,SAAQ,OAAO,GAAO,UAAU,IAA4B,EAAE,CACpE,CACV,GAAM,CAAC,EAAU,GAAa,EAAS,EAAQ,CA6B/C,OA3BA,MAAgB,CACf,GAAI,CAAC,EAAI,SAAY,GAAQ,EAC5B,OAGD,SAAS,GAAU,CAElB,OADA,EAAU,GAAK,CACR,EAAO,IAAA,OAAkB,EAAU,GAAM,CAGjD,IAAM,EAAyB,CAC9B,KAAO,GAAQ,EAAK,SAAY,IAAA,GAChC,SACA,SACA,CAED,OAAO,EAAO,EAAI,QAAS,EAAS,EAAQ,EAS1C,CAAC,EAAM,EAAK,EAAQ,EAAM,EAAO,CAAC,CAE9B,EClGR,SAAS,EAAmB,EAAyB,EAA6C,CACjG,OAAQ,EAAO,KAAf,CACC,IAAK,OACJ,MAAO,CACN,UAAW,CAAC,GAAG,EAAM,UAAW,EAAO,SAAS,CAChD,UAAW,EAAE,CACb,CAEF,IAAK,OAAQ,CACZ,GAAI,EAAM,UAAU,SAAW,EAC9B,OAAO,EAER,IAAM,EAAY,EAAM,UAAU,MAAM,EAAG,GAAG,CAK9C,OAJiB,EAAM,UAAU,EAAM,UAAU,OAAS,KACzC,IAAA,GACT,EAED,CACN,YACA,UAAW,CAAC,GAAG,EAAM,UAAW,EAAO,QAAQ,CAC/C,CAEF,IAAK,OAAQ,CACZ,GAAI,EAAM,UAAU,SAAW,EAC9B,OAAO,EAER,IAAM,EAAY,EAAM,UAAU,MAAM,EAAG,GAAG,CAK9C,OAJa,EAAM,UAAU,EAAM,UAAU,OAAS,KACzC,IAAA,GACL,EAED,CACN,UAAW,CAAC,GAAG,EAAM,UAAW,EAAO,QAAQ,CAC/C,YACA,GA8CJ,SAAS,GAAuC,CAC/C,GAAM,CAAC,EAAO,GAAY,EAAW,EAAoB,CACxD,UAAW,EAAE,CACb,UAAW,EAAE,CACb,CAAC,CAEI,EAAO,EAAa,GAAgB,CACzC,EAAS,CAAE,KAAM,OAAQ,WAAU,CAAC,EAClC,EAAE,CAAC,CAEA,EAAO,EACX,GAA8B,CAC9B,IAAM,EAAW,EAAM,UAAU,EAAM,UAAU,OAAS,GACtD,OAAa,IAAA,GAIjB,OADA,EAAS,CAAE,KAAM,OAAQ,UAAS,CAAC,CAC5B,GAER,CAAC,EAAM,UAAU,CACjB,CAEK,EAAO,EACX,GAA8B,CAC9B,IAAM,EAAO,EAAM,UAAU,EAAM,UAAU,OAAS,GAClD,OAAS,IAAA,GAIb,OADA,EAAS,CAAE,KAAM,OAAQ,UAAS,CAAC,CAC5B,GAER,CAAC,EAAM,UAAU,CACjB,CAED,OAAO,OACC,CACN,QAAS,EAAM,UAAU,OAAS,EAClC,QAAS,EAAM,UAAU,OAAS,EAClC,OACA,OACA,OACA,EACD,CAAC,EAAM,UAAU,OAAQ,EAAM,UAAU,OAAQ,EAAM,EAAM,EAAK,CAClE"}
1
+ {"version":3,"file":"hooks.js","names":[],"sources":["../src/hooks/use-breakpoint.tsx","../src/hooks/use-callback-ref.tsx","../src/hooks/use-debounced-callback.tsx","../src/hooks/use-isomorphic-layout-effect.tsx","../src/hooks/use-random-stable-id.tsx","../src/hooks/use-scroll-behavior.tsx","../src/hooks/use-in-view.tsx","../src/hooks/use-undo-redo.tsx"],"sourcesContent":["import { useSyncExternalStore } from \"react\";\n\n/**\n * Tailwind CSS breakpoints in descending order (largest → smallest).\n *\n * These correspond to Tailwind’s default `theme.screens` config and are used\n * to determine the current viewport size.\n *\n * @see https://tailwindcss.com/docs/screens\n *\n * @example\n * \"2xl\" // ≥96rem (1536px)\n * \"xl\" // ≥80rem (1280px)\n * \"lg\" // ≥64rem (1024px)\n * \"md\" // ≥48rem (768px)\n * \"sm\" // ≥40rem (640px)\n * \"xs\" // ≥30rem (480px)\n * \"2xs\" // ≥22.5rem (360px)\n */\nconst tailwindBreakpoints = [\"2xl\", \"xl\", \"lg\", \"md\", \"sm\", \"xs\", \"2xs\"] as const;\n\n/**\n * A valid Tailwind CSS breakpoint identifier.\n *\n * @example\n * const bp: TailwindBreakpoint = \"md\"; // ≥48rem (768px)\n *\n * @example\n * \"2xl\" // ≥96rem (1536px)\n * \"xl\" // ≥80rem (1280px)\n * \"lg\" // ≥64rem (1024px)\n * \"md\" // ≥48rem (768px)\n * \"sm\" // ≥40rem (640px)\n * \"xs\" // ≥30rem (480px)\n * \"2xs\" // ≥22.5rem (360px)\n */\ntype TailwindBreakpoint = (typeof tailwindBreakpoints)[number];\n\n/**\n * Mantle’s breakpoint set, extending Tailwind’s with `\"default\"`.\n *\n * `\"default\"` represents the base (0px and up) viewport,\n * useful for defining fallbacks or mobile-first styles.\n *\n * @example\n * \"default\" // ≥0rem (0px)\n * \"2xs\" // ≥22.5rem (360px)\n * \"xs\" // ≥30rem (480px)\n * \"sm\" // ≥40rem (640px)\n * \"md\" // ≥48rem (768px)\n * \"lg\" // ≥64rem (1024px)\n * \"xl\" // ≥80rem (1280px)\n * \"2xl\" // ≥96rem (1536px)\n */\nconst breakpoints = [\"default\", ...tailwindBreakpoints] as const;\n\n/**\n * A valid Mantle breakpoint identifier.\n *\n * Includes Tailwind’s standard breakpoints plus `\"default\"` for 0px+.\n *\n * @example\n * const bp: Breakpoint = \"default\"; // ≥0px\n *\n * @example\n * \"default\" // ≥0rem (0px)\n * \"2xs\" // ≥22.5rem (360px)\n * \"xs\" // ≥30rem (480px)\n * \"sm\" // ≥40rem (640px)\n * \"md\" // ≥48rem (768px)\n * \"lg\" // ≥64rem (1024px)\n * \"xl\" // ≥80rem (1280px)\n * \"2xl\" // ≥96rem (1536px)\n */\ntype Breakpoint = (typeof breakpoints)[number];\n\n/**\n * React hook that returns the current breakpoint based on the viewport width.\n *\n * Uses a singleton subscription to a set of min-width media queries and returns\n * the largest matching breakpoint. Designed for React 18+ with\n * `useSyncExternalStore`.\n *\n * @returns {Breakpoint} The current breakpoint that matches the viewport width.\n *\n * @example\n * const breakpoint = useBreakpoint();\n * if (breakpoint === \"lg\") {\n * // Do something for large screens and above\n * }\n */\nfunction useBreakpoint(): Breakpoint {\n\treturn useSyncExternalStore(\n\t\tsubscribeToBreakpointChanges,\n\t\tgetCurrentBreakpointSnapshot,\n\t\t() => \"default\", // SSR fallback\n\t);\n}\n\n/**\n * React hook that returns true if the current viewport width is below the specified breakpoint.\n *\n * This hook uses `window.matchMedia` with a max-width media query and leverages\n * `useSyncExternalStore` to stay compliant with React's concurrent rendering model.\n *\n * @param {TailwindBreakpoint} breakpoint - The breakpoint to check against (e.g., \"md\", \"lg\").\n *\n * @returns {boolean} `true` if the viewport width is below the breakpoint, otherwise `false`.\n *\n * @example\n * // Check if viewport is below medium (768px)\n * const isBelowMd = useIsBelowBreakpoint(\"md\");\n */\nfunction useIsBelowBreakpoint(breakpoint: TailwindBreakpoint): boolean {\n\treturn useSyncExternalStore(\n\t\tcreateBelowBreakpointSubscribe(breakpoint),\n\t\tcreateBelowBreakpointGetSnapshot(breakpoint),\n\t\t() => false, // SSR fallback - assume desktop\n\t);\n}\n\nexport {\n\t//,\n\tbreakpoints,\n\tuseBreakpoint,\n\tuseIsBelowBreakpoint,\n};\n\nexport type {\n\t//,\n\tBreakpoint,\n\tTailwindBreakpoint,\n};\n\n/**\n * A CSS media query string representing a minimum width in `rem` units.\n *\n * @example\n * const query: MinWidthQuery = \"(min-width: 48rem)\";\n *\n * @private\n */\ntype MinWidthQuery = `(min-width: ${number}rem)`;\n\n/**\n * A CSS media query string representing a maximum width in `rem` units.\n *\n * @example\n * const query: MaxWidthQuery = \"(max-width: 47.99rem)\";\n *\n * @private\n */\ntype MaxWidthQuery = `(max-width: ${number}rem)`;\n\n/**\n * Precomputed min-width media query strings for each Tailwind breakpoint.\n *\n * Using constants avoids template string work in hot paths and ensures type\n * safety against the `MinWidthQuery` template literal type.\n *\n * @remarks\n * These are expressed in `rem`. If your CSS breakpoints are in `px`, consider\n * aligning units to avoid JS/CSS drift when `html{font-size}` changes.\n *\n * @private\n */\nconst breakpointQueries = {\n\t\"2xl\": \"(min-width: 96rem)\" as const,\n\txl: \"(min-width: 80rem)\" as const,\n\tlg: \"(min-width: 64rem)\" as const,\n\tmd: \"(min-width: 48rem)\" as const,\n\tsm: \"(min-width: 40rem)\" as const,\n\txs: \"(min-width: 30rem)\" as const,\n\t\"2xs\": \"(min-width: 22.5rem)\" as const,\n} as const satisfies Record<TailwindBreakpoint, MinWidthQuery>;\n\n/**\n * Precomputed max-width media query strings used by `useIsBelowBreakpoint`.\n *\n * The `-0.01rem` offset avoids overlap at exact boundaries.\n *\n * @private\n */\nconst belowBreakpointQueries = {\n\t\"2xl\": \"(max-width: 95.99rem)\" as const, // 96 - 0.01\n\txl: \"(max-width: 79.99rem)\" as const, // 80 - 0.01\n\tlg: \"(max-width: 63.99rem)\" as const, // 64 - 0.01\n\tmd: \"(max-width: 47.99rem)\" as const, // 48 - 0.01\n\tsm: \"(max-width: 39.99rem)\" as const, // 40 - 0.01\n\txs: \"(max-width: 29.99rem)\" as const, // 30 - 0.01\n\t\"2xs\": \"(max-width: 22.49rem)\" as const, // 22.5 - 0.01\n} as const satisfies Record<TailwindBreakpoint, MaxWidthQuery>;\n\n/**\n * Lazily-initialized cache of `MediaQueryList` objects for min-width queries.\n *\n * Initialized on first access to remain SSR-safe (no `window` at import time).\n *\n * @private\n */\nlet minWidthMQLs: Record<TailwindBreakpoint, MediaQueryList> | null = null;\n\n/**\n * Lazily-initialized cache of `MediaQueryList` objects for max-width queries.\n *\n * Used by `useIsBelowBreakpoint`. Also SSR-safe by lazy access.\n *\n * @private\n */\nlet maxWidthMQLs: Record<TailwindBreakpoint, MediaQueryList> | null = null;\n\n/**\n * Get (and lazily create) the cached `MediaQueryList` objects for min-width queries.\n *\n * @returns A record of `MediaQueryList` keyed by Tailwind breakpoint.\n * @private\n */\nfunction getMinWidthMQLs(): Record<TailwindBreakpoint, MediaQueryList> {\n\tif (!minWidthMQLs) {\n\t\tminWidthMQLs = {\n\t\t\t\"2xl\": window.matchMedia(breakpointQueries[\"2xl\"]),\n\t\t\txl: window.matchMedia(breakpointQueries.xl),\n\t\t\tlg: window.matchMedia(breakpointQueries.lg),\n\t\t\tmd: window.matchMedia(breakpointQueries.md),\n\t\t\tsm: window.matchMedia(breakpointQueries.sm),\n\t\t\txs: window.matchMedia(breakpointQueries.xs),\n\t\t\t\"2xs\": window.matchMedia(breakpointQueries[\"2xs\"]),\n\t\t};\n\t}\n\treturn minWidthMQLs;\n}\n\n/**\n * Get (and lazily create) the cached `MediaQueryList` for a specific max-width breakpoint.\n *\n * @param breakpoint - Tailwind breakpoint identifier (e.g., \"md\").\n * @returns The corresponding `MediaQueryList`.\n * @private\n */\nfunction getMaxWidthMQL(breakpoint: TailwindBreakpoint): MediaQueryList {\n\tif (!maxWidthMQLs) {\n\t\tmaxWidthMQLs = {\n\t\t\t\"2xl\": window.matchMedia(belowBreakpointQueries[\"2xl\"]),\n\t\t\txl: window.matchMedia(belowBreakpointQueries.xl),\n\t\t\tlg: window.matchMedia(belowBreakpointQueries.lg),\n\t\t\tmd: window.matchMedia(belowBreakpointQueries.md),\n\t\t\tsm: window.matchMedia(belowBreakpointQueries.sm),\n\t\t\txs: window.matchMedia(belowBreakpointQueries.xs),\n\t\t\t\"2xs\": window.matchMedia(belowBreakpointQueries[\"2xs\"]),\n\t\t};\n\t}\n\treturn maxWidthMQLs[breakpoint];\n}\n\n/**\n * Current breakpoint value used by the singleton store backing `useBreakpoint`.\n *\n * Initialized to `\"default\"` and updated on media-query change events.\n *\n * @private\n */\nlet currentBreakpointValue: Breakpoint = \"default\";\n\n/**\n * Set of component listeners subscribed to the singleton breakpoint store.\n *\n * Each listener is invoked when the current breakpoint value changes.\n *\n * @private\n */\nconst breakpointListeners = new Set<() => void>();\n\n/**\n * Flag indicating whether global media-query listeners are currently attached.\n *\n * Prevents duplicate registrations and enables full teardown when unused.\n *\n * @private\n */\nlet breakpointSubscriptionActive = false;\n\n/**\n * Compute the current breakpoint by checking cached min-width MQLs\n * from largest to smallest.\n *\n * @returns {Breakpoint} The largest matching breakpoint, or `\"default\"`.\n * @private\n */\nfunction getCurrentBreakpoint(): Breakpoint {\n\tconst mqls = getMinWidthMQLs();\n\tfor (const breakpoint of tailwindBreakpoints) {\n\t\tif (mqls[breakpoint].matches) {\n\t\t\treturn breakpoint;\n\t\t}\n\t}\n\treturn \"default\";\n}\n\n/**\n * Update the current breakpoint value and notify all listeners.\n *\n * Uses `requestAnimationFrame` to coalesce rapid resize events and minimize\n * re-renders during active window resizing.\n *\n * @private\n */\nlet breakpointUpdatePending = false;\nfunction updateCurrentBreakpoint() {\n\tif (!breakpointUpdatePending) {\n\t\tbreakpointUpdatePending = true;\n\t\trequestAnimationFrame(() => {\n\t\t\tbreakpointUpdatePending = false;\n\t\t\tconst newBreakpoint = getCurrentBreakpoint();\n\t\t\tif (newBreakpoint !== currentBreakpointValue) {\n\t\t\t\tcurrentBreakpointValue = newBreakpoint;\n\t\t\t\tfor (const listener of breakpointListeners) {\n\t\t\t\t\tlistener();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n}\n\n/**\n * Subscribe a component to breakpoint changes (singleton pattern).\n *\n * Ensures only one set of MQL listeners exists app-wide. Also reconciles the\n * `useSyncExternalStore` initial snapshot/subscribe race by invoking the\n * subscriber once on mount.\n *\n * @param callback - Listener invoked when the breakpoint value may have changed.\n * @returns Cleanup function to unsubscribe the listener.\n * @private\n */\nfunction subscribeToBreakpointChanges(callback: () => void) {\n\tbreakpointListeners.add(callback);\n\n\t// Attach global listeners once\n\tif (!breakpointSubscriptionActive) {\n\t\tbreakpointSubscriptionActive = true;\n\t\tconst mqls = getMinWidthMQLs();\n\n\t\t// Initialize current value synchronously\n\t\tcurrentBreakpointValue = getCurrentBreakpoint();\n\n\t\t// Attach listeners to all breakpoint MQLs\n\t\tfor (const mql of Object.values(mqls)) {\n\t\t\tmql.addEventListener(\"change\", updateCurrentBreakpoint);\n\t\t}\n\t}\n\n\t// Reconcile initial getSnapshot vs subscribe ordering\n\tcallback();\n\n\t// Cleanup\n\treturn () => {\n\t\tbreakpointListeners.delete(callback);\n\n\t\t// Tear down global listeners when no one is listening\n\t\tif (breakpointListeners.size === 0 && breakpointSubscriptionActive) {\n\t\t\tbreakpointSubscriptionActive = false;\n\t\t\tconst mqls = getMinWidthMQLs();\n\t\t\tfor (const mql of Object.values(mqls)) {\n\t\t\t\tmql.removeEventListener(\"change\", updateCurrentBreakpoint);\n\t\t\t}\n\t\t}\n\t};\n}\n\n/**\n * Return the current breakpoint value from the singleton store.\n *\n * Used as the `getSnapshot` for `useSyncExternalStore`.\n *\n * @returns {Breakpoint} The latest computed breakpoint.\n * @private\n */\nfunction getCurrentBreakpointSnapshot(): Breakpoint {\n\treturn currentBreakpointValue;\n}\n\n/**\n * Cached `subscribe` functions keyed by breakpoint.\n *\n * Without caching, `useSyncExternalStore` receives a new function reference on\n * every render, causing it to tear down and re-attach the MQL listener each\n * time — the primary source of resize sluggishness.\n *\n * @private\n */\nconst belowBreakpointSubscribeCache = new Map<\n\tTailwindBreakpoint,\n\t(callback: () => void) => () => void\n>();\n\n/**\n * Get (or create and cache) a `subscribe` function for a specific \"below\" breakpoint.\n *\n * Uses a cached `MediaQueryList` and rAF-throttled change handler to avoid\n * bursty updates during resize.\n *\n * @param breakpoint - Tailwind breakpoint identifier (e.g., \"lg\").\n * @returns A stable `subscribe` function suitable for `useSyncExternalStore`.\n * @private\n */\nfunction createBelowBreakpointSubscribe(breakpoint: TailwindBreakpoint) {\n\tlet cached = belowBreakpointSubscribeCache.get(breakpoint);\n\tif (cached) {\n\t\treturn cached;\n\t}\n\n\tcached = (callback: () => void) => {\n\t\tconst mediaQuery = getMaxWidthMQL(breakpoint);\n\n\t\t// rAF throttle the change callback during active resize\n\t\tlet pending = false;\n\t\tconst onChange = () => {\n\t\t\tif (!pending) {\n\t\t\t\tpending = true;\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tpending = false;\n\t\t\t\t\tcallback();\n\t\t\t\t});\n\t\t\t}\n\t\t};\n\n\t\tmediaQuery.addEventListener(\"change\", onChange);\n\t\treturn () => {\n\t\t\tmediaQuery.removeEventListener(\"change\", onChange);\n\t\t};\n\t};\n\n\tbelowBreakpointSubscribeCache.set(breakpoint, cached);\n\treturn cached;\n}\n\n/**\n * Cached `getSnapshot` functions keyed by breakpoint.\n *\n * Ensures `useSyncExternalStore` receives a referentially stable function,\n * preventing unnecessary subscription churn.\n *\n * @private\n */\nconst belowBreakpointSnapshotCache = new Map<TailwindBreakpoint, () => boolean>();\n\n/**\n * Get (or create and cache) a `getSnapshot` function for a specific \"below\" breakpoint.\n *\n * Uses the cached `MediaQueryList` for the target breakpoint.\n *\n * @param breakpoint - Tailwind breakpoint identifier (e.g., \"lg\").\n * @returns A stable function that returns `true` when the viewport is below the breakpoint.\n * @private\n */\nfunction createBelowBreakpointGetSnapshot(breakpoint: TailwindBreakpoint) {\n\tlet cached = belowBreakpointSnapshotCache.get(breakpoint);\n\tif (cached) {\n\t\treturn cached;\n\t}\n\n\tcached = () => {\n\t\tconst mediaQuery = getMaxWidthMQL(breakpoint);\n\t\treturn mediaQuery.matches;\n\t};\n\n\tbelowBreakpointSnapshotCache.set(breakpoint, cached);\n\treturn cached;\n}\n","import { useEffect, useMemo, useRef } from \"react\";\n\n/**\n * Returns a memoized callback that will always refer to the latest callback\n * passed to the hook.\n *\n * This is useful when you want to pass a callback which may or may not be\n * memoized (have a stable identity) to a child component that will be updated\n * without causing the child component to re-render.\n */\nfunction useCallbackRef<T extends (...args: unknown[]) => unknown>(callback: T | undefined): T {\n\tconst callbackRef = useRef(callback);\n\n\tuseEffect(() => {\n\t\tcallbackRef.current = callback;\n\t});\n\n\treturn useMemo(() => ((...args) => callbackRef.current?.(...args)) as T, []);\n}\n\nexport {\n\t//,\n\tuseCallbackRef,\n};\n","import { useCallback, useEffect, useRef } from \"react\";\nimport { useCallbackRef } from \"./use-callback-ref.js\";\n\ntype Options = {\n\t/**\n\t * The delay in milliseconds to wait before calling the callback.\n\t */\n\twaitMs: number;\n};\n\n/**\n * Create a debounced version of a callback function.\n *\n * It allows you to delay the execution of a function until a certain period of\n * inactivity has passed (the `options.waitMs`), which can be useful for limiting rapid\n * invocations of a function (like in search inputs or button clicks)\n *\n * Note: The debounced callback will always refer to the latest callback passed\n * even without memoization, so it's stable and safe to use in dependency arrays.\n */\nfunction useDebouncedCallback<T extends (...args: unknown[]) => unknown>(\n\tcallbackFn: T,\n\toptions: Options,\n) {\n\tconst stableCallbackFn = useCallbackRef(callbackFn);\n\tconst debounceTimerRef = useRef(0);\n\tuseEffect(() => () => window.clearTimeout(debounceTimerRef.current), []);\n\n\treturn useCallback(\n\t\t(...args: Parameters<T>) => {\n\t\t\twindow.clearTimeout(debounceTimerRef.current);\n\t\t\tdebounceTimerRef.current = window.setTimeout(() => stableCallbackFn(...args), options.waitMs);\n\t\t},\n\t\t[stableCallbackFn, options.waitMs],\n\t);\n}\n\nexport {\n\t//,\n\tuseDebouncedCallback,\n};\n","import { useEffect, useLayoutEffect } from \"react\";\n\n/**\n * useIsomorphicLayoutEffect is a hook that uses useLayoutEffect on the client and useEffect on the server.\n */\nexport const useIsomorphicLayoutEffect =\n\ttypeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n","import { useMemo } from \"react\";\n\n/**\n * Hook to generate a random, stable id.\n * This is similar to `useId`, but generates a stable id client side which can also\n * be used for css selectors and element ids.\n */\nconst useRandomStableId = (prefix = \"mantle\") => useMemo(() => randomStableId(prefix), [prefix]);\n\nexport {\n\t//,\n\tuseRandomStableId,\n};\n\nfunction randomStableId(prefix = \"mantle\") {\n\tconst _prefix = prefix.trim() || \"mantle\";\n\treturn [_prefix, randomPostfix()].join(\"-\");\n}\n\nfunction randomPostfix() {\n\treturn Math.random().toString(36).substring(2, 9);\n}\n","import { useMemo } from \"react\";\nimport { usePrefersReducedMotion } from \"./use-prefers-reduced-motion.js\";\n\n/**\n * `scroll-behavior` values:\n *\n * - `\"auto\"` — scrolling happens instantly (no animation).\n * - `\"smooth\"` — scrolling animates smoothly using a user-agent–defined easing and duration.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior#values\n */\nexport type ScrollBehavior = \"auto\" | \"smooth\";\n\n/**\n * Returns a `ScrollBehavior` that respects the user's motion preference via `usePrefersReducedMotion`.\n *\n * - When `usePrefersReducedMotion()` is `true`, returns `\"auto\"` (no animated scroll).\n * - Otherwise returns `\"smooth\"`.\n *\n * Use this with `window.scrollTo`, `Element.scrollIntoView`, etc. It prevents\n * smooth-scrolling for users who opt out of motion and avoids SSR “first paint”\n * animations thanks to the hook’s conservative server default.\n *\n * @example\n * // Scroll to top\n * const behavior = useScrollBehavior();\n * window.scrollTo({ top: 0, behavior });\n *\n * @example\n * // Bring a section into view\n * const behavior = useScrollBehavior();\n * sectionRef.current?.scrollIntoView({ behavior, block: \"start\" });\n *\n * @see {@link usePrefersReducedMotion}\n * @see CSS `scroll-behavior` property (values: `\"auto\"`, `\"smooth\"`).\n */\nexport function useScrollBehavior(): ScrollBehavior {\n\tconst prefersReducedMotion = usePrefersReducedMotion();\n\n\treturn useMemo(() => (prefersReducedMotion ? \"auto\" : \"smooth\"), [prefersReducedMotion]);\n}\n","\"use client\";\n\nimport { type RefObject, useEffect, useState } from \"react\";\nimport type { InViewOptions, MarginType } from \"../utils/in-view.js\";\nimport { inView } from \"../utils/in-view.js\";\n\n/**\n * Options for the `useInView` hook.\n */\ntype UseInViewOptions = {\n\t/**\n\t * A ref to a scrollable container element to use as the intersection root.\n\t * Defaults to the browser viewport.\n\t */\n\troot?: RefObject<Element | null>;\n\n\t/**\n\t * Expand or contract the detected area from each side of the root's bounding box.\n\t * Uses the same syntax as the CSS `margin` shorthand (e.g. `\"10px\"`, `\"10% 20px\"`).\n\t */\n\tmargin?: MarginType;\n\n\t/**\n\t * How much of the element must be visible before it is considered in view.\n\t * - `\"some\"` (default): Any part of the element is visible.\n\t * - `\"all\"`: The entire element is visible.\n\t * - `number`: An intersection ratio between `0` and `1` (e.g. `0.5` for 50%).\n\t */\n\tamount?: \"some\" | \"all\" | number;\n\n\t/**\n\t * If `true`, stop observing once the element enters the viewport for the\n\t * first time. Useful for one-shot entrance animations.\n\t * Defaults to `false`.\n\t */\n\tonce?: boolean;\n\n\t/**\n\t * The initial visibility state returned before the observer has attached.\n\t * Defaults to `false`.\n\t */\n\tinitial?: boolean;\n};\n\n/**\n * React hook that tracks whether a DOM element is visible within the viewport\n * (or a scrollable container) using the `IntersectionObserver` API.\n *\n * @param ref - A ref attached to the element to observe.\n * @param options - Options controlling the scroll root, margin, threshold,\n * initial state, and one-time detection.\n * @returns `true` if the element is currently in view, otherwise `false`.\n *\n * @example\n * // Basic usage\n * const ref = useRef<HTMLDivElement>(null);\n * const isInView = useInView(ref);\n *\n * return <div ref={ref}>{isInView ? \"Visible!\" : \"Hidden\"}</div>;\n *\n * @example\n * // Trigger once when the element first enters the viewport\n * const ref = useRef<HTMLDivElement>(null);\n * const isInView = useInView(ref, { once: true });\n *\n * return (\n * <div\n * ref={ref}\n * style={{ opacity: isInView ? 1 : 0, transition: \"opacity 0.5s\" }}\n * />\n * );\n *\n * @example\n * // Require 50% of the element to be visible\n * const ref = useRef<HTMLDivElement>(null);\n * const isInView = useInView(ref, { amount: 0.5 });\n */\nfunction useInView(\n\tref: RefObject<Element | null>,\n\t{ root, margin, amount, once = false, initial = false }: UseInViewOptions = {},\n): boolean {\n\tconst [isInView, setInView] = useState(initial);\n\n\tuseEffect(() => {\n\t\tif (!ref.current || (once && isInView)) {\n\t\t\treturn;\n\t\t}\n\n\t\tfunction onEnter() {\n\t\t\tsetInView(true);\n\t\t\treturn once ? undefined : () => setInView(false);\n\t\t}\n\n\t\tconst options: InViewOptions = {\n\t\t\troot: (root && root.current) || undefined,\n\t\t\tmargin,\n\t\t\tamount,\n\t\t};\n\n\t\treturn inView(ref.current, onEnter, options);\n\t\t/**\n\t\t * Intentionally omit `isInView` from deps. The effect must only re-run\n\t\t * when the observation parameters change, not when visibility changes.\n\t\t * Including `isInView` would restart the observer (disconnect + reconnect)\n\t\t * on every enter/leave event, causing wasteful churn for the common\n\t\t * `once=false` case.\n\t\t */\n\t\t// oxlint-disable-next-line react-hooks/exhaustive-deps\n\t}, [root, ref, margin, once, amount]);\n\n\treturn isInView;\n}\n\nexport { useInView };\nexport type { UseInViewOptions };\n","import { useCallback, useMemo, useReducer } from \"react\";\n\ntype UndoRedoState<T> = {\n\tundoStack: T[];\n\tredoStack: T[];\n};\n\ntype UndoRedoAction<T> =\n\t| { type: \"push\"; snapshot: T }\n\t| { type: \"undo\"; current: T }\n\t| { type: \"redo\"; current: T };\n\nfunction undoRedoReducer<T>(state: UndoRedoState<T>, action: UndoRedoAction<T>): UndoRedoState<T> {\n\tswitch (action.type) {\n\t\tcase \"push\": {\n\t\t\treturn {\n\t\t\t\tundoStack: [...state.undoStack, action.snapshot],\n\t\t\t\tredoStack: [],\n\t\t\t};\n\t\t}\n\t\tcase \"undo\": {\n\t\t\tif (state.undoStack.length === 0) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\tconst undoStack = state.undoStack.slice(0, -1);\n\t\t\tconst previous = state.undoStack[state.undoStack.length - 1];\n\t\t\tif (previous === undefined) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tundoStack,\n\t\t\t\tredoStack: [...state.redoStack, action.current],\n\t\t\t};\n\t\t}\n\t\tcase \"redo\": {\n\t\t\tif (state.redoStack.length === 0) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\tconst redoStack = state.redoStack.slice(0, -1);\n\t\t\tconst next = state.redoStack[state.redoStack.length - 1];\n\t\t\tif (next === undefined) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tundoStack: [...state.undoStack, action.current],\n\t\t\t\tredoStack,\n\t\t\t};\n\t\t}\n\t}\n}\n\ntype UseUndoRedoReturn<T> = {\n\t/** Whether there are actions to undo. */\n\tcanUndo: boolean;\n\t/** Whether there are actions to redo. */\n\tcanRedo: boolean;\n\t/** Push a snapshot onto the undo stack. Clears the redo stack. */\n\tpush: (snapshot: T) => void;\n\t/** Pop the last snapshot from the undo stack. Returns `undefined` if empty. */\n\tundo: (current: T) => T | undefined;\n\t/** Pop the last snapshot from the redo stack. Returns `undefined` if empty. */\n\tredo: (current: T) => T | undefined;\n};\n\n/**\n * A generic undo/redo hook backed by a reducer.\n *\n * Maintains two stacks (undo and redo). Call `push` before mutating state\n * to snapshot the current value. Call `undo`/`redo` with the current value\n * to swap it with the previous/next snapshot.\n *\n * @example\n * ```tsx\n * const { push, undo, redo, canUndo, canRedo } = useUndoRedo<string[]>();\n *\n * function removeItem(item: string) {\n * push([...items]); // snapshot before mutation\n * setItems(items.filter((i) => i !== item));\n * }\n *\n * function handleKeyDown(event: KeyboardEvent) {\n * if ((event.metaKey || event.ctrlKey) && event.key === \"z\" && !event.shiftKey) {\n * const previous = undo(items);\n * if (previous) setItems(previous);\n * }\n * if ((event.metaKey || event.ctrlKey) && (event.shiftKey && event.key === \"z\" || event.key === \"y\")) {\n * const next = redo(items);\n * if (next) setItems(next);\n * }\n * }\n * ```\n */\nfunction useUndoRedo<T>(): UseUndoRedoReturn<T> {\n\tconst [state, dispatch] = useReducer(undoRedoReducer<T>, {\n\t\tundoStack: [],\n\t\tredoStack: [],\n\t});\n\n\tconst push = useCallback((snapshot: T) => {\n\t\tdispatch({ type: \"push\", snapshot });\n\t}, []);\n\n\tconst undo = useCallback(\n\t\t(current: T): T | undefined => {\n\t\t\tconst previous = state.undoStack[state.undoStack.length - 1];\n\t\t\tif (previous === undefined) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tdispatch({ type: \"undo\", current });\n\t\t\treturn previous;\n\t\t},\n\t\t[state.undoStack],\n\t);\n\n\tconst redo = useCallback(\n\t\t(current: T): T | undefined => {\n\t\t\tconst next = state.redoStack[state.redoStack.length - 1];\n\t\t\tif (next === undefined) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tdispatch({ type: \"redo\", current });\n\t\t\treturn next;\n\t\t},\n\t\t[state.redoStack],\n\t);\n\n\treturn useMemo(\n\t\t() => ({\n\t\t\tcanUndo: state.undoStack.length > 0,\n\t\t\tcanRedo: state.redoStack.length > 0,\n\t\t\tpush,\n\t\t\tundo,\n\t\t\tredo,\n\t\t}),\n\t\t[state.undoStack.length, state.redoStack.length, push, undo, redo],\n\t);\n}\n\nexport {\n\t//,\n\tuseUndoRedo,\n};\n\nexport type {\n\t//,\n\tUseUndoRedoReturn,\n};\n"],"mappings":"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"}
package/dist/mantle.css CHANGED
@@ -486,6 +486,41 @@ MARK: UTILITIES
486
486
  background-attachment: local, local, scroll, scroll;
487
487
  }
488
488
 
489
+ @utility scroll-shadow-x {
490
+ /* CSS variables default to transparent — data attributes flip them on */
491
+ --_cover-left: transparent;
492
+ --_cover-right: transparent;
493
+ --_shadow-left: transparent;
494
+ --_shadow-right: transparent;
495
+
496
+ background:
497
+ /* Shadow Cover LEFT */
498
+ linear-gradient(to right, var(--_cover-left) 30%, transparent) left center,
499
+ /* Shadow Cover RIGHT */ linear-gradient(to left, var(--_cover-right) 30%, transparent) right
500
+ center,
501
+ /* Shadow LEFT */ radial-gradient(farthest-side at 0% 50%, var(--_shadow-left), rgb(0 0 0 / 0%))
502
+ left center,
503
+ /* Shadow RIGHT */
504
+ radial-gradient(farthest-side at 100% 50%, var(--_shadow-right), rgb(0 0 0 / 0%)) right center;
505
+ background-repeat: no-repeat;
506
+ background-size:
507
+ 40px 100%,
508
+ 40px 100%,
509
+ 14px 100%,
510
+ 14px 100%;
511
+ background-attachment: scroll;
512
+
513
+ &[data-scroll-left] {
514
+ --_cover-left: var(--background-color-base);
515
+ --_shadow-left: var(--navigation-shadow);
516
+ }
517
+
518
+ &[data-scroll-right] {
519
+ --_cover-right: var(--background-color-base);
520
+ --_shadow-right: var(--navigation-shadow);
521
+ }
522
+ }
523
+
489
524
  @utility nav-scroll-shadow {
490
525
  /* Shadow Cover TOP */
491
526
  background:
@@ -1,5 +1,5 @@
1
1
  import { t as IconButton } from "./icon-button-2r6S3HVA.js";
2
- import { t as Button } from "./button-B6StZJsz.js";
2
+ import { t as Button } from "./button-ByK1wG1b.js";
3
3
  import { t as DropdownMenu } from "./dropdown-menu-BEjpuGrT.js";
4
4
  import * as react from "react";
5
5
  import { ComponentProps, ReactNode } from "react";
package/dist/tabs.js CHANGED
@@ -1,2 +1,2 @@
1
- import{t as e}from"./cx-D1HYnpvA.js";import{t}from"./booleanish-CBGdPL3Q.js";import{Children as n,cloneElement as r,createContext as i,forwardRef as a,isValidElement as o,useContext as s}from"react";import c from"clsx";import l from"tiny-invariant";import{Fragment as u,jsx as d,jsxs as f}from"react/jsx-runtime";import{cva as p}from"class-variance-authority";import{Content as m,List as h,Root as g,Trigger as _}from"@radix-ui/react-tabs";const v=i({orientation:`horizontal`,appearance:`classic`}),y=a(({className:t,children:n,orientation:r=`horizontal`,appearance:i=`classic`,...a},o)=>d(g,{className:e(`flex gap-4`,r===`horizontal`?`flex-col`:`flex-row`,t),orientation:r,ref:o,...a,children:d(v.Provider,{value:{orientation:r,appearance:i},children:n})}));y.displayName=`Tabs`;const b=p(`flex border-gray-200`,{variants:{orientation:{horizontal:`flex-row items-center`,vertical:`flex-col items-end gap-3.5 self-stretch`},appearance:{classic:``,pill:``}},compoundVariants:[{orientation:`horizontal`,appearance:`pill`,className:`gap-1`},{orientation:`horizontal`,appearance:`classic`,className:`gap-6 border-b`},{orientation:`vertical`,appearance:`classic`,className:`border-r`}]}),x=a(({className:t,...n},r)=>{let{orientation:i,appearance:a}=s(v);return d(h,{"aria-orientation":i,className:e(b({orientation:i,appearance:a}),t),ref:r,...n})});x.displayName=`TabsList`;const S=p(`absolute z-0`,{variants:{orientation:{horizontal:`-bottom-px left-0 right-0 h-0.75`,vertical:`-right-px bottom-0 top-0 w-0.75`},appearance:{classic:`group-data-state-active/tab-trigger:bg-blue-600`,pill:`hidden`}}}),C=()=>{let{orientation:e,appearance:t}=s(v);return d(`span`,{"aria-hidden":!0,className:c(S({orientation:e,appearance:t}))})};C.displayName=`TabsTriggerDecoration`;const w=p(e(`group/tab-trigger relative flex cursor-pointer items-center gap-1 whitespace-nowrap py-3 text-sm font-medium text-gray-600`,`ring-focus-accent outline-hidden`,`aria-disabled:cursor-default aria-disabled:opacity-50`,`focus-visible:ring-4`,`[&>svg]:shrink-0 [&>svg]:size-5`,`not-aria-disabled:hover:text-gray-900`),{variants:{orientation:{horizontal:`rounded-tl-md rounded-tr-md`,vertical:`rounded-bl-md rounded-tl-md pr-3`},appearance:{classic:e(`not-aria-disabled:hover:data-state-active:text-blue-600`,`data-state-active:text-blue-600`),pill:e(`not-aria-disabled:hover:data-state-active:text-blue-700`,`not-aria-disabled:hover:data-state-active:bg-accent-500/20`,`data-state-active:text-blue-700`,`data-state-active:bg-accent-500/20`,`rounded-full py-2 px-3`)}}}),T=a(({"aria-disabled":i,asChild:a=!1,children:c,className:p,disabled:m,...h},g)=>{let{orientation:y,appearance:b}=s(v),x=t(i??m),S={"aria-disabled":i??m,className:e(w({orientation:y,appearance:b}),p),disabled:x,...h};if(a){let e=n.only(c);l(o(e),"When using `asChild`, TabsTrigger must be passed a single child as a JSX tag.");let t=e.props?.children,i=x?{href:void 0,to:void 0}:{tabIndex:0};return d(_,{asChild:!0,...S,ref:g,children:r(x?d(`button`,{type:`button`}):e,i,f(u,{children:[d(C,{}),t]}))})}return f(_,{ref:g,...S,children:[d(C,{}),c]})});T.displayName=`TabsTrigger`;const E=({className:t,children:n,...r})=>d(`span`,{className:e(`rounded-full bg-gray-500/20 px-1.5 text-xs font-medium text-gray-600`,`group-data-state-active/tab-trigger:bg-blue-500/20 group-data-state-active/tab-trigger:text-blue-700 group-hover/tab-trigger:group-enabled/tab-trigger:group-data-state-active/tab-trigger:text-blue-700`,`group-hover/tab-trigger:group-enabled/tab-trigger:text-gray-700`,t),...r,children:n});E.displayName=`TabBadge`;const D=a(({className:t,...n},r)=>d(m,{ref:r,className:e(`focus-visible:ring-focus-accent outline-hidden focus-visible:ring-4`,t),...n}));D.displayName=`TabsContent`;const O={Root:y,Content:D,List:x,Trigger:T,Badge:E};export{O as Tabs};
1
+ import{t as e}from"./cx-D1HYnpvA.js";import{t}from"./booleanish-CBGdPL3Q.js";import{t as n}from"./compose-refs-DeIsFv68.js";import{t as r}from"./use-prefers-reduced-motion-BiG6QGkf.js";import{Children as i,cloneElement as a,createContext as o,forwardRef as s,isValidElement as c,useContext as l,useEffect as u,useRef as d}from"react";import f from"clsx";import p from"tiny-invariant";import{Fragment as m,jsx as h,jsxs as g}from"react/jsx-runtime";import{cva as _}from"class-variance-authority";import{Content as v,List as y,Root as b,Trigger as x}from"@radix-ui/react-tabs";const S=o({orientation:`horizontal`,appearance:`classic`}),C=s(({className:t,children:n,orientation:r=`horizontal`,appearance:i=`classic`,...a},o)=>h(b,{className:e(`flex gap-4`,r===`horizontal`?`flex-col`:`flex-row`,t),orientation:r,ref:o,...a,children:h(S.Provider,{value:{orientation:r,appearance:i},children:n})}));C.displayName=`Tabs`;const w=_(`flex border-gray-200`,{variants:{orientation:{horizontal:`scroll-shadow-x flex-row items-center overflow-x-auto overscroll-x-none w-full min-w-0 py-1 -my-1 px-1 -mx-1`,vertical:`flex-col items-end gap-3.5 self-stretch`},appearance:{classic:``,pill:``}},compoundVariants:[{orientation:`horizontal`,appearance:`pill`,className:`gap-1`},{orientation:`horizontal`,appearance:`classic`,className:`gap-6 border-b`},{orientation:`vertical`,appearance:`classic`,className:`border-r`}]}),T=s(({className:t,...i},a)=>{let{orientation:o,appearance:s}=l(S),c=d(null);return u(()=>{let e=c.current;if(!e||o!==`horizontal`)return;let t=new AbortController,n=0,i=()=>{e.toggleAttribute(`data-scroll-left`,e.scrollLeft>0),e.toggleAttribute(`data-scroll-right`,Math.ceil(e.scrollLeft)<n-1)},a=()=>{n=e.scrollWidth-e.clientWidth,i()};e.addEventListener(`scroll`,i,{passive:!0,signal:t.signal});let s=new MutationObserver(a);s.observe(e,{childList:!0,subtree:!0}),e.addEventListener(`focusin`,t=>{if(t.target instanceof Element&&t.target!==e){let e=r()?`auto`:`smooth`;t.target.scrollIntoView({behavior:e,inline:`center`,block:`nearest`})}},{signal:t.signal});let l=new ResizeObserver(a);return l.observe(e),a(),()=>{t.abort(),l.disconnect(),s.disconnect()}},[o]),h(y,{"aria-orientation":o,className:e(w({orientation:o,appearance:s}),t),ref:n(c,a),...i})});T.displayName=`TabsList`;const E=_(`absolute z-0`,{variants:{orientation:{horizontal:`-bottom-px left-0 right-0 h-0.75`,vertical:`-right-px bottom-0 top-0 w-0.75`},appearance:{classic:`group-data-state-active/tab-trigger:bg-blue-600`,pill:`hidden`}}}),D=()=>{let{orientation:e,appearance:t}=l(S);return h(`span`,{"aria-hidden":!0,className:f(E({orientation:e,appearance:t}))})};D.displayName=`TabsTriggerDecoration`;const O=_(e(`group/tab-trigger relative flex cursor-pointer items-center gap-1 whitespace-nowrap py-3 text-sm font-medium text-gray-600`,`ring-focus-accent outline-hidden`,`aria-disabled:cursor-default aria-disabled:opacity-50`,`focus-visible:ring-4`,`[&>svg]:shrink-0 [&>svg]:size-5`,`not-aria-disabled:hover:text-gray-900`),{variants:{orientation:{horizontal:`rounded-tl-md rounded-tr-md`,vertical:`rounded-bl-md rounded-tl-md pr-3`},appearance:{classic:e(`not-aria-disabled:hover:data-state-active:text-blue-600`,`data-state-active:text-blue-600`),pill:e(`not-aria-disabled:hover:data-state-active:text-blue-700`,`not-aria-disabled:hover:data-state-active:bg-accent-500/20`,`data-state-active:text-blue-700`,`data-state-active:bg-accent-500/20`,`rounded-full py-2 px-3`)}}}),k=s(({"aria-disabled":n,asChild:r=!1,children:o,className:s,disabled:u,...d},f)=>{let{orientation:_,appearance:v}=l(S),y=t(n??u),b={"aria-disabled":n??u,className:e(O({orientation:_,appearance:v}),s),disabled:y,...d};if(r){let e=i.only(o);p(c(e),"When using `asChild`, TabsTrigger must be passed a single child as a JSX tag.");let t=e.props?.children,n=y?{href:void 0,to:void 0}:{tabIndex:0};return h(x,{asChild:!0,...b,ref:f,children:a(y?h(`button`,{type:`button`}):e,n,g(m,{children:[h(D,{}),t]}))})}return g(x,{ref:f,...b,children:[h(D,{}),o]})});k.displayName=`TabsTrigger`;const A=({className:t,children:n,...r})=>h(`span`,{className:e(`rounded-full bg-gray-500/20 px-1.5 text-xs font-medium text-gray-600`,`group-data-state-active/tab-trigger:bg-blue-500/20 group-data-state-active/tab-trigger:text-blue-700 group-hover/tab-trigger:group-enabled/tab-trigger:group-data-state-active/tab-trigger:text-blue-700`,`group-hover/tab-trigger:group-enabled/tab-trigger:text-gray-700`,t),...r,children:n});A.displayName=`TabBadge`;const j=s(({className:t,...n},r)=>h(v,{ref:r,className:e(`focus-visible:ring-focus-accent outline-hidden focus-visible:ring-4`,t),...n}));j.displayName=`TabsContent`;const M={Root:C,Content:j,List:T,Trigger:k,Badge:A};export{M as Tabs};
2
2
  //# sourceMappingURL=tabs.js.map
package/dist/tabs.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tabs.js","names":["Root","TabsPrimitiveRoot","List","TabsPrimitiveList","clsx","Trigger","TabsPrimitiveTrigger","Content","TabsPrimitiveContent"],"sources":["../src/components/tabs/tabs.tsx"],"sourcesContent":["import {\n\tContent as TabsPrimitiveContent,\n\tList as TabsPrimitiveList,\n\tRoot as TabsPrimitiveRoot,\n\tTrigger as TabsPrimitiveTrigger,\n} from \"@radix-ui/react-tabs\";\nimport { cva } from \"class-variance-authority\";\nimport clsx from \"clsx\";\nimport type { ComponentPropsWithoutRef, ComponentRef, HTMLAttributes } from \"react\";\nimport {\n\tChildren,\n\tcloneElement,\n\tcreateContext,\n\tforwardRef,\n\tisValidElement,\n\tuseContext,\n} from \"react\";\nimport invariant from \"tiny-invariant\";\nimport { parseBooleanish } from \"../../types/booleanish.js\";\nimport { cx } from \"../../utils/cx/cx.js\";\n\ntype Orientation = \"horizontal\" | \"vertical\";\ntype Appearance = \"classic\" | \"pill\";\n\ntype TabsStateContextValue = {\n\torientation: Orientation;\n\tappearance: Appearance;\n};\n\nconst TabsStateContext = createContext<TabsStateContextValue>({\n\torientation: \"horizontal\",\n\tappearance: \"classic\",\n});\n\n/**\n * A set of layered sections of content—known as tab panels—that are displayed one at a time.\n * The root component that provides context for all tab components.\n *\n * @see https://mantle.ngrok.com/components/tabs#tabsroot\n *\n * @example\n * ```tsx\n * <Tabs.Root defaultValue=\"account\">\n * <Tabs.List>\n * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n * </Tabs.List>\n * <Tabs.Content value=\"account\">\n * <p>Make changes to your account here.</p>\n * </Tabs.Content>\n * <Tabs.Content value=\"password\">\n * <p>Change your password here.</p>\n * </Tabs.Content>\n * </Tabs.Root>\n * ```\n */\nconst Root = forwardRef<\n\tComponentRef<typeof TabsPrimitiveRoot>,\n\tComponentPropsWithoutRef<typeof TabsPrimitiveRoot> & {\n\t\t/**\n\t\t * The appearance of the tabs. Classic appearance shows the tab\n\t\t * list with an underline; pill appearance shows each tab as a pill.\n\t\t * @default \"classic\"\n\t\t */\n\t\tappearance?: \"classic\" | \"pill\";\n\t}\n>(({ className, children, orientation = \"horizontal\", appearance = \"classic\", ...props }, ref) => (\n\t<TabsPrimitiveRoot\n\t\tclassName={cx(\"flex gap-4\", orientation === \"horizontal\" ? \"flex-col\" : \"flex-row\", className)}\n\t\torientation={orientation}\n\t\tref={ref}\n\t\t{...props}\n\t>\n\t\t<TabsStateContext.Provider value={{ orientation, appearance }}>\n\t\t\t{children}\n\t\t</TabsStateContext.Provider>\n\t</TabsPrimitiveRoot>\n));\nRoot.displayName = \"Tabs\";\n\n/**\n * Variants for the List component\n */\nconst listVariants = cva(\"flex border-gray-200\", {\n\tvariants: {\n\t\torientation: {\n\t\t\thorizontal: \"flex-row items-center\",\n\t\t\tvertical: \"flex-col items-end gap-3.5 self-stretch\",\n\t\t} as const satisfies Record<Orientation, string>,\n\t\tappearance: {\n\t\t\tclassic: \"\",\n\t\t\tpill: \"\",\n\t\t} as const satisfies Record<Appearance, string>,\n\t},\n\tcompoundVariants: [\n\t\t{\n\t\t\torientation: \"horizontal\",\n\t\t\tappearance: \"pill\",\n\t\t\tclassName: \"gap-1\",\n\t\t},\n\t\t{\n\t\t\torientation: \"horizontal\",\n\t\t\tappearance: \"classic\",\n\t\t\tclassName: \"gap-6 border-b\",\n\t\t},\n\t\t{\n\t\t\torientation: \"vertical\",\n\t\t\tappearance: \"classic\",\n\t\t\tclassName: \"border-r\",\n\t\t},\n\t],\n});\n\n/**\n * Contains the triggers that are aligned along the edge of the active content.\n * The container for tab triggers that provides the visual layout for tab navigation.\n *\n * @see https://mantle.ngrok.com/components/tabs#tabslist\n *\n * @example\n * ```tsx\n * <Tabs.Root defaultValue=\"account\">\n * <Tabs.List>\n * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n * </Tabs.List>\n * <Tabs.Content value=\"account\">\n * <p>Make changes to your account here.</p>\n * </Tabs.Content>\n * </Tabs.Root>\n * ```\n */\nconst List = forwardRef<\n\tComponentRef<typeof TabsPrimitiveList>,\n\tComponentPropsWithoutRef<typeof TabsPrimitiveList>\n>(({ className, ...props }, ref) => {\n\tconst { orientation, appearance } = useContext(TabsStateContext);\n\n\treturn (\n\t\t<TabsPrimitiveList\n\t\t\taria-orientation={orientation}\n\t\t\tclassName={cx(listVariants({ orientation, appearance }), className)}\n\t\t\tref={ref}\n\t\t\t{...props}\n\t\t/>\n\t);\n});\nList.displayName = \"TabsList\";\n\ntype TabsTriggerProps = ComponentPropsWithoutRef<typeof TabsPrimitiveTrigger>;\n\n/**\n * Variants for the TabsTriggerDecoration component\n */\nconst triggerDecorationVariants = cva(\"absolute z-0\", {\n\tvariants: {\n\t\torientation: {\n\t\t\thorizontal: \"-bottom-px left-0 right-0 h-0.75\",\n\t\t\tvertical: \"-right-px bottom-0 top-0 w-0.75\",\n\t\t} as const satisfies Record<Orientation, string>,\n\t\tappearance: {\n\t\t\tclassic: \"group-data-state-active/tab-trigger:bg-blue-600\",\n\t\t\tpill: \"hidden\",\n\t\t} as const satisfies Record<Appearance, string>,\n\t},\n});\n\nconst TabsTriggerDecoration = () => {\n\tconst { orientation, appearance } = useContext(TabsStateContext);\n\n\treturn (\n\t\t<span aria-hidden className={clsx(triggerDecorationVariants({ orientation, appearance }))} />\n\t);\n};\nTabsTriggerDecoration.displayName = \"TabsTriggerDecoration\";\n\n/**\n * Variants for the Trigger component\n */\nconst triggerVariants = cva(\n\tcx(\n\t\t\"group/tab-trigger relative flex cursor-pointer items-center gap-1 whitespace-nowrap py-3 text-sm font-medium text-gray-600\",\n\t\t\"ring-focus-accent outline-hidden\",\n\t\t\"aria-disabled:cursor-default aria-disabled:opacity-50\",\n\t\t\"focus-visible:ring-4\",\n\t\t\"[&>svg]:shrink-0 [&>svg]:size-5\",\n\t\t\"not-aria-disabled:hover:text-gray-900\",\n\t),\n\t{\n\t\tvariants: {\n\t\t\torientation: {\n\t\t\t\thorizontal: \"rounded-tl-md rounded-tr-md\",\n\t\t\t\tvertical: \"rounded-bl-md rounded-tl-md pr-3\",\n\t\t\t} as const satisfies Record<Orientation, string>,\n\t\t\tappearance: {\n\t\t\t\tclassic: cx(\n\t\t\t\t\t\"not-aria-disabled:hover:data-state-active:text-blue-600\",\n\t\t\t\t\t\"data-state-active:text-blue-600\",\n\t\t\t\t),\n\t\t\t\tpill: cx(\n\t\t\t\t\t\"not-aria-disabled:hover:data-state-active:text-blue-700\",\n\t\t\t\t\t\"not-aria-disabled:hover:data-state-active:bg-accent-500/20\",\n\t\t\t\t\t\"data-state-active:text-blue-700\",\n\t\t\t\t\t\"data-state-active:bg-accent-500/20\",\n\t\t\t\t\t\"rounded-full py-2 px-3\",\n\t\t\t\t),\n\t\t\t} as const satisfies Record<Appearance, string>,\n\t\t},\n\t},\n);\n\n/**\n * The button that activates its associated content.\n * A clickable tab trigger that switches between different tab content panels.\n *\n * @see https://mantle.ngrok.com/components/tabs#tabstrigger\n *\n * @example\n * ```tsx\n * <Tabs.Root defaultValue=\"account\">\n * <Tabs.List>\n * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n * </Tabs.List>\n * <Tabs.Content value=\"account\">\n * <p>Make changes to your account here.</p>\n * </Tabs.Content>\n * </Tabs.Root>\n * ```\n */\nconst Trigger = forwardRef<ComponentRef<typeof TabsPrimitiveTrigger>, TabsTriggerProps>(\n\t(\n\t\t{\n\t\t\t\"aria-disabled\": _ariaDisabled,\n\t\t\tasChild = false,\n\t\t\tchildren,\n\t\t\tclassName,\n\t\t\tdisabled: _disabled,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst { orientation, appearance } = useContext(TabsStateContext);\n\t\tconst disabled = parseBooleanish(_ariaDisabled ?? _disabled);\n\n\t\tconst tabsTriggerProps = {\n\t\t\t\"aria-disabled\": _ariaDisabled ?? _disabled,\n\t\t\tclassName: cx(triggerVariants({ orientation, appearance }), className),\n\t\t\tdisabled,\n\t\t\t...props,\n\t\t};\n\n\t\tif (asChild) {\n\t\t\tconst singleChild = Children.only(children);\n\t\t\tinvariant(\n\t\t\t\tisValidElement<TabsTriggerProps>(singleChild),\n\t\t\t\t\"When using `asChild`, TabsTrigger must be passed a single child as a JSX tag.\",\n\t\t\t);\n\t\t\tconst grandchildren = singleChild.props?.children;\n\n\t\t\tconst cloneProps = disabled\n\t\t\t\t? /**\n\t\t\t\t\t * When disabled, prevent anchor/link children from being clickable by\n\t\t\t\t\t * removing their href/to props!\n\t\t\t\t\t * This is necessary because `<a>` doesn't support the `disabled`\n\t\t\t\t\t * attribute and would be navigable. We could use `pointer-events-none`\n\t\t\t\t\t * instead, but don't by default because it would also prevent tooltip\n\t\t\t\t\t * interactions, which may be surprising.\n\t\t\t\t\t */\n\t\t\t\t\t{ href: undefined, to: undefined }\n\t\t\t\t: /**\n\t\t\t\t\t * when NOT disabled, allow keyboard navigation to the trigger,\n\t\t\t\t\t * even for asChild anchors/links\n\t\t\t\t\t */\n\t\t\t\t\t{ tabIndex: 0 };\n\n\t\t\treturn (\n\t\t\t\t<TabsPrimitiveTrigger asChild {...tabsTriggerProps} ref={ref}>\n\t\t\t\t\t{cloneElement(\n\t\t\t\t\t\tdisabled ? <button type=\"button\" /> : singleChild,\n\t\t\t\t\t\tcloneProps,\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<TabsTriggerDecoration />\n\t\t\t\t\t\t\t{grandchildren}\n\t\t\t\t\t\t</>,\n\t\t\t\t\t)}\n\t\t\t\t</TabsPrimitiveTrigger>\n\t\t\t);\n\t\t}\n\n\t\treturn (\n\t\t\t<TabsPrimitiveTrigger ref={ref} {...tabsTriggerProps}>\n\t\t\t\t<TabsTriggerDecoration />\n\t\t\t\t{children}\n\t\t\t</TabsPrimitiveTrigger>\n\t\t);\n\t},\n);\nTrigger.displayName = \"TabsTrigger\";\n\n/**\n * A badge component that can be used inside tab triggers to display additional information.\n * Typically used to show counts or status indicators within tab headers.\n *\n * @see https://mantle.ngrok.com/components/tabs#tabsbadge\n *\n * @example\n * ```tsx\n * <Tabs.Root defaultValue=\"account\">\n * <Tabs.List>\n * <Tabs.Trigger value=\"account\">\n * Account <Tabs.Badge>5</Tabs.Badge>\n * </Tabs.Trigger>\n * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n * </Tabs.List>\n * </Tabs.Root>\n * ```\n */\nconst Badge = ({ className, children, ...props }: HTMLAttributes<HTMLSpanElement>) => (\n\t<span\n\t\tclassName={cx(\n\t\t\t\"rounded-full bg-gray-500/20 px-1.5 text-xs font-medium text-gray-600\",\n\t\t\t\"group-data-state-active/tab-trigger:bg-blue-500/20 group-data-state-active/tab-trigger:text-blue-700 group-hover/tab-trigger:group-enabled/tab-trigger:group-data-state-active/tab-trigger:text-blue-700\",\n\t\t\t\"group-hover/tab-trigger:group-enabled/tab-trigger:text-gray-700\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t>\n\t\t{children}\n\t</span>\n);\nBadge.displayName = \"TabBadge\";\n\n/**\n * Contains the content associated with each trigger.\n * The content panel that displays when its corresponding tab trigger is active.\n *\n * @see https://mantle.ngrok.com/components/tabs#tabscontent\n *\n * @example\n * ```tsx\n * <Tabs.Root defaultValue=\"account\">\n * <Tabs.List>\n * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n * </Tabs.List>\n * <Tabs.Content value=\"account\">\n * <p>Make changes to your account here.</p>\n * </Tabs.Content>\n * <Tabs.Content value=\"password\">\n * <p>Change your password here.</p>\n * </Tabs.Content>\n * </Tabs.Root>\n * ```\n */\nconst Content = forwardRef<\n\tComponentRef<typeof TabsPrimitiveContent>,\n\tComponentPropsWithoutRef<typeof TabsPrimitiveContent>\n>(({ className, ...props }, ref) => (\n\t<TabsPrimitiveContent\n\t\tref={ref}\n\t\tclassName={cx(\"focus-visible:ring-focus-accent outline-hidden focus-visible:ring-4\", className)}\n\t\t{...props}\n\t/>\n));\nContent.displayName = \"TabsContent\";\n\n/**\n * A set of layered sections of content—known as tab panels—that are displayed one at a time.\n * The root component that provides context for all tab components.\n *\n * @see https://mantle.ngrok.com/components/tabs\n *\n * @example\n * ```tsx\n * <Tabs.Root defaultValue=\"account\">\n * <Tabs.List>\n * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n * </Tabs.List>\n * <Tabs.Content value=\"account\">\n * <p>Make changes to your account here.</p>\n * </Tabs.Content>\n * <Tabs.Content value=\"password\">\n * <p>Change your password here.</p>\n * </Tabs.Content>\n * </Tabs.Root>\n * ```\n */\nconst Tabs = {\n\t/**\n\t * The root container of the tabs component that provides context for all tab components.\n\t * A set of layered sections of content—known as tab panels—that are displayed one at a time.\n\t *\n\t * @see https://mantle.ngrok.com/components/tabs#tabsroot\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tabs.Root defaultValue=\"account\">\n\t * <Tabs.List>\n\t * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n\t * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n\t * </Tabs.List>\n\t * <Tabs.Content value=\"account\">\n\t * <p>Make changes to your account here.</p>\n\t * </Tabs.Content>\n\t * </Tabs.Root>\n\t * ```\n\t */\n\tRoot,\n\t/**\n\t * Contains the content associated with each trigger.\n\t * The content panel that displays when its corresponding tab trigger is active.\n\t *\n\t * @see https://mantle.ngrok.com/components/tabs#tabscontent\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tabs.Root defaultValue=\"account\">\n\t * <Tabs.List>\n\t * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n\t * </Tabs.List>\n\t * <Tabs.Content value=\"account\">\n\t * <p>Make changes to your account here.</p>\n\t * </Tabs.Content>\n\t * </Tabs.Root>\n\t * ```\n\t */\n\tContent,\n\t/**\n\t * Contains the triggers that are aligned along the edge of the active content.\n\t * The container for tab triggers that provides the visual layout for tab navigation.\n\t *\n\t * @see https://mantle.ngrok.com/components/tabs#tabslist\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tabs.Root defaultValue=\"account\">\n\t * <Tabs.List>\n\t * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n\t * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n\t * </Tabs.List>\n\t * </Tabs.Root>\n\t * ```\n\t */\n\tList,\n\t/**\n\t * The button that activates its associated content.\n\t * A clickable tab trigger that switches between different tab content panels.\n\t *\n\t * @see https://mantle.ngrok.com/components/tabs#tabstrigger\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tabs.Root defaultValue=\"account\">\n\t * <Tabs.List>\n\t * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n\t * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n\t * </Tabs.List>\n\t * </Tabs.Root>\n\t * ```\n\t */\n\tTrigger,\n\t/**\n\t * A badge component that can be used inside tab triggers to display additional information.\n\t * Typically used to show counts or status indicators within tab headers.\n\t *\n\t * @see https://mantle.ngrok.com/components/tabs#tabsbadge\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tabs.Root defaultValue=\"account\">\n\t * <Tabs.List>\n\t * <Tabs.Trigger value=\"account\">\n\t * Account <Tabs.Badge>5</Tabs.Badge>\n\t * </Tabs.Trigger>\n\t * </Tabs.List>\n\t * </Tabs.Root>\n\t * ```\n\t */\n\tBadge,\n} as const;\n\nexport {\n\t//\n\tTabs,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"tabs.js","names":["Root","TabsPrimitiveRoot","List","TabsPrimitiveList","clsx","Trigger","TabsPrimitiveTrigger","Content","TabsPrimitiveContent"],"sources":["../src/components/tabs/tabs.tsx"],"sourcesContent":["import {\n\tContent as TabsPrimitiveContent,\n\tList as TabsPrimitiveList,\n\tRoot as TabsPrimitiveRoot,\n\tTrigger as TabsPrimitiveTrigger,\n} from \"@radix-ui/react-tabs\";\nimport { cva } from \"class-variance-authority\";\nimport clsx from \"clsx\";\nimport type { ComponentPropsWithoutRef, ComponentRef, HTMLAttributes } from \"react\";\nimport {\n\tChildren,\n\tcloneElement,\n\tcreateContext,\n\tforwardRef,\n\tisValidElement,\n\tuseContext,\n\tuseEffect,\n\tuseRef,\n} from \"react\";\nimport invariant from \"tiny-invariant\";\nimport { parseBooleanish } from \"../../types/booleanish.js\";\nimport { composeRefs } from \"../../utils/compose-refs/compose-refs.js\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { getPrefersReducedMotion } from \"../../hooks/use-prefers-reduced-motion.js\";\nimport type { ScrollBehavior } from \"../../hooks/use-scroll-behavior.js\";\n\ntype Orientation = \"horizontal\" | \"vertical\";\ntype Appearance = \"classic\" | \"pill\";\n\ntype TabsStateContextValue = {\n\torientation: Orientation;\n\tappearance: Appearance;\n};\n\nconst TabsStateContext = createContext<TabsStateContextValue>({\n\torientation: \"horizontal\",\n\tappearance: \"classic\",\n});\n\n/**\n * A set of layered sections of content—known as tab panels—that are displayed one at a time.\n * The root component that provides context for all tab components.\n *\n * @see https://mantle.ngrok.com/components/tabs#tabsroot\n *\n * @example\n * ```tsx\n * <Tabs.Root defaultValue=\"account\">\n * <Tabs.List>\n * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n * </Tabs.List>\n * <Tabs.Content value=\"account\">\n * <p>Make changes to your account here.</p>\n * </Tabs.Content>\n * <Tabs.Content value=\"password\">\n * <p>Change your password here.</p>\n * </Tabs.Content>\n * </Tabs.Root>\n * ```\n */\nconst Root = forwardRef<\n\tComponentRef<typeof TabsPrimitiveRoot>,\n\tComponentPropsWithoutRef<typeof TabsPrimitiveRoot> & {\n\t\t/**\n\t\t * The appearance of the tabs. Classic appearance shows the tab\n\t\t * list with an underline; pill appearance shows each tab as a pill.\n\t\t * @default \"classic\"\n\t\t */\n\t\tappearance?: \"classic\" | \"pill\";\n\t}\n>(({ className, children, orientation = \"horizontal\", appearance = \"classic\", ...props }, ref) => (\n\t<TabsPrimitiveRoot\n\t\tclassName={cx(\"flex gap-4\", orientation === \"horizontal\" ? \"flex-col\" : \"flex-row\", className)}\n\t\torientation={orientation}\n\t\tref={ref}\n\t\t{...props}\n\t>\n\t\t<TabsStateContext.Provider value={{ orientation, appearance }}>\n\t\t\t{children}\n\t\t</TabsStateContext.Provider>\n\t</TabsPrimitiveRoot>\n));\nRoot.displayName = \"Tabs\";\n\n/**\n * Variants for the List component\n */\nconst listVariants = cva(\"flex border-gray-200\", {\n\tvariants: {\n\t\torientation: {\n\t\t\thorizontal:\n\t\t\t\t\"scroll-shadow-x flex-row items-center overflow-x-auto overscroll-x-none w-full min-w-0 py-1 -my-1 px-1 -mx-1\",\n\t\t\tvertical: \"flex-col items-end gap-3.5 self-stretch\",\n\t\t} as const satisfies Record<Orientation, string>,\n\t\tappearance: {\n\t\t\tclassic: \"\",\n\t\t\tpill: \"\",\n\t\t} as const satisfies Record<Appearance, string>,\n\t},\n\tcompoundVariants: [\n\t\t{\n\t\t\torientation: \"horizontal\",\n\t\t\tappearance: \"pill\",\n\t\t\tclassName: \"gap-1\",\n\t\t},\n\t\t{\n\t\t\torientation: \"horizontal\",\n\t\t\tappearance: \"classic\",\n\t\t\tclassName: \"gap-6 border-b\",\n\t\t},\n\t\t{\n\t\t\torientation: \"vertical\",\n\t\t\tappearance: \"classic\",\n\t\t\tclassName: \"border-r\",\n\t\t},\n\t],\n});\n\n/**\n * Contains the triggers that are aligned along the edge of the active content.\n * The container for tab triggers that provides the visual layout for tab navigation.\n *\n * @see https://mantle.ngrok.com/components/tabs#tabslist\n *\n * @example\n * ```tsx\n * <Tabs.Root defaultValue=\"account\">\n * <Tabs.List>\n * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n * </Tabs.List>\n * <Tabs.Content value=\"account\">\n * <p>Make changes to your account here.</p>\n * </Tabs.Content>\n * </Tabs.Root>\n * ```\n */\nconst List = forwardRef<\n\tComponentRef<typeof TabsPrimitiveList>,\n\tComponentPropsWithoutRef<typeof TabsPrimitiveList>\n>(({ className, ...props }, ref) => {\n\tconst { orientation, appearance } = useContext(TabsStateContext);\n\tconst scrollRef = useRef<ComponentRef<typeof TabsPrimitiveList>>(null);\n\n\tuseEffect(() => {\n\t\tconst element = scrollRef.current;\n\t\tif (!element || orientation !== \"horizontal\") {\n\t\t\treturn;\n\t\t}\n\n\t\tconst abortController = new AbortController();\n\t\tlet maxScrollLeft = 0;\n\n\t\tconst updateShadows = () => {\n\t\t\telement.toggleAttribute(\"data-scroll-left\", element.scrollLeft > 0);\n\t\t\telement.toggleAttribute(\n\t\t\t\t\"data-scroll-right\",\n\t\t\t\tMath.ceil(element.scrollLeft) < maxScrollLeft - 1,\n\t\t\t);\n\t\t};\n\n\t\tconst handleResize = () => {\n\t\t\tmaxScrollLeft = element.scrollWidth - element.clientWidth;\n\t\t\tupdateShadows();\n\t\t};\n\n\t\t// passive: true — lets the browser optimize scroll performance by guaranteeing\n\t\t// we won't call preventDefault() inside this handler.\n\t\telement.addEventListener(\"scroll\", updateShadows, {\n\t\t\tpassive: true,\n\t\t\tsignal: abortController.signal,\n\t\t});\n\n\t\t// ResizeObserver alone won't catch scrollWidth changes caused by content\n\t\t// mutations (e.g. font loading, badge count changes) when the list container\n\t\t// itself doesn't resize. MutationObserver covers those cases.\n\t\tconst mutationObserver = new MutationObserver(handleResize);\n\t\tmutationObserver.observe(element, {\n\t\t\tchildList: true,\n\t\t\tsubtree: true, // subtree catches badge/label content changes inside triggers\n\t\t});\n\n\t\t// When Radix moves focus via arrow keys it calls element.focus(), which doesn't\n\t\t// always scroll the target into view inside an overflow container. We handle it\n\t\t// explicitly here via event delegation so every trigger gets this behavior with\n\t\t// a single listener rather than one per trigger.\n\t\telement.addEventListener(\n\t\t\t\"focusin\",\n\t\t\t(event) => {\n\t\t\t\tif (event.target instanceof Element && event.target !== element) {\n\t\t\t\t\tconst scrollBehavior: ScrollBehavior = getPrefersReducedMotion() ? \"auto\" : \"smooth\";\n\t\t\t\t\tevent.target.scrollIntoView({\n\t\t\t\t\t\tbehavior: scrollBehavior,\n\t\t\t\t\t\t// \"center\" rather than \"nearest\" so the focused tab lands in the middle\n\t\t\t\t\t\t// of the visible area, giving the user context on both sides.\n\t\t\t\t\t\tinline: \"center\",\n\t\t\t\t\t\tblock: \"nearest\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ signal: abortController.signal },\n\t\t);\n\t\tconst resizeObserver = new ResizeObserver(handleResize);\n\t\tresizeObserver.observe(element);\n\t\thandleResize();\n\n\t\treturn () => {\n\t\t\tabortController.abort();\n\t\t\tresizeObserver.disconnect();\n\t\t\tmutationObserver.disconnect();\n\t\t};\n\t}, [orientation]);\n\n\treturn (\n\t\t<TabsPrimitiveList\n\t\t\taria-orientation={orientation}\n\t\t\tclassName={cx(listVariants({ orientation, appearance }), className)}\n\t\t\tref={composeRefs(scrollRef, ref)}\n\t\t\t{...props}\n\t\t/>\n\t);\n});\nList.displayName = \"TabsList\";\n\ntype TabsTriggerProps = ComponentPropsWithoutRef<typeof TabsPrimitiveTrigger>;\n\n/**\n * Variants for the TabsTriggerDecoration component\n */\nconst triggerDecorationVariants = cva(\"absolute z-0\", {\n\tvariants: {\n\t\torientation: {\n\t\t\thorizontal: \"-bottom-px left-0 right-0 h-0.75\",\n\t\t\tvertical: \"-right-px bottom-0 top-0 w-0.75\",\n\t\t} as const satisfies Record<Orientation, string>,\n\t\tappearance: {\n\t\t\tclassic: \"group-data-state-active/tab-trigger:bg-blue-600\",\n\t\t\tpill: \"hidden\",\n\t\t} as const satisfies Record<Appearance, string>,\n\t},\n});\n\nconst TabsTriggerDecoration = () => {\n\tconst { orientation, appearance } = useContext(TabsStateContext);\n\n\treturn (\n\t\t<span aria-hidden className={clsx(triggerDecorationVariants({ orientation, appearance }))} />\n\t);\n};\nTabsTriggerDecoration.displayName = \"TabsTriggerDecoration\";\n\n/**\n * Variants for the Trigger component\n */\nconst triggerVariants = cva(\n\tcx(\n\t\t\"group/tab-trigger relative flex cursor-pointer items-center gap-1 whitespace-nowrap py-3 text-sm font-medium text-gray-600\",\n\t\t\"ring-focus-accent outline-hidden\",\n\t\t\"aria-disabled:cursor-default aria-disabled:opacity-50\",\n\t\t\"focus-visible:ring-4\",\n\t\t\"[&>svg]:shrink-0 [&>svg]:size-5\",\n\t\t\"not-aria-disabled:hover:text-gray-900\",\n\t),\n\t{\n\t\tvariants: {\n\t\t\torientation: {\n\t\t\t\thorizontal: \"rounded-tl-md rounded-tr-md\",\n\t\t\t\tvertical: \"rounded-bl-md rounded-tl-md pr-3\",\n\t\t\t} as const satisfies Record<Orientation, string>,\n\t\t\tappearance: {\n\t\t\t\tclassic: cx(\n\t\t\t\t\t\"not-aria-disabled:hover:data-state-active:text-blue-600\",\n\t\t\t\t\t\"data-state-active:text-blue-600\",\n\t\t\t\t),\n\t\t\t\tpill: cx(\n\t\t\t\t\t\"not-aria-disabled:hover:data-state-active:text-blue-700\",\n\t\t\t\t\t\"not-aria-disabled:hover:data-state-active:bg-accent-500/20\",\n\t\t\t\t\t\"data-state-active:text-blue-700\",\n\t\t\t\t\t\"data-state-active:bg-accent-500/20\",\n\t\t\t\t\t\"rounded-full py-2 px-3\",\n\t\t\t\t),\n\t\t\t} as const satisfies Record<Appearance, string>,\n\t\t},\n\t},\n);\n\n/**\n * The button that activates its associated content.\n * A clickable tab trigger that switches between different tab content panels.\n *\n * @see https://mantle.ngrok.com/components/tabs#tabstrigger\n *\n * @example\n * ```tsx\n * <Tabs.Root defaultValue=\"account\">\n * <Tabs.List>\n * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n * </Tabs.List>\n * <Tabs.Content value=\"account\">\n * <p>Make changes to your account here.</p>\n * </Tabs.Content>\n * </Tabs.Root>\n * ```\n */\nconst Trigger = forwardRef<ComponentRef<typeof TabsPrimitiveTrigger>, TabsTriggerProps>(\n\t(\n\t\t{\n\t\t\t\"aria-disabled\": _ariaDisabled,\n\t\t\tasChild = false,\n\t\t\tchildren,\n\t\t\tclassName,\n\t\t\tdisabled: _disabled,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst { orientation, appearance } = useContext(TabsStateContext);\n\t\tconst disabled = parseBooleanish(_ariaDisabled ?? _disabled);\n\n\t\tconst tabsTriggerProps = {\n\t\t\t\"aria-disabled\": _ariaDisabled ?? _disabled,\n\t\t\tclassName: cx(triggerVariants({ orientation, appearance }), className),\n\t\t\tdisabled,\n\t\t\t...props,\n\t\t};\n\n\t\tif (asChild) {\n\t\t\tconst singleChild = Children.only(children);\n\t\t\tinvariant(\n\t\t\t\tisValidElement<TabsTriggerProps>(singleChild),\n\t\t\t\t\"When using `asChild`, TabsTrigger must be passed a single child as a JSX tag.\",\n\t\t\t);\n\t\t\tconst grandchildren = singleChild.props?.children;\n\n\t\t\tconst cloneProps = disabled\n\t\t\t\t? /**\n\t\t\t\t\t * When disabled, prevent anchor/link children from being clickable by\n\t\t\t\t\t * removing their href/to props!\n\t\t\t\t\t * This is necessary because `<a>` doesn't support the `disabled`\n\t\t\t\t\t * attribute and would be navigable. We could use `pointer-events-none`\n\t\t\t\t\t * instead, but don't by default because it would also prevent tooltip\n\t\t\t\t\t * interactions, which may be surprising.\n\t\t\t\t\t */\n\t\t\t\t\t{ href: undefined, to: undefined }\n\t\t\t\t: /**\n\t\t\t\t\t * when NOT disabled, allow keyboard navigation to the trigger,\n\t\t\t\t\t * even for asChild anchors/links\n\t\t\t\t\t */\n\t\t\t\t\t{ tabIndex: 0 };\n\n\t\t\treturn (\n\t\t\t\t<TabsPrimitiveTrigger asChild {...tabsTriggerProps} ref={ref}>\n\t\t\t\t\t{cloneElement(\n\t\t\t\t\t\tdisabled ? <button type=\"button\" /> : singleChild,\n\t\t\t\t\t\tcloneProps,\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<TabsTriggerDecoration />\n\t\t\t\t\t\t\t{grandchildren}\n\t\t\t\t\t\t</>,\n\t\t\t\t\t)}\n\t\t\t\t</TabsPrimitiveTrigger>\n\t\t\t);\n\t\t}\n\n\t\treturn (\n\t\t\t<TabsPrimitiveTrigger ref={ref} {...tabsTriggerProps}>\n\t\t\t\t<TabsTriggerDecoration />\n\t\t\t\t{children}\n\t\t\t</TabsPrimitiveTrigger>\n\t\t);\n\t},\n);\nTrigger.displayName = \"TabsTrigger\";\n\n/**\n * A badge component that can be used inside tab triggers to display additional information.\n * Typically used to show counts or status indicators within tab headers.\n *\n * @see https://mantle.ngrok.com/components/tabs#tabsbadge\n *\n * @example\n * ```tsx\n * <Tabs.Root defaultValue=\"account\">\n * <Tabs.List>\n * <Tabs.Trigger value=\"account\">\n * Account <Tabs.Badge>5</Tabs.Badge>\n * </Tabs.Trigger>\n * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n * </Tabs.List>\n * </Tabs.Root>\n * ```\n */\nconst Badge = ({ className, children, ...props }: HTMLAttributes<HTMLSpanElement>) => (\n\t<span\n\t\tclassName={cx(\n\t\t\t\"rounded-full bg-gray-500/20 px-1.5 text-xs font-medium text-gray-600\",\n\t\t\t\"group-data-state-active/tab-trigger:bg-blue-500/20 group-data-state-active/tab-trigger:text-blue-700 group-hover/tab-trigger:group-enabled/tab-trigger:group-data-state-active/tab-trigger:text-blue-700\",\n\t\t\t\"group-hover/tab-trigger:group-enabled/tab-trigger:text-gray-700\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t>\n\t\t{children}\n\t</span>\n);\nBadge.displayName = \"TabBadge\";\n\n/**\n * Contains the content associated with each trigger.\n * The content panel that displays when its corresponding tab trigger is active.\n *\n * @see https://mantle.ngrok.com/components/tabs#tabscontent\n *\n * @example\n * ```tsx\n * <Tabs.Root defaultValue=\"account\">\n * <Tabs.List>\n * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n * </Tabs.List>\n * <Tabs.Content value=\"account\">\n * <p>Make changes to your account here.</p>\n * </Tabs.Content>\n * <Tabs.Content value=\"password\">\n * <p>Change your password here.</p>\n * </Tabs.Content>\n * </Tabs.Root>\n * ```\n */\nconst Content = forwardRef<\n\tComponentRef<typeof TabsPrimitiveContent>,\n\tComponentPropsWithoutRef<typeof TabsPrimitiveContent>\n>(({ className, ...props }, ref) => (\n\t<TabsPrimitiveContent\n\t\tref={ref}\n\t\tclassName={cx(\"focus-visible:ring-focus-accent outline-hidden focus-visible:ring-4\", className)}\n\t\t{...props}\n\t/>\n));\nContent.displayName = \"TabsContent\";\n\n/**\n * A set of layered sections of content—known as tab panels—that are displayed one at a time.\n * The root component that provides context for all tab components.\n *\n * @see https://mantle.ngrok.com/components/tabs\n *\n * @example\n * ```tsx\n * <Tabs.Root defaultValue=\"account\">\n * <Tabs.List>\n * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n * </Tabs.List>\n * <Tabs.Content value=\"account\">\n * <p>Make changes to your account here.</p>\n * </Tabs.Content>\n * <Tabs.Content value=\"password\">\n * <p>Change your password here.</p>\n * </Tabs.Content>\n * </Tabs.Root>\n * ```\n */\nconst Tabs = {\n\t/**\n\t * The root container of the tabs component that provides context for all tab components.\n\t * A set of layered sections of content—known as tab panels—that are displayed one at a time.\n\t *\n\t * @see https://mantle.ngrok.com/components/tabs#tabsroot\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tabs.Root defaultValue=\"account\">\n\t * <Tabs.List>\n\t * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n\t * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n\t * </Tabs.List>\n\t * <Tabs.Content value=\"account\">\n\t * <p>Make changes to your account here.</p>\n\t * </Tabs.Content>\n\t * </Tabs.Root>\n\t * ```\n\t */\n\tRoot,\n\t/**\n\t * Contains the content associated with each trigger.\n\t * The content panel that displays when its corresponding tab trigger is active.\n\t *\n\t * @see https://mantle.ngrok.com/components/tabs#tabscontent\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tabs.Root defaultValue=\"account\">\n\t * <Tabs.List>\n\t * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n\t * </Tabs.List>\n\t * <Tabs.Content value=\"account\">\n\t * <p>Make changes to your account here.</p>\n\t * </Tabs.Content>\n\t * </Tabs.Root>\n\t * ```\n\t */\n\tContent,\n\t/**\n\t * Contains the triggers that are aligned along the edge of the active content.\n\t * The container for tab triggers that provides the visual layout for tab navigation.\n\t *\n\t * @see https://mantle.ngrok.com/components/tabs#tabslist\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tabs.Root defaultValue=\"account\">\n\t * <Tabs.List>\n\t * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n\t * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n\t * </Tabs.List>\n\t * </Tabs.Root>\n\t * ```\n\t */\n\tList,\n\t/**\n\t * The button that activates its associated content.\n\t * A clickable tab trigger that switches between different tab content panels.\n\t *\n\t * @see https://mantle.ngrok.com/components/tabs#tabstrigger\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tabs.Root defaultValue=\"account\">\n\t * <Tabs.List>\n\t * <Tabs.Trigger value=\"account\">Account</Tabs.Trigger>\n\t * <Tabs.Trigger value=\"password\">Password</Tabs.Trigger>\n\t * </Tabs.List>\n\t * </Tabs.Root>\n\t * ```\n\t */\n\tTrigger,\n\t/**\n\t * A badge component that can be used inside tab triggers to display additional information.\n\t * Typically used to show counts or status indicators within tab headers.\n\t *\n\t * @see https://mantle.ngrok.com/components/tabs#tabsbadge\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tabs.Root defaultValue=\"account\">\n\t * <Tabs.List>\n\t * <Tabs.Trigger value=\"account\">\n\t * Account <Tabs.Badge>5</Tabs.Badge>\n\t * </Tabs.Trigger>\n\t * </Tabs.List>\n\t * </Tabs.Root>\n\t * ```\n\t */\n\tBadge,\n} as const;\n\nexport {\n\t//\n\tTabs,\n};\n"],"mappings":"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"}
package/dist/theme.d.ts CHANGED
@@ -202,14 +202,14 @@ declare function useTheme(): ThemeProviderState;
202
202
  * Read the theme and applied theme from the `<html>` element.
203
203
  */
204
204
  declare function readThemeFromHtmlElement(): {
205
- appliedTheme: "light" | "dark" | "light-high-contrast" | "dark-high-contrast" | undefined;
206
- theme: "system" | "light" | "dark" | "light-high-contrast" | "dark-high-contrast" | undefined;
205
+ appliedTheme: "dark" | "light" | "light-high-contrast" | "dark-high-contrast" | undefined;
206
+ theme: "dark" | "light" | "system" | "light-high-contrast" | "dark-high-contrast" | undefined;
207
207
  };
208
208
  /**
209
209
  * If the theme is "system", it will resolve the theme based on the user's media query preferences, otherwise it will return the theme as is.
210
210
  * This will mirror the result that gets applied to the <html> element.
211
211
  */
212
- declare function useAppliedTheme(): "light" | "dark" | "light-high-contrast" | "dark-high-contrast";
212
+ declare function useAppliedTheme(): "dark" | "light" | "light-high-contrast" | "dark-high-contrast";
213
213
  /**
214
214
  * determineThemeFromMediaQuery returns the theme that should be used based on the user's media query preferences.
215
215
  * @private
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngrok/mantle",
3
- "version": "0.66.16",
3
+ "version": "0.66.17",
4
4
  "description": "mantle is ngrok's UI library and design system.",
5
5
  "homepage": "https://mantle.ngrok.com",
6
6
  "license": "MIT",
@@ -338,7 +338,7 @@
338
338
  "@testing-library/jest-dom": "6.9.1",
339
339
  "@testing-library/react": "16.3.2",
340
340
  "@testing-library/user-event": "14.6.1",
341
- "@tsdown/css": "0.21.2",
341
+ "@tsdown/css": "0.21.3",
342
342
  "@types/prismjs": "1.26.6",
343
343
  "@types/react": "19.2.14",
344
344
  "@types/react-dom": "19.2.3",
@@ -350,7 +350,7 @@
350
350
  "react": "19.2.4",
351
351
  "react-dom": "19.2.4",
352
352
  "tailwindcss": "4.2.1",
353
- "tsdown": "0.21.2",
353
+ "tsdown": "0.21.3",
354
354
  "typescript": "5.9.3",
355
355
  "@cfg/tsconfig": "1.0.0"
356
356
  },