@ngrok/mantle 0.66.15 → 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.
- package/dist/accordion.js +1 -1
- package/dist/alert-dialog.d.ts +4 -4
- package/dist/alert-dialog.js +1 -1
- package/dist/alert.js +1 -1
- package/dist/anchor.js +1 -1
- package/dist/badge.js +1 -1
- package/dist/{button-CKL-3sIr.d.ts → button-ByK1wG1b.d.ts} +4 -4
- package/dist/{button-CRRPesae.js → button-CdPMhyKg.js} +2 -2
- package/dist/{button-CRRPesae.js.map → button-CdPMhyKg.js.map} +1 -1
- package/dist/{button-DA2p0_5F.js → button-eAPtlsYO.js} +2 -2
- package/dist/{button-DA2p0_5F.js.map → button-eAPtlsYO.js.map} +1 -1
- package/dist/button.d.ts +1 -1
- package/dist/button.js +1 -1
- package/dist/calendar.js +1 -1
- package/dist/card.js +1 -1
- package/dist/checkbox.d.ts +1 -1
- package/dist/code-block.d.ts +1 -1
- package/dist/code-block.js +1 -1
- package/dist/code.js +1 -1
- package/dist/combobox.js +1 -1
- package/dist/command.d.ts +7 -7
- package/dist/command.js +1 -1
- package/dist/cx-D1HYnpvA.js +2 -0
- package/dist/cx-D1HYnpvA.js.map +1 -0
- package/dist/cx.js +1 -1
- package/dist/data-table.d.ts +1 -1
- package/dist/data-table.js +1 -1
- package/dist/description-list.js +1 -1
- package/dist/{dialog-nNRT5LpD.js → dialog-DUOIIhuN.js} +2 -2
- package/dist/{dialog-nNRT5LpD.js.map → dialog-DUOIIhuN.js.map} +1 -1
- package/dist/dialog.js +1 -1
- package/dist/{dropdown-menu-CjOaj-Ap.js → dropdown-menu-CUwyTKyu.js} +2 -2
- package/dist/{dropdown-menu-CjOaj-Ap.js.map → dropdown-menu-CUwyTKyu.js.map} +1 -1
- package/dist/dropdown-menu.js +1 -1
- package/dist/flag.js +1 -1
- package/dist/hooks.js.map +1 -1
- package/dist/hover-card.js +1 -1
- package/dist/icon-BMH0fD_b.js +2 -0
- package/dist/{icon-B5oNYYrJ.js.map → icon-BMH0fD_b.js.map} +1 -1
- package/dist/{icon-button-D4e9-dq-.js → icon-button-CeeHZOhh.js} +2 -2
- package/dist/{icon-button-D4e9-dq-.js.map → icon-button-CeeHZOhh.js.map} +1 -1
- package/dist/icon.js +1 -1
- package/dist/icons.js +1 -1
- package/dist/icons.js.map +1 -1
- package/dist/{index-aG53Gdqx.d.ts → index-DWqhfw9n.d.ts} +4 -4
- package/dist/input.d.ts +1 -1
- package/dist/input.js +1 -1
- package/dist/{kbd-pyj32aN4.js → kbd-CtTyMWXB.js} +2 -2
- package/dist/{kbd-pyj32aN4.js.map → kbd-CtTyMWXB.js.map} +1 -1
- package/dist/kbd.js +1 -1
- package/dist/label.js +1 -1
- package/dist/mantle.css +37 -0
- package/dist/media-object.js +1 -1
- package/dist/multi-select.js +1 -1
- package/dist/pagination.js +1 -1
- package/dist/popover.js +1 -1
- package/dist/{primitive-qv3vtV4B.js → primitive-pggbsddf.js} +2 -2
- package/dist/{primitive-qv3vtV4B.js.map → primitive-pggbsddf.js.map} +1 -1
- package/dist/progress.js +1 -1
- package/dist/radio-group.js +1 -1
- package/dist/sandboxed-on-click.js +1 -1
- package/dist/{select-DZ2ztBkI.js → select-BXBu1jP_.js} +2 -2
- package/dist/{select-DZ2ztBkI.js.map → select-BXBu1jP_.js.map} +1 -1
- package/dist/select.js +1 -1
- package/dist/{separator-BcCNbHBg.js → separator-fSV4z0Pq.js} +2 -2
- package/dist/{separator-BcCNbHBg.js.map → separator-fSV4z0Pq.js.map} +1 -1
- package/dist/separator.js +1 -1
- package/dist/sheet.js +1 -1
- package/dist/skeleton.js +1 -1
- package/dist/slider.js +1 -1
- package/dist/{slot-DdnjeV2n.js → slot-D_ZUrdEW.js} +2 -2
- package/dist/{slot-DdnjeV2n.js.map → slot-D_ZUrdEW.js.map} +1 -1
- package/dist/slot.js +1 -1
- package/dist/split-button.d.ts +1 -1
- package/dist/split-button.js +1 -1
- package/dist/{svg-only-BVLlbQ4e.js → svg-only-DnZldAY9.js} +2 -2
- package/dist/{svg-only-BVLlbQ4e.js.map → svg-only-DnZldAY9.js.map} +1 -1
- package/dist/switch.js +1 -1
- package/dist/{table-BWMp12sl.js → table-bSFWy29w.js} +2 -2
- package/dist/{table-BWMp12sl.js.map → table-bSFWy29w.js.map} +1 -1
- package/dist/table.js +1 -1
- package/dist/tabs.js +1 -1
- package/dist/tabs.js.map +1 -1
- package/dist/text-area.js +1 -1
- package/dist/{theme-provider-8xCwja4v.js → theme-provider-BFcnjeME.js} +2 -2
- package/dist/{theme-provider-8xCwja4v.js.map → theme-provider-BFcnjeME.js.map} +1 -1
- package/dist/theme.d.ts +1 -1
- package/dist/theme.js +1 -1
- package/dist/{toast-B28JCGXG.js → toast-BhAyUXKL.js} +2 -2
- package/dist/{toast-B28JCGXG.js.map → toast-BhAyUXKL.js.map} +1 -1
- package/dist/toast.js +1 -1
- package/dist/tooltip.js +1 -1
- package/dist/traffic-policy-file-CroUMbsd.js +2 -0
- package/dist/traffic-policy-file-CroUMbsd.js.map +1 -0
- package/package.json +3 -3
- package/dist/cx-bKromGBh.js +0 -2
- package/dist/cx-bKromGBh.js.map +0 -1
- package/dist/icon-B5oNYYrJ.js +0 -2
- package/dist/traffic-policy-file-QnF-2YkY.js +0 -2
- package/dist/traffic-policy-file-QnF-2YkY.js.map +0 -1
package/dist/tabs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./cx-
|
|
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/text-area.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./cx-
|
|
1
|
+
import{t as e}from"./cx-D1HYnpvA.js";import{t}from"./compose-refs-DeIsFv68.js";import{forwardRef as n,useRef as r,useState as i}from"react";import{jsx as a}from"react/jsx-runtime";const o=n(({appearance:n,"aria-invalid":o,className:s,onDragEnter:c,onDragLeave:l,onDropCapture:u,validation:d,...f},p)=>{let m=o!=null&&o!==`false`?`error`:typeof d==`function`?d():d,h=o??m===`error`,[g,_]=i(!1),v=r(null);return a(`textarea`,{"aria-invalid":h,"data-validation":m||void 0,className:e(n===`monospaced`&&`pointer-coarse:text-[0.9375rem] font-mono text-[0.8125rem]`,`border-input bg-form data-drag-over:border-dashed data-drag-over:ring-4 pointer-coarse:py-[calc(theme(spacing[2.5])-1px)] pointer-coarse:text-base flex min-h-24 w-full rounded-md border px-3 py-[calc(theme(spacing[2])-1px)] focus-visible:outline-hidden focus-visible:ring-4 disabled:pointer-events-none disabled:opacity-50`,`placeholder:text-placeholder data-drag-over:border-dashed`,`border-form text-strong ring-focus-accent focus:border-accent-600 data-drag-over:border-accent-600`,`data-validation-error:border-danger-600 data-validation-error:ring-focus-danger data-validation-error:focus-visible:border-danger-600 data-validation-error:data-drag-over:border-danger-600`,`data-validation-success:border-success-600 data-validation-success:ring-focus-success data-validation-success:focus-visible:border-success-600 data-validation-success:data-drag-over:border-success-600`,`data-validation-warning:border-warning-600 data-validation-warning:ring-focus-warning data-validation-warning:focus-visible:border-warning-600 data-validation-warning:data-drag-over:border-warning-600`,s),"data-drag-over":g,onDragEnter:e=>{_(!0),c?.(e)},onDragLeave:e=>{_(!1),l?.(e)},onDropCapture:e=>{_(!1),v.current?.focus(),u?.(e)},ref:t(v,p),...f})});o.displayName=`TextArea`;export{o as TextArea};
|
|
2
2
|
//# sourceMappingURL=text-area.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./cx-
|
|
2
|
-
//# sourceMappingURL=theme-provider-
|
|
1
|
+
import{t as e}from"./cx-D1HYnpvA.js";import{t}from"./use-matches-media-query-CojcYxlA.js";import{n}from"./browser-only-QPyyfLaB.js";import{createContext as r,useContext as i,useEffect as a,useMemo as o,useRef as s,useState as c}from"react";import l from"tiny-invariant";import{jsx as u}from"react/jsx-runtime";const d=[`light`,`dark`,`light-high-contrast`,`dark-high-contrast`],f=[`system`,...d],p=e=>e;function m(e){return typeof e==`string`?f.includes(e):!1}const h=e=>e;function g(e){return typeof e==`string`?d.includes(e):!1}const _=`(prefers-color-scheme: dark)`,v=`(prefers-contrast: more)`,y=`mantle-ui-theme`,b=`system`,x=r([`system`,()=>null]);function S({children:e}){let[t,r]=c(()=>{let e=N({cookie:n()?document.cookie:null});return w(e),e}),i=s(null);a(()=>{function e(e){let t=e??N({cookie:document.cookie});r(t),w(t)}e();try{`BroadcastChannel`in window&&(i.current=new BroadcastChannel(y),i.current.onmessage=t=>{let n=t?.data?.theme;m(n)&&e(n)})}catch{}function t(t){t.key===`${y}__ping`&&e()}window.addEventListener(`storage`,t);let n=window.matchMedia(_),a=window.matchMedia(v);function o(){e()}function s(){document.visibilityState===`visible`&&e()}return n.addEventListener(`change`,o),a.addEventListener(`change`,o),window.addEventListener(`pageshow`,o),document.addEventListener(`visibilitychange`,s),()=>{window.removeEventListener(`storage`,t),n.removeEventListener(`change`,o),a.removeEventListener(`change`,o),window.removeEventListener(`pageshow`,o),document.removeEventListener(`visibilitychange`,s);try{i.current?.close()}catch{}i.current=null}},[]);let l=o(()=>[t,e=>{L(e),r(e),w(e),F(e,{broadcastChannel:i.current,pingKey:`${y}__ping`})}],[t]);return u(x.Provider,{value:l,children:e})}S.displayName=`ThemeProvider`;function C(){let e=i(x);return l(e,`useTheme must be used within a ThemeProvider`),e}function w(e){if(!n())return;let t=window.document.documentElement,r=window.matchMedia(_).matches,i=window.matchMedia(v).matches,a=E(e,{prefersDarkMode:r,prefersHighContrast:i}),o=t.dataset.theme,s=t.dataset.appliedTheme,c=m(o)?o:void 0,l=g(s)?s:void 0;c===e&&l===a||(t.classList.remove(...d),t.classList.add(a),t.dataset.theme=e,t.dataset.appliedTheme=a)}function T(){if(!n())return{appliedTheme:void 0,theme:void 0};let e=window.document.documentElement,t=m(e.dataset.theme)?e.dataset.theme:void 0;return{appliedTheme:g(e.dataset.appliedTheme)?e.dataset.appliedTheme:void 0,theme:t}}function E(e,{prefersDarkMode:t,prefersHighContrast:n}){return e===`system`?O({prefersDarkMode:t,prefersHighContrast:n}):e}function D(){let e=i(x);return E(e==null?`system`:e[0],{prefersDarkMode:t(_),prefersHighContrast:t(v)})}function O({prefersDarkMode:e,prefersHighContrast:t}){return t?e?`dark-high-contrast`:`light-high-contrast`:e?`dark`:`light`}function k(e){let{storageKey:t,defaultTheme:n,themes:r,resolvedThemes:i,prefersDarkModeMediaQuery:a,prefersHighContrastMediaQuery:o}=e;function s(e){return typeof e==`string`&&r.includes(e)}function c(e){let t=document.cookie;if(!t)return null;try{let n=t.split(`;`).find(t=>t.trim().startsWith(`${e}=`))?.split(`=`)[1];return n?decodeURIComponent(n):null}catch{return null}}function l(e,t){let n=new Date;n.setFullYear(n.getFullYear()+1);let r=window.location.hostname,i=window.location.protocol,a=r===`ngrok.com`||r.endsWith(`.ngrok.com`)?`; domain=.ngrok.com`:``,o=i===`https:`?`; Secure`:``;return`${e}=${encodeURIComponent(t)}; expires=${n.toUTCString()}; path=/${a}; SameSite=Lax${o}`}function u(e,t){try{document.cookie=l(e,t)}catch{}}function d(e,t,n){return e===`system`?n?t?`dark-high-contrast`:`light-high-contrast`:t?`dark`:`light`:e}let f=null,p=null,m=null;try{f=c(t)}catch{}if(s(f))m=f;else{try{p=window.localStorage?.getItem(t)??null}catch{}s(p)&&(m=p)}let h=s(m)?m:n,g=matchMedia(a).matches,_=matchMedia(o).matches,v=d(h,g,_),y=document.documentElement;if(y.dataset.appliedTheme!==v||y.dataset.theme!==h){for(let e of i)y.classList.remove(e);y.classList.add(v),y.dataset.theme=h,y.dataset.appliedTheme=v}let b=s(f);try{if(s(p)&&!b){u(t,p);try{window.localStorage.removeItem(t)}catch{}}else b||u(t,h)}catch{}}function A(){let e={storageKey:y,defaultTheme:b,themes:f,resolvedThemes:d,prefersDarkModeMediaQuery:_,prefersHighContrastMediaQuery:v};return`(${k.toString()})(${JSON.stringify(e)})`}const j=({nonce:e})=>u(`script`,{dangerouslySetInnerHTML:{__html:A()},nonce:e,suppressHydrationWarning:!0});j.displayName=`PreventWrongThemeFlashScript`;function M(t={}){let{className:r=``,ssrCookie:i}=t??{};return o(()=>{let t,a;if(!n())t=N({cookie:i}),a=E(t,{prefersDarkMode:!1,prefersHighContrast:!1});else{let e=window.matchMedia(_).matches,n=window.matchMedia(v).matches;t=N({cookie:document.cookie}),a=E(t,{prefersDarkMode:e,prefersHighContrast:n})}return{className:e(r,a),"data-applied-theme":a,"data-theme":t}},[r,i])}function N({cookie:e}){if(!e)return b;try{let t=e.split(`;`).find(e=>e.trim().startsWith(`${y}=`))?.split(`=`)[1],n=t?globalThis.decodeURIComponent(t):null;return m(n)?n:b}catch{return b}}function P(e){if(e)return e.split(`;`).map(e=>e.trim()).find(e=>e.startsWith(`${y}=`))}function F(e,t){let{broadcastChannel:n,pingKey:r}=t;try{if(n){n.postMessage({theme:e,timestamp:Date.now()});return}}catch{}try{localStorage.setItem(r,JSON.stringify({theme:e,timestamp:Date.now()}))}catch{}}function I(e){let t=new Date;t.setFullYear(t.getFullYear()+1);let{hostname:n,protocol:r}=window.location,i=n===`ngrok.com`||n.endsWith(`.ngrok.com`)?`; domain=.ngrok.com`:``,a=r===`https:`?`; Secure`:``;return`${y}=${encodeURIComponent(e)}; expires=${t.toUTCString()}; path=/${i}; SameSite=Lax${a}`}function L(e){if(n())try{document.cookie=I(e)}catch{}}export{A as a,M as c,p as d,g as f,f as h,N as i,C as l,d as m,S as n,T as o,m as p,P as r,D as s,j as t,h as u};
|
|
2
|
+
//# sourceMappingURL=theme-provider-BFcnjeME.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme-provider-8xCwja4v.js","names":[],"sources":["../src/components/theme/themes.ts","../src/components/theme/theme-provider.tsx"],"sourcesContent":["/**\n * resolvedThemes is a tuple of valid themes that have been resolved from \"system\" to a specific theme.\n */\nconst resolvedThemes = [\"light\", \"dark\", \"light-high-contrast\", \"dark-high-contrast\"] as const;\n\n/**\n * ResolvedTheme is a type that represents a theme that has been resolved from \"system\" to a specific theme.\n */\ntype ResolvedTheme = (typeof resolvedThemes)[number];\n\n/**\n * themes is a tuple of valid themes.\n */\nconst themes = [\"system\", ...resolvedThemes] as const;\n\n/**\n * Theme is a string literal type that represents a valid theme.\n */\ntype Theme = (typeof themes)[number];\n\n/**\n * $theme is a helper which translates the Theme type into a string literal type.\n */\nconst $theme = <T extends Theme = Theme>(value: T) => value;\n\n/**\n * Type predicate that checks if a value is a valid theme.\n */\nfunction isTheme(value: unknown): value is Theme {\n\tif (typeof value !== \"string\") {\n\t\treturn false;\n\t}\n\n\treturn themes.includes(value as Theme);\n}\n\n/**\n * $resolvedTheme is a helper which translates the ResolvedTheme type into a string literal type.\n */\nconst $resolvedTheme = <T extends ResolvedTheme = ResolvedTheme>(value: T) => value;\n\n/**\n * Type predicate that checks if a value is a valid resolved theme.\n */\nfunction isResolvedTheme(value: unknown): value is ResolvedTheme {\n\tif (typeof value !== \"string\") {\n\t\treturn false;\n\t}\n\n\treturn resolvedThemes.includes(value as ResolvedTheme);\n}\n\nexport {\n\t//,\n\tthemes,\n\tresolvedThemes,\n\t$resolvedTheme,\n\t$theme,\n\tisResolvedTheme,\n\tisTheme,\n};\n\nexport type {\n\t//,\n\tTheme,\n\tResolvedTheme,\n};\n","\"use client\";\n\nimport type { PropsWithChildren } from \"react\";\nimport { createContext, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport invariant from \"tiny-invariant\";\nimport { useMatchesMediaQuery } from \"../../hooks/use-matches-media-query.js\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { canUseDOM } from \"../browser-only/browser-only.js\";\nimport {\n\ttype ResolvedTheme,\n\ttype Theme,\n\tisResolvedTheme,\n\tisTheme,\n\tresolvedThemes,\n\tthemes,\n} from \"./themes.js\";\n\n/**\n * prefersDarkModeMediaQuery is the media query used to detect if the user prefers dark mode.\n */\nconst prefersDarkModeMediaQuery = \"(prefers-color-scheme: dark)\";\n\n/**\n * prefersHighContrastMediaQuery is the media query used to detect if the user prefers high contrast mode.\n */\nconst prefersHighContrastMediaQuery = \"(prefers-contrast: more)\";\n\n/**\n * THEME_STORAGE_KEY is the key used to store the theme in cookies.\n */\nconst THEME_STORAGE_KEY = \"mantle-ui-theme\";\n\n/**\n * DEFAULT_THEME is the initial theme to apply if no value is found in storage.\n * {@link themes}\n */\nconst DEFAULT_THEME = \"system\" satisfies Theme;\n\n/**\n * ThemeProviderState is the shape of the state returned by the ThemeProviderContext.\n */\ntype ThemeProviderState = [theme: Theme, setTheme: (theme: Theme) => void];\n\n/**\n * Initial state for the ThemeProviderContext.\n */\nconst initialState: ThemeProviderState = [\"system\", () => null];\n\n/**\n * ThemeProviderContext is a React Context that provides the current theme and a function to set the theme.\n */\nconst ThemeProviderContext = createContext<ThemeProviderState | null>(initialState);\n\ntype ThemeProviderProps = PropsWithChildren;\n\n/**\n * ThemeProvider is a React Context Provider that provides the current theme and a function to set the theme.\n *\n * @see https://mantle.ngrok.com/components/theme-provider#themeprovider\n *\n * @example\n * ```tsx\n * <ThemeProvider defaultTheme=\"system\" storageKey=\"app-theme\">\n * <App />\n * </ThemeProvider>\n * ```\n */\nfunction ThemeProvider({ children }: ThemeProviderProps) {\n\t// Init once from cookie and apply immediately to avoid flashes\n\tconst [theme, setTheme] = useState<Theme>(() => {\n\t\tconst storedTheme = getStoredTheme({\n\t\t\tcookie: canUseDOM() ? document.cookie : null,\n\t\t});\n\t\tapplyThemeToHtml(storedTheme);\n\t\treturn storedTheme;\n\t});\n\n\tconst broadcastChannelRef = useRef<BroadcastChannel | null>(null);\n\n\tuseEffect(() => {\n\t\tfunction syncThemeFromCookie(next?: Theme) {\n\t\t\tconst newTheme = next ?? getStoredTheme({ cookie: document.cookie });\n\t\t\tsetTheme(newTheme);\n\t\t\tapplyThemeToHtml(newTheme);\n\t\t}\n\n\t\t// initial sync in case defaultTheme or storageKey changed\n\t\tsyncThemeFromCookie();\n\n\t\t// add cross-tab listeners (prefer broadcast channel, use localStorage as fallback)\n\t\ttry {\n\t\t\tif (\"BroadcastChannel\" in window) {\n\t\t\t\tbroadcastChannelRef.current = new BroadcastChannel(THEME_STORAGE_KEY);\n\t\t\t\tbroadcastChannelRef.current.onmessage = (event) => {\n\t\t\t\t\tconst value: unknown = event?.data?.theme;\n\t\t\t\t\tif (isTheme(value)) {\n\t\t\t\t\t\tsyncThemeFromCookie(value);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {\n\t\t\t// silently swallow errors\n\t\t}\n\n\t\tfunction onStorage(event: StorageEvent) {\n\t\t\tif (event.key === `${THEME_STORAGE_KEY}__ping`) {\n\t\t\t\tsyncThemeFromCookie();\n\t\t\t}\n\t\t}\n\t\twindow.addEventListener(\"storage\", onStorage);\n\n\t\t// add media query listeners for system theme changes\n\t\tconst prefersDarkMql = window.matchMedia(prefersDarkModeMediaQuery);\n\t\tconst prefersHighContrastMql = window.matchMedia(prefersHighContrastMediaQuery);\n\n\t\tfunction onChange() {\n\t\t\tsyncThemeFromCookie();\n\t\t}\n\n\t\tfunction onVisibilityChange() {\n\t\t\tif (document.visibilityState === \"visible\") {\n\t\t\t\tsyncThemeFromCookie();\n\t\t\t}\n\t\t}\n\n\t\tprefersDarkMql.addEventListener(\"change\", onChange);\n\t\tprefersHighContrastMql.addEventListener(\"change\", onChange);\n\n\t\t// pageshow fires on bfcache restore (event.persisted === true) and some restore-from-freeze cases.\n\t\twindow.addEventListener(\"pageshow\", onChange);\n\n\t\t// visibilitychange to handle coming back to a tab\n\t\tdocument.addEventListener(\"visibilitychange\", onVisibilityChange);\n\n\t\t// don't forget to clean up your slop!\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"storage\", onStorage);\n\t\t\tprefersDarkMql.removeEventListener(\"change\", onChange);\n\t\t\tprefersHighContrastMql.removeEventListener(\"change\", onChange);\n\t\t\twindow.removeEventListener(\"pageshow\", onChange);\n\t\t\tdocument.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\n\t\t\ttry {\n\t\t\t\tbroadcastChannelRef.current?.close();\n\t\t\t} catch {\n\t\t\t\t// silently swallow errors\n\t\t\t}\n\t\t\tbroadcastChannelRef.current = null;\n\t\t};\n\t}, []);\n\n\tconst value: ThemeProviderState = useMemo(\n\t\t() => [\n\t\t\ttheme,\n\t\t\t(next: Theme) => {\n\t\t\t\tsetCookie(next);\n\t\t\t\tsetTheme(next);\n\t\t\t\tapplyThemeToHtml(next);\n\t\t\t\tnotifyOtherTabs(next, {\n\t\t\t\t\tbroadcastChannel: broadcastChannelRef.current,\n\t\t\t\t\tpingKey: `${THEME_STORAGE_KEY}__ping`,\n\t\t\t\t});\n\t\t\t},\n\t\t],\n\t\t[theme],\n\t);\n\n\treturn <ThemeProviderContext.Provider value={value}>{children}</ThemeProviderContext.Provider>;\n}\nThemeProvider.displayName = \"ThemeProvider\";\n\n/**\n * useTheme returns the current theme and a function to set the theme.\n *\n * @note This function will throw an error if used outside of a ThemeProvider context tree.\n */\nfunction useTheme() {\n\tconst context = useContext(ThemeProviderContext);\n\n\tinvariant(context, \"useTheme must be used within a ThemeProvider\");\n\n\treturn context;\n}\n\n/**\n * Applies the given theme to the `<html>` element.\n */\nfunction applyThemeToHtml(theme: Theme) {\n\tif (!canUseDOM()) {\n\t\treturn;\n\t}\n\n\tconst html = window.document.documentElement;\n\n\tconst prefersDarkMode = window.matchMedia(prefersDarkModeMediaQuery).matches;\n\tconst prefersHighContrast = window.matchMedia(prefersHighContrastMediaQuery).matches;\n\n\tconst resolvedTheme = resolveTheme(theme, {\n\t\tprefersDarkMode,\n\t\tprefersHighContrast,\n\t});\n\n\tconst htmlTheme = html.dataset.theme;\n\tconst htmlAppliedTheme = html.dataset.appliedTheme;\n\n\tconst currentTheme = isTheme(htmlTheme) ? htmlTheme : undefined;\n\tconst currentResolvedTheme = isResolvedTheme(htmlAppliedTheme) ? htmlAppliedTheme : undefined;\n\n\tif (currentTheme === theme && currentResolvedTheme === resolvedTheme) {\n\t\t// nothing to do: input theme and resolved class already match\n\t\treturn;\n\t}\n\n\t// Clear any stale theme class, then apply the new one\n\thtml.classList.remove(...resolvedThemes); // ✅ remove all potential theme classes\n\thtml.classList.add(resolvedTheme);\n\thtml.dataset.theme = theme;\n\thtml.dataset.appliedTheme = resolvedTheme;\n}\n\n/**\n * Read the theme and applied theme from the `<html>` element.\n */\nfunction readThemeFromHtmlElement() {\n\tif (!canUseDOM()) {\n\t\treturn {\n\t\t\tappliedTheme: undefined,\n\t\t\ttheme: undefined,\n\t\t};\n\t}\n\n\tconst htmlElement = window.document.documentElement;\n\tconst theme = isTheme(htmlElement.dataset.theme) ? htmlElement.dataset.theme : undefined;\n\tconst appliedTheme = isResolvedTheme(htmlElement.dataset.appliedTheme)\n\t\t? htmlElement.dataset.appliedTheme\n\t\t: undefined;\n\n\treturn {\n\t\tappliedTheme,\n\t\ttheme,\n\t};\n}\n\n/**\n * 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.\n * This will mirror the result that gets applied to the <html> element.\n */\nfunction resolveTheme(\n\ttheme: Theme,\n\t{\n\t\tprefersDarkMode,\n\t\tprefersHighContrast,\n\t}: { prefersDarkMode: boolean; prefersHighContrast: boolean },\n) {\n\tif (theme === \"system\") {\n\t\treturn determineThemeFromMediaQuery({\n\t\t\tprefersDarkMode,\n\t\t\tprefersHighContrast,\n\t\t});\n\t}\n\n\treturn theme;\n}\n\n/**\n * 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.\n * This will mirror the result that gets applied to the <html> element.\n */\nfunction useAppliedTheme() {\n\tconst themeContext = useContext(ThemeProviderContext);\n\tconst theme = themeContext != null ? themeContext[0] : \"system\";\n\n\tconst prefersDarkMode = useMatchesMediaQuery(prefersDarkModeMediaQuery);\n\tconst prefersHighContrast = useMatchesMediaQuery(prefersHighContrastMediaQuery);\n\n\treturn resolveTheme(theme, { prefersDarkMode, prefersHighContrast });\n}\n\n/**\n * determineThemeFromMediaQuery returns the theme that should be used based on the user's media query preferences.\n * @private\n *\n * @example\n * ```tsx\n * const theme = determineThemeFromMediaQuery({\n * prefersDarkMode: true,\n * prefersHighContrast: false\n * });\n * // Returns: \"dark\"\n *\n * const themeWithContrast = determineThemeFromMediaQuery({\n * prefersDarkMode: false,\n * prefersHighContrast: true\n * });\n * // Returns: \"light-high-contrast\"\n * ```\n */\nexport function determineThemeFromMediaQuery({\n\tprefersDarkMode,\n\tprefersHighContrast,\n}: {\n\tprefersDarkMode: boolean;\n\tprefersHighContrast: boolean;\n}): ResolvedTheme {\n\tif (prefersHighContrast) {\n\t\treturn prefersDarkMode ? \"dark-high-contrast\" : \"light-high-contrast\";\n\t}\n\n\treturn prefersDarkMode ? \"dark\" : \"light\";\n}\n\n/**\n * Script that runs synchronously to prevent FOUC by applying the correct theme\n * before the page renders. This is the actual function that gets stringified and inlined.\n */\nfunction preventThemeFlash(args: {\n\tstorageKey: string;\n\tdefaultTheme: Theme;\n\tthemes: readonly Theme[];\n\tresolvedThemes: readonly ResolvedTheme[];\n\tprefersDarkModeMediaQuery: string;\n\tprefersHighContrastMediaQuery: string;\n}) {\n\tconst {\n\t\tstorageKey,\n\t\tdefaultTheme,\n\t\tthemes,\n\t\tresolvedThemes,\n\t\tprefersDarkModeMediaQuery,\n\t\tprefersHighContrastMediaQuery,\n\t} = args;\n\n\tfunction isTheme(value: unknown): value is Theme {\n\t\treturn typeof value === \"string\" && themes.includes(value as Theme);\n\t}\n\n\tfunction getThemeFromCookie(name: string): string | null {\n\t\tconst cookie = document.cookie;\n\t\tif (!cookie) {\n\t\t\treturn null;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cookies = cookie.split(\";\");\n\t\t\tconst themeCookie = cookies.find((c) => c.trim().startsWith(`${name}=`));\n\t\t\tconst cookieValue = themeCookie?.split(\"=\")[1];\n\t\t\tconst storedTheme = cookieValue ? decodeURIComponent(cookieValue) : null;\n\t\t\treturn storedTheme;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction buildCookie(name: string, val: string): string {\n\t\tconst expires = new Date();\n\t\texpires.setFullYear(expires.getFullYear() + 1);\n\t\tconst hostname = window.location.hostname;\n\t\tconst protocol = window.location.protocol;\n\t\tconst domainAttribute =\n\t\t\thostname === \"ngrok.com\" || hostname.endsWith(\".ngrok.com\") ? \"; domain=.ngrok.com\" : \"\";\n\t\tconst secureAttribute = protocol === \"https:\" ? \"; Secure\" : \"\";\n\t\treturn `${name}=${encodeURIComponent(val)}; expires=${expires.toUTCString()}; path=/${domainAttribute}; SameSite=Lax${secureAttribute}`;\n\t}\n\n\tfunction writeCookie(name: string, val: string): void {\n\t\ttry {\n\t\t\tdocument.cookie = buildCookie(name, val);\n\t\t} catch {\n\t\t\t// silently swallow errors\n\t\t}\n\t}\n\n\tfunction resolveThemeValue(\n\t\ttheme: Theme,\n\t\tisDark: boolean,\n\t\tisHighContrast: boolean,\n\t): ResolvedTheme {\n\t\tif (theme === \"system\") {\n\t\t\tif (isHighContrast) {\n\t\t\t\treturn isDark ? \"dark-high-contrast\" : \"light-high-contrast\";\n\t\t\t}\n\t\t\treturn isDark ? \"dark\" : \"light\";\n\t\t}\n\t\treturn theme;\n\t}\n\n\t// 1) Read preference: cookie first, fallback to localStorage (migration support)\n\tlet cookieTheme: string | null = null;\n\tlet lsTheme: string | null = null;\n\tlet storedTheme: Theme | null = null;\n\n\ttry {\n\t\tcookieTheme = getThemeFromCookie(storageKey);\n\t} catch {\n\t\t// silently swallow errors\n\t}\n\n\tif (isTheme(cookieTheme)) {\n\t\tstoredTheme = cookieTheme;\n\t} else {\n\t\ttry {\n\t\t\tlsTheme = window.localStorage?.getItem(storageKey) ?? null;\n\t\t} catch {\n\t\t\t// silently swallow errors\n\t\t}\n\t\tif (isTheme(lsTheme)) {\n\t\t\tstoredTheme = lsTheme;\n\t\t}\n\t}\n\n\tconst preference = isTheme(storedTheme) ? storedTheme : defaultTheme;\n\n\t// 2) Resolve theme based on media queries\n\tconst isDark = matchMedia(prefersDarkModeMediaQuery).matches;\n\tconst isHighContrast = matchMedia(prefersHighContrastMediaQuery).matches;\n\tconst resolvedTheme = resolveThemeValue(preference, isDark, isHighContrast);\n\n\tconst html = document.documentElement;\n\t// 3) Apply theme to DOM (same order as applyThemeToHtml)\n\tif (html.dataset.appliedTheme !== resolvedTheme || html.dataset.theme !== preference) {\n\t\t// Remove all theme classes\n\t\tfor (const themeClass of resolvedThemes as readonly string[]) {\n\t\t\thtml.classList.remove(themeClass);\n\t\t}\n\t\t// Add resolved theme class\n\t\thtml.classList.add(resolvedTheme);\n\t\t// Set data attributes\n\t\thtml.dataset.theme = preference;\n\t\thtml.dataset.appliedTheme = resolvedTheme;\n\t}\n\n\t// 4) Handle persistence/migration synchronously to prevent FOUC\n\tconst hadValidCookie = isTheme(cookieTheme);\n\ttry {\n\t\tif (isTheme(lsTheme) && !hadValidCookie) {\n\t\t\t// Migrate from localStorage to cookie\n\t\t\twriteCookie(storageKey, lsTheme);\n\t\t\ttry {\n\t\t\t\twindow.localStorage.removeItem(storageKey);\n\t\t\t} catch {\n\t\t\t\t// silently swallow errors\n\t\t\t}\n\t\t} else if (!hadValidCookie) {\n\t\t\t// Set default cookie if none existed\n\t\t\twriteCookie(storageKey, preference);\n\t\t}\n\t} catch {\n\t\t// silently swallow errors\n\t}\n}\n\n/**\n * preventWrongThemeFlashScriptContent generates a script that prevents the wrong theme from flashing on initial page load.\n * It checks cookies for a stored theme, and if none is found, it sets the default theme.\n * It also applies the correct theme to the `<html>` element based on the user's media query preferences.\n */\nfunction preventWrongThemeFlashScriptContent() {\n\tconst args = {\n\t\tstorageKey: THEME_STORAGE_KEY,\n\t\tdefaultTheme: DEFAULT_THEME,\n\t\tthemes,\n\t\tresolvedThemes,\n\t\tprefersDarkModeMediaQuery,\n\t\tprefersHighContrastMediaQuery,\n\t} as const satisfies Parameters<typeof preventThemeFlash>[0];\n\n\treturn `(${preventThemeFlash.toString()})(${JSON.stringify(args)})`;\n}\n\nexport type PreventWrongThemeFlashScriptProps = {\n\t/**\n\t * An optional CSP nonce to allowlist this inline script. Using this can help\n\t * you to avoid using the CSP `unsafe-inline` directive, which disables\n\t * XSS protection and would allowlist all inline scripts or styles.\n\t *\n\t * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce\n\t */\n\tnonce?: string;\n};\n\n/**\n * Renders an inline script that prevents Flash of Unstyled Content (FOUC) or the\n * wrong theme flashing on first paint.\n *\n * This is the preferred building block for SSR apps. Pair it with\n * {@link preloadFontLink} HTTP `Link` headers in your server entry so font fetches\n * begin before HTML is parsed. For client-only apps without header control, pair\n * it with {@link PreloadFont} elements in `<head>` instead.\n *\n * Place this as early as possible in the `<head>`.\n *\n * @example\n * ```tsx\n * // entry.server.tsx — send font preloads as HTTP headers (preferred for SSR)\n * headers.set(\"Link\", [\n * `<${assetsCdnOrigin}>; rel=preconnect; crossorigin`,\n * preloadFontLink(\"roobert\"),\n * preloadFontLink(\"jetbrains-mono\"),\n * ].join(\", \"));\n *\n * // root.tsx — only the FOUC script in <head>\n * <head>\n * <PreventWrongThemeFlashScript nonce={nonce} />\n * </head>\n * ```\n *\n * @param nonce - Optional CSP nonce to allowlist the inline script under a strict CSP.\n * @returns {JSX.Element} A script tag injected before first paint.\n * @see preloadFontLink\n * @see PreloadFont\n * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce\n */\nconst PreventWrongThemeFlashScript = ({ nonce }: PreventWrongThemeFlashScriptProps) => (\n\t<script\n\t\tdangerouslySetInnerHTML={{\n\t\t\t__html: preventWrongThemeFlashScriptContent(),\n\t\t}}\n\t\tnonce={nonce}\n\t\tsuppressHydrationWarning\n\t/>\n);\nPreventWrongThemeFlashScript.displayName = \"PreventWrongThemeFlashScript\";\n\ntype InitialThemeProps = {\n\tclassName: string;\n\t\"data-applied-theme\": ResolvedTheme;\n\t\"data-theme\": Theme;\n};\n\ntype UseInitialHtmlThemePropsOptions = {\n\tclassName?: string;\n\t/**\n\t * Theme cookie string for SSR theme resolution. Pass only the theme cookie\n\t * pair (via {@link extractThemeCookie}) rather than the full raw `Cookie`\n\t * header to avoid leaking sensitive cookies in serialized loader data.\n\t */\n\tssrCookie?: string;\n};\n\n/**\n * useInitialHtmlThemeProps returns the initial props that should be applied to the <html> element to prevent react hydration errors.\n */\nfunction useInitialHtmlThemeProps(props: UseInitialHtmlThemePropsOptions = {}): InitialThemeProps {\n\tconst { className = \"\", ssrCookie } = props ?? {};\n\n\treturn useMemo(() => {\n\t\tlet initialTheme: Theme;\n\t\tlet resolvedTheme: ResolvedTheme;\n\n\t\tif (!canUseDOM()) {\n\t\t\tinitialTheme = getStoredTheme({ cookie: ssrCookie });\n\t\t\tresolvedTheme = resolveTheme(initialTheme, {\n\t\t\t\t// During SSR we can't detect media queries, so assume light/no high contrast.\n\t\t\t\t// The inline script will correct this before paint for \"system\" theme users.\n\t\t\t\tprefersDarkMode: false,\n\t\t\t\tprefersHighContrast: false,\n\t\t\t});\n\t\t} else {\n\t\t\tconst prefersDarkMode = window.matchMedia(prefersDarkModeMediaQuery).matches;\n\t\t\tconst prefersHighContrast = window.matchMedia(prefersHighContrastMediaQuery).matches;\n\t\t\tinitialTheme = getStoredTheme({ cookie: document.cookie });\n\t\t\tresolvedTheme = resolveTheme(initialTheme, {\n\t\t\t\tprefersDarkMode,\n\t\t\t\tprefersHighContrast,\n\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\tclassName: cx(className, resolvedTheme),\n\t\t\t\"data-applied-theme\": resolvedTheme,\n\t\t\t\"data-theme\": initialTheme,\n\t\t};\n\t}, [className, ssrCookie]);\n}\n\ntype GetStoredThemeOptions = {\n\t/**\n\t * raw Cookie header (SSR) or document.cookie (client)\n\t */\n\tcookie: string | null | undefined;\n};\n\n/**\n * Returns the persisted UI theme from a Cookie header string.\n *\n * Looks for a cookie named by {@link THEME_STORAGE_KEY} and returns its value **iff**\n * it’s a valid `Theme` per `isTheme`. Otherwise, falls back to\n * {@link DEFAULT_THEME}. This function never throws; malformed encodings or\n * missing cookies quietly return the default.\n *\n * @example\n * getStoredTheme({ cookie: `${THEME_STORAGE_KEY}=dark; session=abc` }) // \"dark\"\n * @example\n * getStoredTheme({ cookie: \"\" }) // DEFAULT_THEME\n */\nfunction getStoredTheme({ cookie }: GetStoredThemeOptions): Theme {\n\tif (!cookie) {\n\t\treturn DEFAULT_THEME;\n\t}\n\n\ttry {\n\t\tconst cookies = cookie.split(\";\");\n\t\tconst themeCookie = cookies.find((cookieStr) =>\n\t\t\tcookieStr.trim().startsWith(`${THEME_STORAGE_KEY}=`),\n\t\t);\n\t\tconst cookieValue = themeCookie?.split(\"=\")[1];\n\t\tconst storedTheme = cookieValue ? globalThis.decodeURIComponent(cookieValue) : null;\n\n\t\treturn isTheme(storedTheme) ? storedTheme : DEFAULT_THEME;\n\t} catch {\n\t\treturn DEFAULT_THEME;\n\t}\n}\n\n/**\n * Extract just the mantle theme cookie from a raw `Cookie` header string.\n *\n * Use this in SSR loaders to safely pass the theme cookie to\n * {@link useInitialHtmlThemeProps} without exposing the full `Cookie` header\n * (which may contain HttpOnly/session cookies) in serialized loader data.\n *\n * @example\n * ```ts\n * // app/root.tsx loader\n * export const loader = async ({ request }: Route.LoaderArgs) => {\n * const themeCookie = extractThemeCookie(request.headers.get(\"Cookie\"));\n * return { themeCookie };\n * };\n * ```\n *\n * @param cookieHeader - The raw `Cookie` header string from the request, or null/undefined.\n * @returns The `mantle-ui-theme=<value>` cookie string, or undefined if not found.\n */\nfunction extractThemeCookie(cookieHeader: string | null | undefined): string | undefined {\n\tif (!cookieHeader) {\n\t\treturn undefined;\n\t}\n\n\treturn cookieHeader\n\t\t.split(\";\")\n\t\t.map((part) => part.trim())\n\t\t.find((part) => part.startsWith(`${THEME_STORAGE_KEY}=`));\n}\n\nexport {\n\tPreventWrongThemeFlashScript,\n\tThemeProvider,\n\t//,\n\textractThemeCookie,\n\tgetStoredTheme,\n\tpreventWrongThemeFlashScriptContent,\n\treadThemeFromHtmlElement,\n\tuseAppliedTheme,\n\tuseInitialHtmlThemeProps,\n\tuseTheme,\n};\n\n/**\n * Notifies other open tabs (same origin) that the theme changed.\n *\n * Prefers a shared {@link BroadcastChannel} for immediate, reliable delivery.\n * Falls back to writing a unique “ping” value to `localStorage`, which triggers\n * the cross-tab `storage` event. Both mechanisms only work across the same origin.\n *\n * Uses a timestamp to ensure the storage value always changes so the event fires.\n *\n * @remarks\n * - Same-origin only: BroadcastChannel and the `storage` event do not cross subdomains\n * or different schemes/ports. For cross-subdomain sync, use a postMessage hub or server push.\n * - This function is fire-and-forget and intentionally swallows errors.\n * - Receivers should re-read the cookie/source of truth and then apply the theme;\n * don’t trust the payload blindly.\n *\n * @example\n * // Sender (inside your setter)\n * notifyOtherTabs(nextTheme, {\n * broadcastChannel: broadcastChannelRef.current,\n * pingKey: `${storageKey}__ping`,\n * });\n *\n * @example\n * // Receiver (setup once per tab)\n * const bc = new BroadcastChannel(storageKey);\n * bc.onmessage = () => syncThemeFromCookie();\n * window.addEventListener('storage', (e) => {\n * if (e.key === `${storageKey}__ping`) syncThemeFromCookie();\n * });\n */\nfunction notifyOtherTabs(\n\ttheme: Theme,\n\toptions: {\n\t\tbroadcastChannel: BroadcastChannel | null;\n\t\tpingKey: `${string}__ping`;\n\t},\n) {\n\tconst { broadcastChannel, pingKey } = options;\n\n\t// first try BroadcastChannel\n\ttry {\n\t\tif (broadcastChannel) {\n\t\t\tbroadcastChannel.postMessage({\n\t\t\t\ttheme,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t} catch {\n\t\t// silently swallow errors\n\t}\n\n\t// fallback to storage event: write a \"ping\" key (not the real storageKey)\n\ttry {\n\t\tlocalStorage.setItem(pingKey, JSON.stringify({ theme, timestamp: Date.now() }));\n\t} catch {\n\t\t// silently swallow errors\n\t}\n}\n\nfunction buildThemeCookie(value: string) {\n\tconst expires = new Date();\n\texpires.setFullYear(expires.getFullYear() + 1); // 1 year expiration\n\n\t// Only set .ngrok.com domain for ngrok domains, otherwise let it default to current domain\n\tconst { hostname, protocol } = window.location;\n\tconst domainAttribute =\n\t\thostname === \"ngrok.com\" || hostname.endsWith(\".ngrok.com\") ? \"; domain=.ngrok.com\" : \"\";\n\tconst secureAttribute = protocol === \"https:\" ? \"; Secure\" : \"\";\n\n\treturn `${THEME_STORAGE_KEY}=${encodeURIComponent(value)}; expires=${expires.toUTCString()}; path=/${domainAttribute}; SameSite=Lax${secureAttribute}` as const;\n}\n\n/**\n * Sets a cookie with appropriate domain for the current hostname.\n * Uses .ngrok.com for ngrok domains, otherwise no domain (current domain only).\n */\nfunction setCookie(value: string) {\n\tif (!canUseDOM()) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tdocument.cookie = buildThemeCookie(value);\n\t} catch {\n\t\t// silently swallow errors\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"theme-provider-BFcnjeME.js","names":[],"sources":["../src/components/theme/themes.ts","../src/components/theme/theme-provider.tsx"],"sourcesContent":["/**\n * resolvedThemes is a tuple of valid themes that have been resolved from \"system\" to a specific theme.\n */\nconst resolvedThemes = [\"light\", \"dark\", \"light-high-contrast\", \"dark-high-contrast\"] as const;\n\n/**\n * ResolvedTheme is a type that represents a theme that has been resolved from \"system\" to a specific theme.\n */\ntype ResolvedTheme = (typeof resolvedThemes)[number];\n\n/**\n * themes is a tuple of valid themes.\n */\nconst themes = [\"system\", ...resolvedThemes] as const;\n\n/**\n * Theme is a string literal type that represents a valid theme.\n */\ntype Theme = (typeof themes)[number];\n\n/**\n * $theme is a helper which translates the Theme type into a string literal type.\n */\nconst $theme = <T extends Theme = Theme>(value: T) => value;\n\n/**\n * Type predicate that checks if a value is a valid theme.\n */\nfunction isTheme(value: unknown): value is Theme {\n\tif (typeof value !== \"string\") {\n\t\treturn false;\n\t}\n\n\treturn themes.includes(value as Theme);\n}\n\n/**\n * $resolvedTheme is a helper which translates the ResolvedTheme type into a string literal type.\n */\nconst $resolvedTheme = <T extends ResolvedTheme = ResolvedTheme>(value: T) => value;\n\n/**\n * Type predicate that checks if a value is a valid resolved theme.\n */\nfunction isResolvedTheme(value: unknown): value is ResolvedTheme {\n\tif (typeof value !== \"string\") {\n\t\treturn false;\n\t}\n\n\treturn resolvedThemes.includes(value as ResolvedTheme);\n}\n\nexport {\n\t//,\n\tthemes,\n\tresolvedThemes,\n\t$resolvedTheme,\n\t$theme,\n\tisResolvedTheme,\n\tisTheme,\n};\n\nexport type {\n\t//,\n\tTheme,\n\tResolvedTheme,\n};\n","\"use client\";\n\nimport type { PropsWithChildren } from \"react\";\nimport { createContext, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport invariant from \"tiny-invariant\";\nimport { useMatchesMediaQuery } from \"../../hooks/use-matches-media-query.js\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { canUseDOM } from \"../browser-only/browser-only.js\";\nimport {\n\ttype ResolvedTheme,\n\ttype Theme,\n\tisResolvedTheme,\n\tisTheme,\n\tresolvedThemes,\n\tthemes,\n} from \"./themes.js\";\n\n/**\n * prefersDarkModeMediaQuery is the media query used to detect if the user prefers dark mode.\n */\nconst prefersDarkModeMediaQuery = \"(prefers-color-scheme: dark)\";\n\n/**\n * prefersHighContrastMediaQuery is the media query used to detect if the user prefers high contrast mode.\n */\nconst prefersHighContrastMediaQuery = \"(prefers-contrast: more)\";\n\n/**\n * THEME_STORAGE_KEY is the key used to store the theme in cookies.\n */\nconst THEME_STORAGE_KEY = \"mantle-ui-theme\";\n\n/**\n * DEFAULT_THEME is the initial theme to apply if no value is found in storage.\n * {@link themes}\n */\nconst DEFAULT_THEME = \"system\" satisfies Theme;\n\n/**\n * ThemeProviderState is the shape of the state returned by the ThemeProviderContext.\n */\ntype ThemeProviderState = [theme: Theme, setTheme: (theme: Theme) => void];\n\n/**\n * Initial state for the ThemeProviderContext.\n */\nconst initialState: ThemeProviderState = [\"system\", () => null];\n\n/**\n * ThemeProviderContext is a React Context that provides the current theme and a function to set the theme.\n */\nconst ThemeProviderContext = createContext<ThemeProviderState | null>(initialState);\n\ntype ThemeProviderProps = PropsWithChildren;\n\n/**\n * ThemeProvider is a React Context Provider that provides the current theme and a function to set the theme.\n *\n * @see https://mantle.ngrok.com/components/theme-provider#themeprovider\n *\n * @example\n * ```tsx\n * <ThemeProvider defaultTheme=\"system\" storageKey=\"app-theme\">\n * <App />\n * </ThemeProvider>\n * ```\n */\nfunction ThemeProvider({ children }: ThemeProviderProps) {\n\t// Init once from cookie and apply immediately to avoid flashes\n\tconst [theme, setTheme] = useState<Theme>(() => {\n\t\tconst storedTheme = getStoredTheme({\n\t\t\tcookie: canUseDOM() ? document.cookie : null,\n\t\t});\n\t\tapplyThemeToHtml(storedTheme);\n\t\treturn storedTheme;\n\t});\n\n\tconst broadcastChannelRef = useRef<BroadcastChannel | null>(null);\n\n\tuseEffect(() => {\n\t\tfunction syncThemeFromCookie(next?: Theme) {\n\t\t\tconst newTheme = next ?? getStoredTheme({ cookie: document.cookie });\n\t\t\tsetTheme(newTheme);\n\t\t\tapplyThemeToHtml(newTheme);\n\t\t}\n\n\t\t// initial sync in case defaultTheme or storageKey changed\n\t\tsyncThemeFromCookie();\n\n\t\t// add cross-tab listeners (prefer broadcast channel, use localStorage as fallback)\n\t\ttry {\n\t\t\tif (\"BroadcastChannel\" in window) {\n\t\t\t\tbroadcastChannelRef.current = new BroadcastChannel(THEME_STORAGE_KEY);\n\t\t\t\tbroadcastChannelRef.current.onmessage = (event) => {\n\t\t\t\t\tconst value: unknown = event?.data?.theme;\n\t\t\t\t\tif (isTheme(value)) {\n\t\t\t\t\t\tsyncThemeFromCookie(value);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {\n\t\t\t// silently swallow errors\n\t\t}\n\n\t\tfunction onStorage(event: StorageEvent) {\n\t\t\tif (event.key === `${THEME_STORAGE_KEY}__ping`) {\n\t\t\t\tsyncThemeFromCookie();\n\t\t\t}\n\t\t}\n\t\twindow.addEventListener(\"storage\", onStorage);\n\n\t\t// add media query listeners for system theme changes\n\t\tconst prefersDarkMql = window.matchMedia(prefersDarkModeMediaQuery);\n\t\tconst prefersHighContrastMql = window.matchMedia(prefersHighContrastMediaQuery);\n\n\t\tfunction onChange() {\n\t\t\tsyncThemeFromCookie();\n\t\t}\n\n\t\tfunction onVisibilityChange() {\n\t\t\tif (document.visibilityState === \"visible\") {\n\t\t\t\tsyncThemeFromCookie();\n\t\t\t}\n\t\t}\n\n\t\tprefersDarkMql.addEventListener(\"change\", onChange);\n\t\tprefersHighContrastMql.addEventListener(\"change\", onChange);\n\n\t\t// pageshow fires on bfcache restore (event.persisted === true) and some restore-from-freeze cases.\n\t\twindow.addEventListener(\"pageshow\", onChange);\n\n\t\t// visibilitychange to handle coming back to a tab\n\t\tdocument.addEventListener(\"visibilitychange\", onVisibilityChange);\n\n\t\t// don't forget to clean up your slop!\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"storage\", onStorage);\n\t\t\tprefersDarkMql.removeEventListener(\"change\", onChange);\n\t\t\tprefersHighContrastMql.removeEventListener(\"change\", onChange);\n\t\t\twindow.removeEventListener(\"pageshow\", onChange);\n\t\t\tdocument.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\n\t\t\ttry {\n\t\t\t\tbroadcastChannelRef.current?.close();\n\t\t\t} catch {\n\t\t\t\t// silently swallow errors\n\t\t\t}\n\t\t\tbroadcastChannelRef.current = null;\n\t\t};\n\t}, []);\n\n\tconst value: ThemeProviderState = useMemo(\n\t\t() => [\n\t\t\ttheme,\n\t\t\t(next: Theme) => {\n\t\t\t\tsetCookie(next);\n\t\t\t\tsetTheme(next);\n\t\t\t\tapplyThemeToHtml(next);\n\t\t\t\tnotifyOtherTabs(next, {\n\t\t\t\t\tbroadcastChannel: broadcastChannelRef.current,\n\t\t\t\t\tpingKey: `${THEME_STORAGE_KEY}__ping`,\n\t\t\t\t});\n\t\t\t},\n\t\t],\n\t\t[theme],\n\t);\n\n\treturn <ThemeProviderContext.Provider value={value}>{children}</ThemeProviderContext.Provider>;\n}\nThemeProvider.displayName = \"ThemeProvider\";\n\n/**\n * useTheme returns the current theme and a function to set the theme.\n *\n * @note This function will throw an error if used outside of a ThemeProvider context tree.\n */\nfunction useTheme() {\n\tconst context = useContext(ThemeProviderContext);\n\n\tinvariant(context, \"useTheme must be used within a ThemeProvider\");\n\n\treturn context;\n}\n\n/**\n * Applies the given theme to the `<html>` element.\n */\nfunction applyThemeToHtml(theme: Theme) {\n\tif (!canUseDOM()) {\n\t\treturn;\n\t}\n\n\tconst html = window.document.documentElement;\n\n\tconst prefersDarkMode = window.matchMedia(prefersDarkModeMediaQuery).matches;\n\tconst prefersHighContrast = window.matchMedia(prefersHighContrastMediaQuery).matches;\n\n\tconst resolvedTheme = resolveTheme(theme, {\n\t\tprefersDarkMode,\n\t\tprefersHighContrast,\n\t});\n\n\tconst htmlTheme = html.dataset.theme;\n\tconst htmlAppliedTheme = html.dataset.appliedTheme;\n\n\tconst currentTheme = isTheme(htmlTheme) ? htmlTheme : undefined;\n\tconst currentResolvedTheme = isResolvedTheme(htmlAppliedTheme) ? htmlAppliedTheme : undefined;\n\n\tif (currentTheme === theme && currentResolvedTheme === resolvedTheme) {\n\t\t// nothing to do: input theme and resolved class already match\n\t\treturn;\n\t}\n\n\t// Clear any stale theme class, then apply the new one\n\thtml.classList.remove(...resolvedThemes); // ✅ remove all potential theme classes\n\thtml.classList.add(resolvedTheme);\n\thtml.dataset.theme = theme;\n\thtml.dataset.appliedTheme = resolvedTheme;\n}\n\n/**\n * Read the theme and applied theme from the `<html>` element.\n */\nfunction readThemeFromHtmlElement() {\n\tif (!canUseDOM()) {\n\t\treturn {\n\t\t\tappliedTheme: undefined,\n\t\t\ttheme: undefined,\n\t\t};\n\t}\n\n\tconst htmlElement = window.document.documentElement;\n\tconst theme = isTheme(htmlElement.dataset.theme) ? htmlElement.dataset.theme : undefined;\n\tconst appliedTheme = isResolvedTheme(htmlElement.dataset.appliedTheme)\n\t\t? htmlElement.dataset.appliedTheme\n\t\t: undefined;\n\n\treturn {\n\t\tappliedTheme,\n\t\ttheme,\n\t};\n}\n\n/**\n * 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.\n * This will mirror the result that gets applied to the <html> element.\n */\nfunction resolveTheme(\n\ttheme: Theme,\n\t{\n\t\tprefersDarkMode,\n\t\tprefersHighContrast,\n\t}: { prefersDarkMode: boolean; prefersHighContrast: boolean },\n) {\n\tif (theme === \"system\") {\n\t\treturn determineThemeFromMediaQuery({\n\t\t\tprefersDarkMode,\n\t\t\tprefersHighContrast,\n\t\t});\n\t}\n\n\treturn theme;\n}\n\n/**\n * 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.\n * This will mirror the result that gets applied to the <html> element.\n */\nfunction useAppliedTheme() {\n\tconst themeContext = useContext(ThemeProviderContext);\n\tconst theme = themeContext != null ? themeContext[0] : \"system\";\n\n\tconst prefersDarkMode = useMatchesMediaQuery(prefersDarkModeMediaQuery);\n\tconst prefersHighContrast = useMatchesMediaQuery(prefersHighContrastMediaQuery);\n\n\treturn resolveTheme(theme, { prefersDarkMode, prefersHighContrast });\n}\n\n/**\n * determineThemeFromMediaQuery returns the theme that should be used based on the user's media query preferences.\n * @private\n *\n * @example\n * ```tsx\n * const theme = determineThemeFromMediaQuery({\n * prefersDarkMode: true,\n * prefersHighContrast: false\n * });\n * // Returns: \"dark\"\n *\n * const themeWithContrast = determineThemeFromMediaQuery({\n * prefersDarkMode: false,\n * prefersHighContrast: true\n * });\n * // Returns: \"light-high-contrast\"\n * ```\n */\nexport function determineThemeFromMediaQuery({\n\tprefersDarkMode,\n\tprefersHighContrast,\n}: {\n\tprefersDarkMode: boolean;\n\tprefersHighContrast: boolean;\n}): ResolvedTheme {\n\tif (prefersHighContrast) {\n\t\treturn prefersDarkMode ? \"dark-high-contrast\" : \"light-high-contrast\";\n\t}\n\n\treturn prefersDarkMode ? \"dark\" : \"light\";\n}\n\n/**\n * Script that runs synchronously to prevent FOUC by applying the correct theme\n * before the page renders. This is the actual function that gets stringified and inlined.\n */\nfunction preventThemeFlash(args: {\n\tstorageKey: string;\n\tdefaultTheme: Theme;\n\tthemes: readonly Theme[];\n\tresolvedThemes: readonly ResolvedTheme[];\n\tprefersDarkModeMediaQuery: string;\n\tprefersHighContrastMediaQuery: string;\n}) {\n\tconst {\n\t\tstorageKey,\n\t\tdefaultTheme,\n\t\tthemes,\n\t\tresolvedThemes,\n\t\tprefersDarkModeMediaQuery,\n\t\tprefersHighContrastMediaQuery,\n\t} = args;\n\n\tfunction isTheme(value: unknown): value is Theme {\n\t\treturn typeof value === \"string\" && themes.includes(value as Theme);\n\t}\n\n\tfunction getThemeFromCookie(name: string): string | null {\n\t\tconst cookie = document.cookie;\n\t\tif (!cookie) {\n\t\t\treturn null;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cookies = cookie.split(\";\");\n\t\t\tconst themeCookie = cookies.find((c) => c.trim().startsWith(`${name}=`));\n\t\t\tconst cookieValue = themeCookie?.split(\"=\")[1];\n\t\t\tconst storedTheme = cookieValue ? decodeURIComponent(cookieValue) : null;\n\t\t\treturn storedTheme;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction buildCookie(name: string, val: string): string {\n\t\tconst expires = new Date();\n\t\texpires.setFullYear(expires.getFullYear() + 1);\n\t\tconst hostname = window.location.hostname;\n\t\tconst protocol = window.location.protocol;\n\t\tconst domainAttribute =\n\t\t\thostname === \"ngrok.com\" || hostname.endsWith(\".ngrok.com\") ? \"; domain=.ngrok.com\" : \"\";\n\t\tconst secureAttribute = protocol === \"https:\" ? \"; Secure\" : \"\";\n\t\treturn `${name}=${encodeURIComponent(val)}; expires=${expires.toUTCString()}; path=/${domainAttribute}; SameSite=Lax${secureAttribute}`;\n\t}\n\n\tfunction writeCookie(name: string, val: string): void {\n\t\ttry {\n\t\t\tdocument.cookie = buildCookie(name, val);\n\t\t} catch {\n\t\t\t// silently swallow errors\n\t\t}\n\t}\n\n\tfunction resolveThemeValue(\n\t\ttheme: Theme,\n\t\tisDark: boolean,\n\t\tisHighContrast: boolean,\n\t): ResolvedTheme {\n\t\tif (theme === \"system\") {\n\t\t\tif (isHighContrast) {\n\t\t\t\treturn isDark ? \"dark-high-contrast\" : \"light-high-contrast\";\n\t\t\t}\n\t\t\treturn isDark ? \"dark\" : \"light\";\n\t\t}\n\t\treturn theme;\n\t}\n\n\t// 1) Read preference: cookie first, fallback to localStorage (migration support)\n\tlet cookieTheme: string | null = null;\n\tlet lsTheme: string | null = null;\n\tlet storedTheme: Theme | null = null;\n\n\ttry {\n\t\tcookieTheme = getThemeFromCookie(storageKey);\n\t} catch {\n\t\t// silently swallow errors\n\t}\n\n\tif (isTheme(cookieTheme)) {\n\t\tstoredTheme = cookieTheme;\n\t} else {\n\t\ttry {\n\t\t\tlsTheme = window.localStorage?.getItem(storageKey) ?? null;\n\t\t} catch {\n\t\t\t// silently swallow errors\n\t\t}\n\t\tif (isTheme(lsTheme)) {\n\t\t\tstoredTheme = lsTheme;\n\t\t}\n\t}\n\n\tconst preference = isTheme(storedTheme) ? storedTheme : defaultTheme;\n\n\t// 2) Resolve theme based on media queries\n\tconst isDark = matchMedia(prefersDarkModeMediaQuery).matches;\n\tconst isHighContrast = matchMedia(prefersHighContrastMediaQuery).matches;\n\tconst resolvedTheme = resolveThemeValue(preference, isDark, isHighContrast);\n\n\tconst html = document.documentElement;\n\t// 3) Apply theme to DOM (same order as applyThemeToHtml)\n\tif (html.dataset.appliedTheme !== resolvedTheme || html.dataset.theme !== preference) {\n\t\t// Remove all theme classes\n\t\tfor (const themeClass of resolvedThemes as readonly string[]) {\n\t\t\thtml.classList.remove(themeClass);\n\t\t}\n\t\t// Add resolved theme class\n\t\thtml.classList.add(resolvedTheme);\n\t\t// Set data attributes\n\t\thtml.dataset.theme = preference;\n\t\thtml.dataset.appliedTheme = resolvedTheme;\n\t}\n\n\t// 4) Handle persistence/migration synchronously to prevent FOUC\n\tconst hadValidCookie = isTheme(cookieTheme);\n\ttry {\n\t\tif (isTheme(lsTheme) && !hadValidCookie) {\n\t\t\t// Migrate from localStorage to cookie\n\t\t\twriteCookie(storageKey, lsTheme);\n\t\t\ttry {\n\t\t\t\twindow.localStorage.removeItem(storageKey);\n\t\t\t} catch {\n\t\t\t\t// silently swallow errors\n\t\t\t}\n\t\t} else if (!hadValidCookie) {\n\t\t\t// Set default cookie if none existed\n\t\t\twriteCookie(storageKey, preference);\n\t\t}\n\t} catch {\n\t\t// silently swallow errors\n\t}\n}\n\n/**\n * preventWrongThemeFlashScriptContent generates a script that prevents the wrong theme from flashing on initial page load.\n * It checks cookies for a stored theme, and if none is found, it sets the default theme.\n * It also applies the correct theme to the `<html>` element based on the user's media query preferences.\n */\nfunction preventWrongThemeFlashScriptContent() {\n\tconst args = {\n\t\tstorageKey: THEME_STORAGE_KEY,\n\t\tdefaultTheme: DEFAULT_THEME,\n\t\tthemes,\n\t\tresolvedThemes,\n\t\tprefersDarkModeMediaQuery,\n\t\tprefersHighContrastMediaQuery,\n\t} as const satisfies Parameters<typeof preventThemeFlash>[0];\n\n\treturn `(${preventThemeFlash.toString()})(${JSON.stringify(args)})`;\n}\n\nexport type PreventWrongThemeFlashScriptProps = {\n\t/**\n\t * An optional CSP nonce to allowlist this inline script. Using this can help\n\t * you to avoid using the CSP `unsafe-inline` directive, which disables\n\t * XSS protection and would allowlist all inline scripts or styles.\n\t *\n\t * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce\n\t */\n\tnonce?: string;\n};\n\n/**\n * Renders an inline script that prevents Flash of Unstyled Content (FOUC) or the\n * wrong theme flashing on first paint.\n *\n * This is the preferred building block for SSR apps. Pair it with\n * {@link preloadFontLink} HTTP `Link` headers in your server entry so font fetches\n * begin before HTML is parsed. For client-only apps without header control, pair\n * it with {@link PreloadFont} elements in `<head>` instead.\n *\n * Place this as early as possible in the `<head>`.\n *\n * @example\n * ```tsx\n * // entry.server.tsx — send font preloads as HTTP headers (preferred for SSR)\n * headers.set(\"Link\", [\n * `<${assetsCdnOrigin}>; rel=preconnect; crossorigin`,\n * preloadFontLink(\"roobert\"),\n * preloadFontLink(\"jetbrains-mono\"),\n * ].join(\", \"));\n *\n * // root.tsx — only the FOUC script in <head>\n * <head>\n * <PreventWrongThemeFlashScript nonce={nonce} />\n * </head>\n * ```\n *\n * @param nonce - Optional CSP nonce to allowlist the inline script under a strict CSP.\n * @returns {JSX.Element} A script tag injected before first paint.\n * @see preloadFontLink\n * @see PreloadFont\n * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce\n */\nconst PreventWrongThemeFlashScript = ({ nonce }: PreventWrongThemeFlashScriptProps) => (\n\t<script\n\t\tdangerouslySetInnerHTML={{\n\t\t\t__html: preventWrongThemeFlashScriptContent(),\n\t\t}}\n\t\tnonce={nonce}\n\t\tsuppressHydrationWarning\n\t/>\n);\nPreventWrongThemeFlashScript.displayName = \"PreventWrongThemeFlashScript\";\n\ntype InitialThemeProps = {\n\tclassName: string;\n\t\"data-applied-theme\": ResolvedTheme;\n\t\"data-theme\": Theme;\n};\n\ntype UseInitialHtmlThemePropsOptions = {\n\tclassName?: string;\n\t/**\n\t * Theme cookie string for SSR theme resolution. Pass only the theme cookie\n\t * pair (via {@link extractThemeCookie}) rather than the full raw `Cookie`\n\t * header to avoid leaking sensitive cookies in serialized loader data.\n\t */\n\tssrCookie?: string;\n};\n\n/**\n * useInitialHtmlThemeProps returns the initial props that should be applied to the <html> element to prevent react hydration errors.\n */\nfunction useInitialHtmlThemeProps(props: UseInitialHtmlThemePropsOptions = {}): InitialThemeProps {\n\tconst { className = \"\", ssrCookie } = props ?? {};\n\n\treturn useMemo(() => {\n\t\tlet initialTheme: Theme;\n\t\tlet resolvedTheme: ResolvedTheme;\n\n\t\tif (!canUseDOM()) {\n\t\t\tinitialTheme = getStoredTheme({ cookie: ssrCookie });\n\t\t\tresolvedTheme = resolveTheme(initialTheme, {\n\t\t\t\t// During SSR we can't detect media queries, so assume light/no high contrast.\n\t\t\t\t// The inline script will correct this before paint for \"system\" theme users.\n\t\t\t\tprefersDarkMode: false,\n\t\t\t\tprefersHighContrast: false,\n\t\t\t});\n\t\t} else {\n\t\t\tconst prefersDarkMode = window.matchMedia(prefersDarkModeMediaQuery).matches;\n\t\t\tconst prefersHighContrast = window.matchMedia(prefersHighContrastMediaQuery).matches;\n\t\t\tinitialTheme = getStoredTheme({ cookie: document.cookie });\n\t\t\tresolvedTheme = resolveTheme(initialTheme, {\n\t\t\t\tprefersDarkMode,\n\t\t\t\tprefersHighContrast,\n\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\tclassName: cx(className, resolvedTheme),\n\t\t\t\"data-applied-theme\": resolvedTheme,\n\t\t\t\"data-theme\": initialTheme,\n\t\t};\n\t}, [className, ssrCookie]);\n}\n\ntype GetStoredThemeOptions = {\n\t/**\n\t * raw Cookie header (SSR) or document.cookie (client)\n\t */\n\tcookie: string | null | undefined;\n};\n\n/**\n * Returns the persisted UI theme from a Cookie header string.\n *\n * Looks for a cookie named by {@link THEME_STORAGE_KEY} and returns its value **iff**\n * it’s a valid `Theme` per `isTheme`. Otherwise, falls back to\n * {@link DEFAULT_THEME}. This function never throws; malformed encodings or\n * missing cookies quietly return the default.\n *\n * @example\n * getStoredTheme({ cookie: `${THEME_STORAGE_KEY}=dark; session=abc` }) // \"dark\"\n * @example\n * getStoredTheme({ cookie: \"\" }) // DEFAULT_THEME\n */\nfunction getStoredTheme({ cookie }: GetStoredThemeOptions): Theme {\n\tif (!cookie) {\n\t\treturn DEFAULT_THEME;\n\t}\n\n\ttry {\n\t\tconst cookies = cookie.split(\";\");\n\t\tconst themeCookie = cookies.find((cookieStr) =>\n\t\t\tcookieStr.trim().startsWith(`${THEME_STORAGE_KEY}=`),\n\t\t);\n\t\tconst cookieValue = themeCookie?.split(\"=\")[1];\n\t\tconst storedTheme = cookieValue ? globalThis.decodeURIComponent(cookieValue) : null;\n\n\t\treturn isTheme(storedTheme) ? storedTheme : DEFAULT_THEME;\n\t} catch {\n\t\treturn DEFAULT_THEME;\n\t}\n}\n\n/**\n * Extract just the mantle theme cookie from a raw `Cookie` header string.\n *\n * Use this in SSR loaders to safely pass the theme cookie to\n * {@link useInitialHtmlThemeProps} without exposing the full `Cookie` header\n * (which may contain HttpOnly/session cookies) in serialized loader data.\n *\n * @example\n * ```ts\n * // app/root.tsx loader\n * export const loader = async ({ request }: Route.LoaderArgs) => {\n * const themeCookie = extractThemeCookie(request.headers.get(\"Cookie\"));\n * return { themeCookie };\n * };\n * ```\n *\n * @param cookieHeader - The raw `Cookie` header string from the request, or null/undefined.\n * @returns The `mantle-ui-theme=<value>` cookie string, or undefined if not found.\n */\nfunction extractThemeCookie(cookieHeader: string | null | undefined): string | undefined {\n\tif (!cookieHeader) {\n\t\treturn undefined;\n\t}\n\n\treturn cookieHeader\n\t\t.split(\";\")\n\t\t.map((part) => part.trim())\n\t\t.find((part) => part.startsWith(`${THEME_STORAGE_KEY}=`));\n}\n\nexport {\n\tPreventWrongThemeFlashScript,\n\tThemeProvider,\n\t//,\n\textractThemeCookie,\n\tgetStoredTheme,\n\tpreventWrongThemeFlashScriptContent,\n\treadThemeFromHtmlElement,\n\tuseAppliedTheme,\n\tuseInitialHtmlThemeProps,\n\tuseTheme,\n};\n\n/**\n * Notifies other open tabs (same origin) that the theme changed.\n *\n * Prefers a shared {@link BroadcastChannel} for immediate, reliable delivery.\n * Falls back to writing a unique “ping” value to `localStorage`, which triggers\n * the cross-tab `storage` event. Both mechanisms only work across the same origin.\n *\n * Uses a timestamp to ensure the storage value always changes so the event fires.\n *\n * @remarks\n * - Same-origin only: BroadcastChannel and the `storage` event do not cross subdomains\n * or different schemes/ports. For cross-subdomain sync, use a postMessage hub or server push.\n * - This function is fire-and-forget and intentionally swallows errors.\n * - Receivers should re-read the cookie/source of truth and then apply the theme;\n * don’t trust the payload blindly.\n *\n * @example\n * // Sender (inside your setter)\n * notifyOtherTabs(nextTheme, {\n * broadcastChannel: broadcastChannelRef.current,\n * pingKey: `${storageKey}__ping`,\n * });\n *\n * @example\n * // Receiver (setup once per tab)\n * const bc = new BroadcastChannel(storageKey);\n * bc.onmessage = () => syncThemeFromCookie();\n * window.addEventListener('storage', (e) => {\n * if (e.key === `${storageKey}__ping`) syncThemeFromCookie();\n * });\n */\nfunction notifyOtherTabs(\n\ttheme: Theme,\n\toptions: {\n\t\tbroadcastChannel: BroadcastChannel | null;\n\t\tpingKey: `${string}__ping`;\n\t},\n) {\n\tconst { broadcastChannel, pingKey } = options;\n\n\t// first try BroadcastChannel\n\ttry {\n\t\tif (broadcastChannel) {\n\t\t\tbroadcastChannel.postMessage({\n\t\t\t\ttheme,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t} catch {\n\t\t// silently swallow errors\n\t}\n\n\t// fallback to storage event: write a \"ping\" key (not the real storageKey)\n\ttry {\n\t\tlocalStorage.setItem(pingKey, JSON.stringify({ theme, timestamp: Date.now() }));\n\t} catch {\n\t\t// silently swallow errors\n\t}\n}\n\nfunction buildThemeCookie(value: string) {\n\tconst expires = new Date();\n\texpires.setFullYear(expires.getFullYear() + 1); // 1 year expiration\n\n\t// Only set .ngrok.com domain for ngrok domains, otherwise let it default to current domain\n\tconst { hostname, protocol } = window.location;\n\tconst domainAttribute =\n\t\thostname === \"ngrok.com\" || hostname.endsWith(\".ngrok.com\") ? \"; domain=.ngrok.com\" : \"\";\n\tconst secureAttribute = protocol === \"https:\" ? \"; Secure\" : \"\";\n\n\treturn `${THEME_STORAGE_KEY}=${encodeURIComponent(value)}; expires=${expires.toUTCString()}; path=/${domainAttribute}; SameSite=Lax${secureAttribute}` as const;\n}\n\n/**\n * Sets a cookie with appropriate domain for the current hostname.\n * Uses .ngrok.com for ngrok domains, otherwise no domain (current domain only).\n */\nfunction setCookie(value: string) {\n\tif (!canUseDOM()) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tdocument.cookie = buildThemeCookie(value);\n\t} catch {\n\t\t// silently swallow errors\n\t}\n}\n"],"mappings":"sTAGA,MAAM,EAAiB,CAAC,QAAS,OAAQ,sBAAuB,qBAAqB,CAU/E,EAAS,CAAC,SAAU,GAAG,EAAe,CAUtC,EAAmC,GAAa,EAKtD,SAAS,EAAQ,EAAgC,CAKhD,OAJI,OAAO,GAAU,SAId,EAAO,SAAS,EAAe,CAH9B,GAST,MAAM,EAA2D,GAAa,EAK9E,SAAS,EAAgB,EAAwC,CAKhE,OAJI,OAAO,GAAU,SAId,EAAe,SAAS,EAAuB,CAH9C,GC1BT,MAAM,EAA4B,+BAK5B,EAAgC,2BAKhC,EAAoB,kBAMpB,EAAgB,SAehB,EAAuB,EALY,CAAC,aAAgB,KAAK,CAKoB,CAgBnF,SAAS,EAAc,CAAE,YAAgC,CAExD,GAAM,CAAC,EAAO,GAAY,MAAsB,CAC/C,IAAM,EAAc,EAAe,CAClC,OAAQ,GAAW,CAAG,SAAS,OAAS,KACxC,CAAC,CAEF,OADA,EAAiB,EAAY,CACtB,GACN,CAEI,EAAsB,EAAgC,KAAK,CAEjE,MAAgB,CACf,SAAS,EAAoB,EAAc,CAC1C,IAAM,EAAW,GAAQ,EAAe,CAAE,OAAQ,SAAS,OAAQ,CAAC,CACpE,EAAS,EAAS,CAClB,EAAiB,EAAS,CAI3B,GAAqB,CAGrB,GAAI,CACC,qBAAsB,SACzB,EAAoB,QAAU,IAAI,iBAAiB,EAAkB,CACrE,EAAoB,QAAQ,UAAa,GAAU,CAClD,IAAM,EAAiB,GAAO,MAAM,MAChC,EAAQ,EAAM,EACjB,EAAoB,EAAM,QAItB,EAIR,SAAS,EAAU,EAAqB,CACnC,EAAM,MAAQ,GAAG,EAAkB,SACtC,GAAqB,CAGvB,OAAO,iBAAiB,UAAW,EAAU,CAG7C,IAAM,EAAiB,OAAO,WAAW,EAA0B,CAC7D,EAAyB,OAAO,WAAW,EAA8B,CAE/E,SAAS,GAAW,CACnB,GAAqB,CAGtB,SAAS,GAAqB,CACzB,SAAS,kBAAoB,WAChC,GAAqB,CAcvB,OAVA,EAAe,iBAAiB,SAAU,EAAS,CACnD,EAAuB,iBAAiB,SAAU,EAAS,CAG3D,OAAO,iBAAiB,WAAY,EAAS,CAG7C,SAAS,iBAAiB,mBAAoB,EAAmB,KAGpD,CACZ,OAAO,oBAAoB,UAAW,EAAU,CAChD,EAAe,oBAAoB,SAAU,EAAS,CACtD,EAAuB,oBAAoB,SAAU,EAAS,CAC9D,OAAO,oBAAoB,WAAY,EAAS,CAChD,SAAS,oBAAoB,mBAAoB,EAAmB,CAEpE,GAAI,CACH,EAAoB,SAAS,OAAO,MAC7B,EAGR,EAAoB,QAAU,OAE7B,EAAE,CAAC,CAEN,IAAM,EAA4B,MAC3B,CACL,EACC,GAAgB,CAChB,EAAU,EAAK,CACf,EAAS,EAAK,CACd,EAAiB,EAAK,CACtB,EAAgB,EAAM,CACrB,iBAAkB,EAAoB,QACtC,QAAS,GAAG,EAAkB,QAC9B,CAAC,EAEH,CACD,CAAC,EAAM,CACP,CAED,OAAO,EAAC,EAAqB,SAAtB,CAAsC,QAAQ,WAAyC,CAAA,CAE/F,EAAc,YAAc,gBAO5B,SAAS,GAAW,CACnB,IAAM,EAAU,EAAW,EAAqB,CAIhD,OAFA,EAAU,EAAS,+CAA+C,CAE3D,EAMR,SAAS,EAAiB,EAAc,CACvC,GAAI,CAAC,GAAW,CACf,OAGD,IAAM,EAAO,OAAO,SAAS,gBAEvB,EAAkB,OAAO,WAAW,EAA0B,CAAC,QAC/D,EAAsB,OAAO,WAAW,EAA8B,CAAC,QAEvE,EAAgB,EAAa,EAAO,CACzC,kBACA,sBACA,CAAC,CAEI,EAAY,EAAK,QAAQ,MACzB,EAAmB,EAAK,QAAQ,aAEhC,EAAe,EAAQ,EAAU,CAAG,EAAY,IAAA,GAChD,EAAuB,EAAgB,EAAiB,CAAG,EAAmB,IAAA,GAEhF,IAAiB,GAAS,IAAyB,IAMvD,EAAK,UAAU,OAAO,GAAG,EAAe,CACxC,EAAK,UAAU,IAAI,EAAc,CACjC,EAAK,QAAQ,MAAQ,EACrB,EAAK,QAAQ,aAAe,GAM7B,SAAS,GAA2B,CACnC,GAAI,CAAC,GAAW,CACf,MAAO,CACN,aAAc,IAAA,GACd,MAAO,IAAA,GACP,CAGF,IAAM,EAAc,OAAO,SAAS,gBAC9B,EAAQ,EAAQ,EAAY,QAAQ,MAAM,CAAG,EAAY,QAAQ,MAAQ,IAAA,GAK/E,MAAO,CACN,aALoB,EAAgB,EAAY,QAAQ,aAAa,CACnE,EAAY,QAAQ,aACpB,IAAA,GAIF,QACA,CAOF,SAAS,EACR,EACA,CACC,kBACA,uBAEA,CAQD,OAPI,IAAU,SACN,EAA6B,CACnC,kBACA,sBACA,CAAC,CAGI,EAOR,SAAS,GAAkB,CAC1B,IAAM,EAAe,EAAW,EAAqB,CAMrD,OAAO,EALO,GAAgB,KAAyB,SAAlB,EAAa,GAKvB,CAAE,gBAHL,EAAqB,EAA0B,CAGzB,oBAFlB,EAAqB,EAA8B,CAEZ,CAAC,CAsBrE,SAAgB,EAA6B,CAC5C,kBACA,uBAIiB,CAKjB,OAJI,EACI,EAAkB,qBAAuB,sBAG1C,EAAkB,OAAS,QAOnC,SAAS,EAAkB,EAOxB,CACF,GAAM,CACL,aACA,eACA,SACA,iBACA,4BACA,iCACG,EAEJ,SAAS,EAAQ,EAAgC,CAChD,OAAO,OAAO,GAAU,UAAY,EAAO,SAAS,EAAe,CAGpE,SAAS,EAAmB,EAA6B,CACxD,IAAM,EAAS,SAAS,OACxB,GAAI,CAAC,EACJ,OAAO,KAGR,GAAI,CAGH,IAAM,EAFU,EAAO,MAAM,IAAI,CACL,KAAM,GAAM,EAAE,MAAM,CAAC,WAAW,GAAG,EAAK,GAAG,CAAC,EACvC,MAAM,IAAI,CAAC,GAE5C,OADoB,EAAc,mBAAmB,EAAY,CAAG,UAE7D,CACP,OAAO,MAIT,SAAS,EAAY,EAAc,EAAqB,CACvD,IAAM,EAAU,IAAI,KACpB,EAAQ,YAAY,EAAQ,aAAa,CAAG,EAAE,CAC9C,IAAM,EAAW,OAAO,SAAS,SAC3B,EAAW,OAAO,SAAS,SAC3B,EACL,IAAa,aAAe,EAAS,SAAS,aAAa,CAAG,sBAAwB,GACjF,EAAkB,IAAa,SAAW,WAAa,GAC7D,MAAO,GAAG,EAAK,GAAG,mBAAmB,EAAI,CAAC,YAAY,EAAQ,aAAa,CAAC,UAAU,EAAgB,gBAAgB,IAGvH,SAAS,EAAY,EAAc,EAAmB,CACrD,GAAI,CACH,SAAS,OAAS,EAAY,EAAM,EAAI,MACjC,GAKT,SAAS,EACR,EACA,EACA,EACgB,CAOhB,OANI,IAAU,SACT,EACI,EAAS,qBAAuB,sBAEjC,EAAS,OAAS,QAEnB,EAIR,IAAI,EAA6B,KAC7B,EAAyB,KACzB,EAA4B,KAEhC,GAAI,CACH,EAAc,EAAmB,EAAW,MACrC,EAIR,GAAI,EAAQ,EAAY,CACvB,EAAc,MACR,CACN,GAAI,CACH,EAAU,OAAO,cAAc,QAAQ,EAAW,EAAI,UAC/C,EAGJ,EAAQ,EAAQ,GACnB,EAAc,GAIhB,IAAM,EAAa,EAAQ,EAAY,CAAG,EAAc,EAGlD,EAAS,WAAW,EAA0B,CAAC,QAC/C,EAAiB,WAAW,EAA8B,CAAC,QAC3D,EAAgB,EAAkB,EAAY,EAAQ,EAAe,CAErE,EAAO,SAAS,gBAEtB,GAAI,EAAK,QAAQ,eAAiB,GAAiB,EAAK,QAAQ,QAAU,EAAY,CAErF,IAAK,IAAM,KAAc,EACxB,EAAK,UAAU,OAAO,EAAW,CAGlC,EAAK,UAAU,IAAI,EAAc,CAEjC,EAAK,QAAQ,MAAQ,EACrB,EAAK,QAAQ,aAAe,EAI7B,IAAM,EAAiB,EAAQ,EAAY,CAC3C,GAAI,CACH,GAAI,EAAQ,EAAQ,EAAI,CAAC,EAAgB,CAExC,EAAY,EAAY,EAAQ,CAChC,GAAI,CACH,OAAO,aAAa,WAAW,EAAW,MACnC,QAGG,GAEX,EAAY,EAAY,EAAW,MAE7B,GAUT,SAAS,GAAsC,CAC9C,IAAM,EAAO,CACZ,WAAY,EACZ,aAAc,EACd,SACA,iBACA,4BACA,gCACA,CAED,MAAO,IAAI,EAAkB,UAAU,CAAC,IAAI,KAAK,UAAU,EAAK,CAAC,GA8ClE,MAAM,GAAgC,CAAE,WACvC,EAAC,SAAD,CACC,wBAAyB,CACxB,OAAQ,GAAqC,CAC7C,CACM,QACP,yBAAA,GACC,CAAA,CAEH,EAA6B,YAAc,+BAqB3C,SAAS,EAAyB,EAAyC,EAAE,CAAqB,CACjG,GAAM,CAAE,YAAY,GAAI,aAAc,GAAS,EAAE,CAEjD,OAAO,MAAc,CACpB,IAAI,EACA,EAEJ,GAAI,CAAC,GAAW,CACf,EAAe,EAAe,CAAE,OAAQ,EAAW,CAAC,CACpD,EAAgB,EAAa,EAAc,CAG1C,gBAAiB,GACjB,oBAAqB,GACrB,CAAC,KACI,CACN,IAAM,EAAkB,OAAO,WAAW,EAA0B,CAAC,QAC/D,EAAsB,OAAO,WAAW,EAA8B,CAAC,QAC7E,EAAe,EAAe,CAAE,OAAQ,SAAS,OAAQ,CAAC,CAC1D,EAAgB,EAAa,EAAc,CAC1C,kBACA,sBACA,CAAC,CAGH,MAAO,CACN,UAAW,EAAG,EAAW,EAAc,CACvC,qBAAsB,EACtB,aAAc,EACd,EACC,CAAC,EAAW,EAAU,CAAC,CAuB3B,SAAS,EAAe,CAAE,UAAwC,CACjE,GAAI,CAAC,EACJ,OAAO,EAGR,GAAI,CAKH,IAAM,EAJU,EAAO,MAAM,IAAI,CACL,KAAM,GACjC,EAAU,MAAM,CAAC,WAAW,GAAG,EAAkB,GAAG,CACpD,EACgC,MAAM,IAAI,CAAC,GACtC,EAAc,EAAc,WAAW,mBAAmB,EAAY,CAAG,KAE/E,OAAO,EAAQ,EAAY,CAAG,EAAc,OACrC,CACP,OAAO,GAuBT,SAAS,EAAmB,EAA6D,CACnF,KAIL,OAAO,EACL,MAAM,IAAI,CACV,IAAK,GAAS,EAAK,MAAM,CAAC,CAC1B,KAAM,GAAS,EAAK,WAAW,GAAG,EAAkB,GAAG,CAAC,CA+C3D,SAAS,EACR,EACA,EAIC,CACD,GAAM,CAAE,mBAAkB,WAAY,EAGtC,GAAI,CACH,GAAI,EAAkB,CACrB,EAAiB,YAAY,CAC5B,QACA,UAAW,KAAK,KAAK,CACrB,CAAC,CACF,aAEM,EAKR,GAAI,CACH,aAAa,QAAQ,EAAS,KAAK,UAAU,CAAE,QAAO,UAAW,KAAK,KAAK,CAAE,CAAC,CAAC,MACxE,GAKT,SAAS,EAAiB,EAAe,CACxC,IAAM,EAAU,IAAI,KACpB,EAAQ,YAAY,EAAQ,aAAa,CAAG,EAAE,CAG9C,GAAM,CAAE,WAAU,YAAa,OAAO,SAChC,EACL,IAAa,aAAe,EAAS,SAAS,aAAa,CAAG,sBAAwB,GACjF,EAAkB,IAAa,SAAW,WAAa,GAE7D,MAAO,GAAG,EAAkB,GAAG,mBAAmB,EAAM,CAAC,YAAY,EAAQ,aAAa,CAAC,UAAU,EAAgB,gBAAgB,IAOtI,SAAS,EAAU,EAAe,CAC5B,MAAW,CAIhB,GAAI,CACH,SAAS,OAAS,EAAiB,EAAM,MAClC"}
|
package/dist/theme.d.ts
CHANGED
|
@@ -203,7 +203,7 @@ declare function useTheme(): ThemeProviderState;
|
|
|
203
203
|
*/
|
|
204
204
|
declare function readThemeFromHtmlElement(): {
|
|
205
205
|
appliedTheme: "dark" | "light" | "light-high-contrast" | "dark-high-contrast" | undefined;
|
|
206
|
-
theme: "dark" | "light" | "light-high-contrast" | "dark-high-contrast" |
|
|
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.
|
package/dist/theme.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as e,c as t,d as n,f as r,h as i,i as a,l as o,m as s,n as c,o as l,p as u,r as d,s as f,t as p,u as m}from"./theme-provider-
|
|
1
|
+
import{a as e,c as t,d as n,f as r,h as i,i as a,l as o,m as s,n as c,o as l,p as u,r as d,s as f,t as p,u as m}from"./theme-provider-BFcnjeME.js";import{useEffect as h}from"react";import{Fragment as g,jsx as _,jsxs as v}from"react/jsx-runtime";const y=`mantle-dark-styles`,b=`mantle-light-high-contrast-styles`,x=`mantle-dark-high-contrast-styles`,S=`(prefers-color-scheme: dark)`,C=`(prefers-contrast: more) and (prefers-color-scheme: light)`,w=`(prefers-contrast: more) and (prefers-color-scheme: dark)`;function T(e,t){let n=t??e;return{dark:n===`dark`?`all`:S,lightHighContrast:n===`light-high-contrast`?`all`:C,darkHighContrast:n===`dark-high-contrast`?`all`:w}}function E(e){return e}function D(e){let{darkLinkId:t,lightHcLinkId:n,darkHcLinkId:r,mediaDark:i,mediaLightHc:a,mediaDarkHc:o,forceTheme:s}=e,c=document.documentElement.dataset.appliedTheme,l=s??c,u=document.getElementById(t),d=document.getElementById(n),f=document.getElementById(r);u&&(u.media=l===`dark`?`all`:i),d&&(d.media=l===`light-high-contrast`?`all`:a),f&&(f.media=l===`dark-high-contrast`?`all`:o)}function O(e){let t={darkLinkId:y,lightHcLinkId:b,darkHcLinkId:x,mediaDark:S,mediaLightHc:C,mediaDarkHc:w,forceTheme:e};return`(${D.toString()})(${JSON.stringify(t)})`}function k({darkCssUrl:e,lightHighContrastCssUrl:t,darkHighContrastCssUrl:n,forceTheme:i,nonce:o,ssrCookie:s}){h(()=>{function e(){let e=document.documentElement.dataset.appliedTheme;return r(e)?e:void 0}function t(){let{dark:t,lightHighContrast:n,darkHighContrast:r}=T(e(),i),a=document.getElementById(y),o=document.getElementById(b),s=document.getElementById(x);a&&(a.media=t),o&&(o.media=n),s&&(s.media=r)}t();let n=new MutationObserver(t);return n.observe(document.documentElement,{attributes:!0,attributeFilter:[`data-applied-theme`]}),()=>{n.disconnect()}},[i]);let c=s==null?void 0:a({cookie:s}),l=c===`system`?void 0:c,{dark:u,lightHighContrast:d,darkHighContrast:f}=T(l,i);return v(g,{children:[(!i||i===`dark`)&&_(`link`,{rel:`stylesheet`,id:y,href:e,media:u,suppressHydrationWarning:!0}),(!i||i===`light-high-contrast`)&&_(`link`,{rel:`stylesheet`,id:b,href:t,media:d,suppressHydrationWarning:!0}),(!i||i===`dark-high-contrast`)&&_(`link`,{rel:`stylesheet`,id:x,href:n,media:f,suppressHydrationWarning:!0}),!i&&l==null&&_(`script`,{dangerouslySetInnerHTML:{__html:O(i)},nonce:o,suppressHydrationWarning:!0})]})}k.displayName=`MantleStyleSheets`;const A=`https://assets.ngrok.com`,j=`${A}/fonts`,M={roobert:`/roobert/roobert-proportional-vf.woff2`,"jetbrains-mono":`/jetbrains/jetbrainsmono-wght.woff2`,"jetbrains-mono-italic":`/jetbrains/jetbrainsmono-italic-wght.woff2`,"family-regular":`/family/family-regular.woff2`,"family-italic":`/family/family-italic.woff2`};function N(e){return`${j}${e.startsWith(`/`)?e:`/${e}`}`}function P(e){return`<${N(M[e])}>; rel=preload; as=font; type="font/woff2"; crossorigin`}const F=({name:e})=>_(`link`,{rel:`preload`,href:N(M[e]),as:`font`,type:`font/woff2`,crossOrigin:`anonymous`});F.displayName=`PreloadFont`;export{m as $resolvedTheme,n as $theme,k as MantleStyleSheets,F as PreloadFont,p as PreventWrongThemeFlashScript,c as ThemeProvider,A as assetsCdnOrigin,d as extractThemeCookie,O as fixMediaScriptContent,N as fontHref,a as getStoredTheme,r as isResolvedTheme,u as isTheme,E as mantleStyleSheetUrls,P as preloadFontLink,e as preventWrongThemeFlashScriptContent,l as readThemeFromHtmlElement,s as resolvedThemes,i as themes,f as useAppliedTheme,t as useInitialHtmlThemeProps,o as useTheme};
|
|
2
2
|
//# sourceMappingURL=theme.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./cx-
|
|
2
|
-
//# sourceMappingURL=toast-
|
|
1
|
+
import{t as e}from"./cx-D1HYnpvA.js";import{t}from"./icon-BMH0fD_b.js";import{t as n}from"./slot-D_ZUrdEW.js";import{s as r}from"./theme-provider-BFcnjeME.js";import{createContext as i,forwardRef as a,useContext as o}from"react";import{jsx as s,jsxs as c}from"react/jsx-runtime";import{CheckCircleIcon as l}from"@phosphor-icons/react/CheckCircle";import{InfoIcon as u}from"@phosphor-icons/react/Info";import{WarningIcon as d}from"@phosphor-icons/react/Warning";import{WarningDiamondIcon as f}from"@phosphor-icons/react/WarningDiamond";import*as p from"sonner";const m=({className:t,containerAriaLabel:n,dir:i,duration_ms:a=4e3,position:o=`top-center`,style:c})=>{let l=r();return s(p.Toaster,{className:e(`toaster overlay-prompt pointer-events-auto *:duration-200`,t),containerAriaLabel:n,dir:i,duration:a,gap:12,position:o??`top-center`,style:c,theme:l,toastOptions:{unstyled:!0}})};m.displayName=`Toaster`;const h=i(``);function g(e,t){let n=t?.duration_ms;return typeof n==`number`&&n<=0&&(n=1/0),p.toast.custom(t=>s(h.Provider,{value:t,children:e}),{duration:n,...t?.id?{id:t.id}:{},unstyled:!0})}const _=i({priority:`info`}),v=a(({asChild:t,children:r,className:i,priority:a,...o},l)=>{let u=t?n:`div`;return s(_.Provider,{value:{priority:a},children:c(u,{className:e(`relative flex items-start gap-2 text-sm font-sans`,`p-3 pl-3.75`,`bg-popover high-contrast:border-popover rounded rounded-r-[0.3125rem] border border-gray-500/35 shadow-lg`,i),ref:l,...o,children:[s(T,{priority:a}),r]})})});v.displayName=`Toast`;const y=a(({className:n,svg:r,...i},a)=>{let c=o(_);switch(c.priority){case`danger`:return s(t,{className:e(`text-danger-600`,n),ref:a,svg:r??s(d,{weight:`fill`}),...i});case`warning`:return s(t,{className:e(`text-warning-600`,n),ref:a,svg:r??s(f,{weight:`fill`}),...i});case`success`:return s(t,{className:e(`text-success-600`,n),ref:a,svg:r??s(l,{weight:`fill`}),...i});case`info`:return s(t,{className:e(`text-accent-600`,n),ref:a,svg:s(u,{weight:`fill`}),...i});default:throw Error(`Unreachable Case: ${c.priority}`)}});y.displayName=`ToastIcon`;const b=a(({asChild:t,className:r,onClick:i,...a},c)=>{let l=o(h);return s(t?n:`button`,{className:e(`shrink-0`,`data-icon-button:-mr-0.5 data-icon-button:-mt-0.5 data-icon-button:rounded-xs`,r),onClick:e=>{i?.(e),!e.defaultPrevented&&p.toast.dismiss(l)},ref:c,...a})});b.displayName=`ToastAction`;const x=a(({asChild:t,className:r,...i},a)=>s(t?n:`p`,{className:e(`text-strong flex-1 text-sm font-body`,r),ref:a,...i}));x.displayName=`ToastMessage`;const S={Root:v,Action:b,Icon:y,Message:x};function C(e){e.target instanceof Element&&e.target.closest(`.overlay-prompt`)&&e.preventDefault()}const w={info:`bg-accent-600`,warning:`bg-warning-600`,success:`bg-success-600`,danger:`bg-danger-600`};function T({className:t,priority:n,...r}){return s(`div`,{"aria-hidden":!0,className:e(`z-1 absolute -inset-px right-auto w-1.5 rounded-l`,w[n],t),...r})}export{C as i,m as n,g as r,S as t};
|
|
2
|
+
//# sourceMappingURL=toast-BhAyUXKL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast-B28JCGXG.js","names":["IconComponent"],"sources":["../src/components/toast/toast.tsx"],"sourcesContent":["\"use client\";\n\nimport { CheckCircleIcon } from \"@phosphor-icons/react/CheckCircle\";\nimport { InfoIcon } from \"@phosphor-icons/react/Info\";\nimport { WarningIcon } from \"@phosphor-icons/react/Warning\";\nimport { WarningDiamondIcon } from \"@phosphor-icons/react/WarningDiamond\";\nimport {\n\ttype ComponentProps,\n\ttype ComponentRef,\n\ttype ReactNode,\n\tcreateContext,\n\tforwardRef,\n\tuseContext,\n} from \"react\";\nimport * as ToastPrimitive from \"sonner\";\nimport type { WithAsChild } from \"../../types/as-child.js\";\nimport type { WithStyleProps } from \"../../types/with-style-props.js\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { Icon as IconComponent } from \"../icon/icon.js\";\nimport type { SvgOnlyProps } from \"../icon/svg-only.js\";\nimport { Slot } from \"../slot/index.js\";\nimport { useAppliedTheme } from \"../theme/theme-provider.js\";\n\ntype ToasterPrimitiveProps = ComponentProps<typeof ToastPrimitive.Toaster>;\ntype ToasterPrimitiveTheme = ToasterPrimitiveProps[\"theme\"];\n\ntype ToasterProps = WithStyleProps &\n\tPick<ToasterPrimitiveProps, \"containerAriaLabel\" | \"dir\" | \"position\"> & {\n\t\t/**\n\t\t * Time in milliseconds that should elapse before automatically dismissing toasts.\n\t\t * When set here, this will be the default duration for all toasts.\n\t\t * @default 4000\n\t\t */\n\t\tduration_ms?: number;\n\t};\n\n/**\n * A container for displaying all toasts.\n *\n * Only one `<Toaster />` should be rendered in an app a time, preferably at the\n * root level of the app.\n *\n * @see https://mantle.ngrok.com/components/toast#toaster\n *\n * @example\n * ```tsx\n * <Toaster\n * position=\"top-right\"\n * duration_ms={5000}\n * />\n * ```\n */\nconst Toaster = ({\n\t//,\n\tclassName,\n\tcontainerAriaLabel,\n\tdir,\n\tduration_ms = 4000,\n\tposition = \"top-center\",\n\tstyle,\n}: ToasterProps) => {\n\tconst theme = useAppliedTheme();\n\n\treturn (\n\t\t<ToastPrimitive.Toaster\n\t\t\tclassName={cx(\"toaster overlay-prompt pointer-events-auto *:duration-200\", className)}\n\t\t\tcontainerAriaLabel={containerAriaLabel}\n\t\t\tdir={dir}\n\t\t\tduration={duration_ms}\n\t\t\tgap={12}\n\t\t\tposition={position ?? \"top-center\"}\n\t\t\tstyle={style}\n\t\t\ttheme={theme as ToasterPrimitiveTheme} // we have additional themes that are not in the sonner types, so we need to cast for now\n\t\t\ttoastOptions={{\n\t\t\t\tunstyled: true,\n\t\t\t}}\n\t\t/>\n\t);\n};\nToaster.displayName = \"Toaster\";\n\nconst ToastIdContext = createContext<string | number>(\"\");\n\ntype MakeToastOptions = {\n\t/**\n\t * Time in milliseconds that should elapse before automatically closing the toast.\n\t * Will default to the `<Toaster />`'s `duration_ms` if not provided.\n\t *\n\t * You can keep the toast open until manually dismissed by passing a value <= 0 or Number.POSITIVE_INFINITY\n\t */\n\tduration_ms?: number;\n\t/**\n\t * An optional custom ID for this toast. If not given, a unique ID is provided for you.\n\t */\n\tid?: string;\n};\n\n/**\n * Create a toast. Provide a `<Toast.Root>` component as the `children` to be rendered\n * inside the `<Toaster />` section.\n *\n * @see https://mantle.ngrok.com/components/toast#maketoast\n *\n * @example\n * ```tsx\n * // Basic toast with auto-dismiss (default 4000ms, inherits from `<Toaster>`)\n * makeToast(\n * <Toast.Root priority=\"success\">\n * <Toast.Icon />\n * <Toast.Message>Operation completed successfully!</Toast.Message>\n * <Toast.Action>Dismiss</Toast.Action>\n * </Toast.Root>\n * );\n *\n * // Toast that stays open until manually dismissed\n * makeToast(\n * <Toast.Root priority=\"warning\">\n * <Toast.Icon />\n * <Toast.Message>Action required</Toast.Message>\n * <Toast.Action>Dismiss</Toast.Action>\n * </Toast.Root>,\n * { duration_ms: Number.POSITIVE_INFINITY }\n * );\n * ```\n */\nfunction makeToast(children: ReactNode, options?: MakeToastOptions) {\n\tlet duration = options?.duration_ms;\n\tif (typeof duration === \"number\" && duration <= 0) {\n\t\tduration = Number.POSITIVE_INFINITY;\n\t}\n\n\treturn ToastPrimitive.toast.custom(\n\t\t(toastId) => <ToastIdContext.Provider value={toastId}>{children}</ToastIdContext.Provider>,\n\t\t{\n\t\t\t//\n\t\t\tduration,\n\t\t\t// If a custom ID is provided, use it, else use the toastId provided by the sonner library\n\t\t\t// don't set an ID to `undefined` as it breaks the sonner library\n\t\t\t...(options?.id ? { id: options.id } : {}),\n\t\t\tunstyled: true,\n\t\t},\n\t);\n}\n\nconst priorities = [\n\t//,\n\t\"danger\",\n\t\"info\",\n\t\"success\",\n\t\"warning\",\n] as const;\ntype Priority = (typeof priorities)[number];\n\ntype ToastState = {\n\tpriority: Priority;\n};\n\nconst ToastStateContext = createContext<ToastState>({\n\tpriority: \"info\",\n});\n\ntype ToastProps = ComponentProps<\"div\"> &\n\tWithAsChild & {\n\t\tpriority: Priority;\n\t};\n\n/**\n * A succinct message with a priority that is displayed temporarily.\n * Toasts are used to provide feedback to the user without interrupting their workflow.\n *\n * @see https://mantle.ngrok.com/components/toast#toastroot\n *\n * @example\n * ```tsx\n * <Toast.Root priority=\"success\">\n * <Toast.Icon />\n * <Toast.Message>Changes saved successfully!</Toast.Message>\n * <Toast.Action>Undo</Toast.Action>\n * </Toast.Root>\n * ```\n */\nconst Root = forwardRef<ComponentRef<\"div\">, ToastProps>(\n\t({ asChild, children, className, priority, ...props }, ref) => {\n\t\tconst Component = asChild ? Slot : \"div\";\n\n\t\treturn (\n\t\t\t<ToastStateContext.Provider value={{ priority }}>\n\t\t\t\t<Component\n\t\t\t\t\tclassName={cx(\n\t\t\t\t\t\t\"relative flex items-start gap-2 text-sm font-sans\",\n\t\t\t\t\t\t\"p-3 pl-3.75\",\n\t\t\t\t\t\t\"bg-popover high-contrast:border-popover rounded rounded-r-[0.3125rem] border border-gray-500/35 shadow-lg\",\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * Do not apply overflow-hidden because we want the priority bar accent\n\t\t\t\t\t\t * to overlap the toast border, else the border flows over the\n\t\t\t\t\t\t * priority bar.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tclassName,\n\t\t\t\t\t)}\n\t\t\t\t\tref={ref}\n\t\t\t\t\t{...props}\n\t\t\t\t>\n\t\t\t\t\t<PriorityBarAccent priority={priority} />\n\t\t\t\t\t{children}\n\t\t\t\t</Component>\n\t\t\t</ToastStateContext.Provider>\n\t\t);\n\t},\n);\nRoot.displayName = \"Toast\";\n\ntype ToastIconProps = Partial<SvgOnlyProps>;\n\n/**\n * An icon that visually represents the priority of the toast.\n * If you do not provide an icon, the default icon and color for the priority is used.\n *\n * @see https://mantle.ngrok.com/components/toast#toasticon\n *\n * @example\n * ```tsx\n * <Toast.Root priority=\"warning\">\n * <Toast.Icon />\n * <Toast.Message>Warning message</Toast.Message>\n * </Toast.Root>\n * ```\n */\nconst Icon = forwardRef<ComponentRef<\"svg\">, ToastIconProps>(\n\t({ className, svg, ...props }, ref) => {\n\t\tconst ctx = useContext(ToastStateContext);\n\n\t\tswitch (ctx.priority) {\n\t\t\tcase \"danger\":\n\t\t\t\treturn (\n\t\t\t\t\t<IconComponent\n\t\t\t\t\t\tclassName={cx(\"text-danger-600\", className)}\n\t\t\t\t\t\tref={ref}\n\t\t\t\t\t\tsvg={svg ?? <WarningIcon weight=\"fill\" />}\n\t\t\t\t\t\t{...props}\n\t\t\t\t\t/>\n\t\t\t\t);\n\t\t\tcase \"warning\":\n\t\t\t\treturn (\n\t\t\t\t\t<IconComponent\n\t\t\t\t\t\tclassName={cx(\"text-warning-600\", className)}\n\t\t\t\t\t\tref={ref}\n\t\t\t\t\t\tsvg={svg ?? <WarningDiamondIcon weight=\"fill\" />}\n\t\t\t\t\t\t{...props}\n\t\t\t\t\t/>\n\t\t\t\t);\n\t\t\tcase \"success\":\n\t\t\t\treturn (\n\t\t\t\t\t<IconComponent\n\t\t\t\t\t\tclassName={cx(\"text-success-600\", className)}\n\t\t\t\t\t\tref={ref}\n\t\t\t\t\t\tsvg={svg ?? <CheckCircleIcon weight=\"fill\" />}\n\t\t\t\t\t\t{...props}\n\t\t\t\t\t/>\n\t\t\t\t);\n\t\t\tcase \"info\":\n\t\t\t\treturn (\n\t\t\t\t\t<IconComponent\n\t\t\t\t\t\t//\n\t\t\t\t\t\tclassName={cx(\"text-accent-600\", className)}\n\t\t\t\t\t\tref={ref}\n\t\t\t\t\t\tsvg={<InfoIcon weight=\"fill\" />}\n\t\t\t\t\t\t{...props}\n\t\t\t\t\t/>\n\t\t\t\t);\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unreachable Case: ${ctx.priority}`);\n\t\t}\n\t},\n);\nIcon.displayName = \"ToastIcon\";\n\ntype ToastActionProps = ComponentProps<\"button\"> & WithAsChild;\n\n/**\n * A button that dismisses the toast when clicked.\n * You can prevent the toast from being dismissed `onClick` by calling `event.preventDefault()`\n *\n * @see https://mantle.ngrok.com/components/toast#toastaction\n *\n * @example\n * ```tsx\n * <Toast.Root priority=\"info\">\n * <Toast.Icon />\n * <Toast.Message>File uploaded successfully</Toast.Message>\n * <Toast.Action>View File</Toast.Action>\n * </Toast.Root>\n * ```\n */\nconst Action = forwardRef<ComponentRef<\"button\">, ToastActionProps>(\n\t({ asChild, className, onClick, ...props }, ref) => {\n\t\tconst ctx = useContext(ToastIdContext);\n\n\t\tconst Component = asChild ? Slot : \"button\";\n\n\t\treturn (\n\t\t\t<Component\n\t\t\t\tclassName={cx(\n\t\t\t\t\t//,\n\t\t\t\t\t\"shrink-0\",\n\t\t\t\t\t// 👇 wiggle the bits so that icon buttons toast actions are aligned with the toast icon\n\t\t\t\t\t\"data-icon-button:-mr-0.5 data-icon-button:-mt-0.5 data-icon-button:rounded-xs\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tonClick={(event) => {\n\t\t\t\t\tonClick?.(event);\n\t\t\t\t\tif (event.defaultPrevented) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tToastPrimitive.toast.dismiss(ctx);\n\t\t\t\t}}\n\t\t\t\tref={ref}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\nAction.displayName = \"ToastAction\";\n\ntype ToastMessageProps = ComponentProps<\"p\"> & WithAsChild;\n\n/**\n * The message content of the toast.\n *\n * @see https://mantle.ngrok.com/components/toast#toastmessage\n *\n * @example\n * ```tsx\n * <Toast.Root priority=\"success\">\n * <Toast.Icon />\n * <Toast.Message>Your changes have been saved</Toast.Message>\n * </Toast.Root>\n * ```\n */\nconst Message = forwardRef<ComponentRef<\"p\">, ToastMessageProps>(\n\t({ asChild, className, ...props }, ref) => {\n\t\tconst Component = asChild ? Slot : \"p\";\n\n\t\treturn (\n\t\t\t<Component\n\t\t\t\t//\n\t\t\t\tclassName={cx(\"text-strong flex-1 text-sm font-body\", className)}\n\t\t\t\tref={ref}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\nMessage.displayName = \"ToastMessage\";\n\n/**\n * A succinct message that is displayed temporarily. Toasts are used to provide\n * feedback to the user without interrupting their workflow.\n *\n * @see https://mantle.ngrok.com/components/toast\n *\n * @example\n * ```tsx\n * makeToast(\n * <Toast.Root priority=\"success\">\n * <Toast.Icon />\n * <Toast.Message>Operation completed successfully!</Toast.Message>\n * <Toast.Action>Dismiss</Toast.Action>\n * </Toast.Root>\n * );\n * ```\n */\nconst Toast = {\n\t/**\n\t * A succinct message with a priority that is displayed temporarily.\n\t *\n\t * @see https://mantle.ngrok.com/components/toast#toastroot\n\t *\n\t * @example\n\t * ```tsx\n\t * <Toast.Root priority=\"success\">\n\t * <Toast.Icon />\n\t * <Toast.Message>Changes saved successfully!</Toast.Message>\n\t * <Toast.Action>Undo</Toast.Action>\n\t * </Toast.Root>\n\t * ```\n\t */\n\tRoot,\n\t/**\n\t * A button that dismisses the toast when clicked.\n\t *\n\t * @see https://mantle.ngrok.com/components/toast#toastaction\n\t *\n\t * @example\n\t * ```tsx\n\t * <Toast.Root priority=\"info\">\n\t * <Toast.Icon />\n\t * <Toast.Message>File uploaded successfully</Toast.Message>\n\t * <Toast.Action>View File</Toast.Action>\n\t * </Toast.Root>\n\t * ```\n\t */\n\tAction,\n\t/**\n\t * An icon that visually represents the priority of the toast.\n\t *\n\t * @see https://mantle.ngrok.com/components/toast#toasticon\n\t *\n\t * @example\n\t * ```tsx\n\t * <Toast.Root priority=\"warning\">\n\t * <Toast.Icon />\n\t * <Toast.Message>Warning message</Toast.Message>\n\t * </Toast.Root>\n\t * ```\n\t */\n\tIcon,\n\t/**\n\t * The message content of the toast.\n\t *\n\t * @see https://mantle.ngrok.com/components/toast#toastmessage\n\t *\n\t * @example\n\t * ```tsx\n\t * <Toast.Root priority=\"success\">\n\t * <Toast.Icon />\n\t * <Toast.Message>Your changes have been saved</Toast.Message>\n\t * </Toast.Root>\n\t * ```\n\t */\n\tMessage,\n} as const;\n\nexport {\n\t//,\n\tmakeToast,\n\tToast,\n\tToaster,\n};\n\nexport type {\n\t//,\n\tPriority,\n};\n\n/**\n * @private\n *\n * Allows any mantle floating prompt (e.g. toasts and notifications) to be interacted with\n * even when a modaled view (e.g. dialog, sheet, etc) is open and a focus trap is active.\n *\n * Without this, interacting with the prompt would close the modaled view.\n *\n * @example\n * ```tsx\n * <Dialog.Root onInteractOutside={preventCloseOnPromptInteraction}>\n * <Dialog.Content>\n * <p>Dialog content</p>\n * </Dialog.Content>\n * </Dialog.Root>\n * ```\n */\nexport function preventCloseOnPromptInteraction(\n\tevent: CustomEvent | PointerEvent | MouseEvent | TouchEvent | FocusEvent,\n) {\n\tif (!(event.target instanceof Element)) {\n\t\treturn;\n\t}\n\n\tif (event.target.closest(\".overlay-prompt\")) {\n\t\tevent.preventDefault();\n\t}\n}\n\nconst priorityBackgroundColor = {\n\tinfo: \"bg-accent-600\",\n\twarning: \"bg-warning-600\",\n\tsuccess: \"bg-success-600\",\n\tdanger: \"bg-danger-600\",\n} as const satisfies Record<Priority, string>;\n\ntype PriorityBarAccentProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n\tpriority: Priority;\n};\n\n/**\n * @private\n *\n * A colored bar that visually represents the priority of the toast.\n */\nfunction PriorityBarAccent({ className, priority, ...props }: PriorityBarAccentProps) {\n\treturn (\n\t\t<div\n\t\t\taria-hidden\n\t\t\tclassName={cx(\n\t\t\t\t//\n\t\t\t\t\"z-1 absolute -inset-px right-auto w-1.5 rounded-l\",\n\t\t\t\tpriorityBackgroundColor[priority],\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"toast-BhAyUXKL.js","names":["IconComponent"],"sources":["../src/components/toast/toast.tsx"],"sourcesContent":["\"use client\";\n\nimport { CheckCircleIcon } from \"@phosphor-icons/react/CheckCircle\";\nimport { InfoIcon } from \"@phosphor-icons/react/Info\";\nimport { WarningIcon } from \"@phosphor-icons/react/Warning\";\nimport { WarningDiamondIcon } from \"@phosphor-icons/react/WarningDiamond\";\nimport {\n\ttype ComponentProps,\n\ttype ComponentRef,\n\ttype ReactNode,\n\tcreateContext,\n\tforwardRef,\n\tuseContext,\n} from \"react\";\nimport * as ToastPrimitive from \"sonner\";\nimport type { WithAsChild } from \"../../types/as-child.js\";\nimport type { WithStyleProps } from \"../../types/with-style-props.js\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { Icon as IconComponent } from \"../icon/icon.js\";\nimport type { SvgOnlyProps } from \"../icon/svg-only.js\";\nimport { Slot } from \"../slot/index.js\";\nimport { useAppliedTheme } from \"../theme/theme-provider.js\";\n\ntype ToasterPrimitiveProps = ComponentProps<typeof ToastPrimitive.Toaster>;\ntype ToasterPrimitiveTheme = ToasterPrimitiveProps[\"theme\"];\n\ntype ToasterProps = WithStyleProps &\n\tPick<ToasterPrimitiveProps, \"containerAriaLabel\" | \"dir\" | \"position\"> & {\n\t\t/**\n\t\t * Time in milliseconds that should elapse before automatically dismissing toasts.\n\t\t * When set here, this will be the default duration for all toasts.\n\t\t * @default 4000\n\t\t */\n\t\tduration_ms?: number;\n\t};\n\n/**\n * A container for displaying all toasts.\n *\n * Only one `<Toaster />` should be rendered in an app a time, preferably at the\n * root level of the app.\n *\n * @see https://mantle.ngrok.com/components/toast#toaster\n *\n * @example\n * ```tsx\n * <Toaster\n * position=\"top-right\"\n * duration_ms={5000}\n * />\n * ```\n */\nconst Toaster = ({\n\t//,\n\tclassName,\n\tcontainerAriaLabel,\n\tdir,\n\tduration_ms = 4000,\n\tposition = \"top-center\",\n\tstyle,\n}: ToasterProps) => {\n\tconst theme = useAppliedTheme();\n\n\treturn (\n\t\t<ToastPrimitive.Toaster\n\t\t\tclassName={cx(\"toaster overlay-prompt pointer-events-auto *:duration-200\", className)}\n\t\t\tcontainerAriaLabel={containerAriaLabel}\n\t\t\tdir={dir}\n\t\t\tduration={duration_ms}\n\t\t\tgap={12}\n\t\t\tposition={position ?? \"top-center\"}\n\t\t\tstyle={style}\n\t\t\ttheme={theme as ToasterPrimitiveTheme} // we have additional themes that are not in the sonner types, so we need to cast for now\n\t\t\ttoastOptions={{\n\t\t\t\tunstyled: true,\n\t\t\t}}\n\t\t/>\n\t);\n};\nToaster.displayName = \"Toaster\";\n\nconst ToastIdContext = createContext<string | number>(\"\");\n\ntype MakeToastOptions = {\n\t/**\n\t * Time in milliseconds that should elapse before automatically closing the toast.\n\t * Will default to the `<Toaster />`'s `duration_ms` if not provided.\n\t *\n\t * You can keep the toast open until manually dismissed by passing a value <= 0 or Number.POSITIVE_INFINITY\n\t */\n\tduration_ms?: number;\n\t/**\n\t * An optional custom ID for this toast. If not given, a unique ID is provided for you.\n\t */\n\tid?: string;\n};\n\n/**\n * Create a toast. Provide a `<Toast.Root>` component as the `children` to be rendered\n * inside the `<Toaster />` section.\n *\n * @see https://mantle.ngrok.com/components/toast#maketoast\n *\n * @example\n * ```tsx\n * // Basic toast with auto-dismiss (default 4000ms, inherits from `<Toaster>`)\n * makeToast(\n * <Toast.Root priority=\"success\">\n * <Toast.Icon />\n * <Toast.Message>Operation completed successfully!</Toast.Message>\n * <Toast.Action>Dismiss</Toast.Action>\n * </Toast.Root>\n * );\n *\n * // Toast that stays open until manually dismissed\n * makeToast(\n * <Toast.Root priority=\"warning\">\n * <Toast.Icon />\n * <Toast.Message>Action required</Toast.Message>\n * <Toast.Action>Dismiss</Toast.Action>\n * </Toast.Root>,\n * { duration_ms: Number.POSITIVE_INFINITY }\n * );\n * ```\n */\nfunction makeToast(children: ReactNode, options?: MakeToastOptions) {\n\tlet duration = options?.duration_ms;\n\tif (typeof duration === \"number\" && duration <= 0) {\n\t\tduration = Number.POSITIVE_INFINITY;\n\t}\n\n\treturn ToastPrimitive.toast.custom(\n\t\t(toastId) => <ToastIdContext.Provider value={toastId}>{children}</ToastIdContext.Provider>,\n\t\t{\n\t\t\t//\n\t\t\tduration,\n\t\t\t// If a custom ID is provided, use it, else use the toastId provided by the sonner library\n\t\t\t// don't set an ID to `undefined` as it breaks the sonner library\n\t\t\t...(options?.id ? { id: options.id } : {}),\n\t\t\tunstyled: true,\n\t\t},\n\t);\n}\n\nconst priorities = [\n\t//,\n\t\"danger\",\n\t\"info\",\n\t\"success\",\n\t\"warning\",\n] as const;\ntype Priority = (typeof priorities)[number];\n\ntype ToastState = {\n\tpriority: Priority;\n};\n\nconst ToastStateContext = createContext<ToastState>({\n\tpriority: \"info\",\n});\n\ntype ToastProps = ComponentProps<\"div\"> &\n\tWithAsChild & {\n\t\tpriority: Priority;\n\t};\n\n/**\n * A succinct message with a priority that is displayed temporarily.\n * Toasts are used to provide feedback to the user without interrupting their workflow.\n *\n * @see https://mantle.ngrok.com/components/toast#toastroot\n *\n * @example\n * ```tsx\n * <Toast.Root priority=\"success\">\n * <Toast.Icon />\n * <Toast.Message>Changes saved successfully!</Toast.Message>\n * <Toast.Action>Undo</Toast.Action>\n * </Toast.Root>\n * ```\n */\nconst Root = forwardRef<ComponentRef<\"div\">, ToastProps>(\n\t({ asChild, children, className, priority, ...props }, ref) => {\n\t\tconst Component = asChild ? Slot : \"div\";\n\n\t\treturn (\n\t\t\t<ToastStateContext.Provider value={{ priority }}>\n\t\t\t\t<Component\n\t\t\t\t\tclassName={cx(\n\t\t\t\t\t\t\"relative flex items-start gap-2 text-sm font-sans\",\n\t\t\t\t\t\t\"p-3 pl-3.75\",\n\t\t\t\t\t\t\"bg-popover high-contrast:border-popover rounded rounded-r-[0.3125rem] border border-gray-500/35 shadow-lg\",\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * Do not apply overflow-hidden because we want the priority bar accent\n\t\t\t\t\t\t * to overlap the toast border, else the border flows over the\n\t\t\t\t\t\t * priority bar.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tclassName,\n\t\t\t\t\t)}\n\t\t\t\t\tref={ref}\n\t\t\t\t\t{...props}\n\t\t\t\t>\n\t\t\t\t\t<PriorityBarAccent priority={priority} />\n\t\t\t\t\t{children}\n\t\t\t\t</Component>\n\t\t\t</ToastStateContext.Provider>\n\t\t);\n\t},\n);\nRoot.displayName = \"Toast\";\n\ntype ToastIconProps = Partial<SvgOnlyProps>;\n\n/**\n * An icon that visually represents the priority of the toast.\n * If you do not provide an icon, the default icon and color for the priority is used.\n *\n * @see https://mantle.ngrok.com/components/toast#toasticon\n *\n * @example\n * ```tsx\n * <Toast.Root priority=\"warning\">\n * <Toast.Icon />\n * <Toast.Message>Warning message</Toast.Message>\n * </Toast.Root>\n * ```\n */\nconst Icon = forwardRef<ComponentRef<\"svg\">, ToastIconProps>(\n\t({ className, svg, ...props }, ref) => {\n\t\tconst ctx = useContext(ToastStateContext);\n\n\t\tswitch (ctx.priority) {\n\t\t\tcase \"danger\":\n\t\t\t\treturn (\n\t\t\t\t\t<IconComponent\n\t\t\t\t\t\tclassName={cx(\"text-danger-600\", className)}\n\t\t\t\t\t\tref={ref}\n\t\t\t\t\t\tsvg={svg ?? <WarningIcon weight=\"fill\" />}\n\t\t\t\t\t\t{...props}\n\t\t\t\t\t/>\n\t\t\t\t);\n\t\t\tcase \"warning\":\n\t\t\t\treturn (\n\t\t\t\t\t<IconComponent\n\t\t\t\t\t\tclassName={cx(\"text-warning-600\", className)}\n\t\t\t\t\t\tref={ref}\n\t\t\t\t\t\tsvg={svg ?? <WarningDiamondIcon weight=\"fill\" />}\n\t\t\t\t\t\t{...props}\n\t\t\t\t\t/>\n\t\t\t\t);\n\t\t\tcase \"success\":\n\t\t\t\treturn (\n\t\t\t\t\t<IconComponent\n\t\t\t\t\t\tclassName={cx(\"text-success-600\", className)}\n\t\t\t\t\t\tref={ref}\n\t\t\t\t\t\tsvg={svg ?? <CheckCircleIcon weight=\"fill\" />}\n\t\t\t\t\t\t{...props}\n\t\t\t\t\t/>\n\t\t\t\t);\n\t\t\tcase \"info\":\n\t\t\t\treturn (\n\t\t\t\t\t<IconComponent\n\t\t\t\t\t\t//\n\t\t\t\t\t\tclassName={cx(\"text-accent-600\", className)}\n\t\t\t\t\t\tref={ref}\n\t\t\t\t\t\tsvg={<InfoIcon weight=\"fill\" />}\n\t\t\t\t\t\t{...props}\n\t\t\t\t\t/>\n\t\t\t\t);\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unreachable Case: ${ctx.priority}`);\n\t\t}\n\t},\n);\nIcon.displayName = \"ToastIcon\";\n\ntype ToastActionProps = ComponentProps<\"button\"> & WithAsChild;\n\n/**\n * A button that dismisses the toast when clicked.\n * You can prevent the toast from being dismissed `onClick` by calling `event.preventDefault()`\n *\n * @see https://mantle.ngrok.com/components/toast#toastaction\n *\n * @example\n * ```tsx\n * <Toast.Root priority=\"info\">\n * <Toast.Icon />\n * <Toast.Message>File uploaded successfully</Toast.Message>\n * <Toast.Action>View File</Toast.Action>\n * </Toast.Root>\n * ```\n */\nconst Action = forwardRef<ComponentRef<\"button\">, ToastActionProps>(\n\t({ asChild, className, onClick, ...props }, ref) => {\n\t\tconst ctx = useContext(ToastIdContext);\n\n\t\tconst Component = asChild ? Slot : \"button\";\n\n\t\treturn (\n\t\t\t<Component\n\t\t\t\tclassName={cx(\n\t\t\t\t\t//,\n\t\t\t\t\t\"shrink-0\",\n\t\t\t\t\t// 👇 wiggle the bits so that icon buttons toast actions are aligned with the toast icon\n\t\t\t\t\t\"data-icon-button:-mr-0.5 data-icon-button:-mt-0.5 data-icon-button:rounded-xs\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tonClick={(event) => {\n\t\t\t\t\tonClick?.(event);\n\t\t\t\t\tif (event.defaultPrevented) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tToastPrimitive.toast.dismiss(ctx);\n\t\t\t\t}}\n\t\t\t\tref={ref}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\nAction.displayName = \"ToastAction\";\n\ntype ToastMessageProps = ComponentProps<\"p\"> & WithAsChild;\n\n/**\n * The message content of the toast.\n *\n * @see https://mantle.ngrok.com/components/toast#toastmessage\n *\n * @example\n * ```tsx\n * <Toast.Root priority=\"success\">\n * <Toast.Icon />\n * <Toast.Message>Your changes have been saved</Toast.Message>\n * </Toast.Root>\n * ```\n */\nconst Message = forwardRef<ComponentRef<\"p\">, ToastMessageProps>(\n\t({ asChild, className, ...props }, ref) => {\n\t\tconst Component = asChild ? Slot : \"p\";\n\n\t\treturn (\n\t\t\t<Component\n\t\t\t\t//\n\t\t\t\tclassName={cx(\"text-strong flex-1 text-sm font-body\", className)}\n\t\t\t\tref={ref}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\nMessage.displayName = \"ToastMessage\";\n\n/**\n * A succinct message that is displayed temporarily. Toasts are used to provide\n * feedback to the user without interrupting their workflow.\n *\n * @see https://mantle.ngrok.com/components/toast\n *\n * @example\n * ```tsx\n * makeToast(\n * <Toast.Root priority=\"success\">\n * <Toast.Icon />\n * <Toast.Message>Operation completed successfully!</Toast.Message>\n * <Toast.Action>Dismiss</Toast.Action>\n * </Toast.Root>\n * );\n * ```\n */\nconst Toast = {\n\t/**\n\t * A succinct message with a priority that is displayed temporarily.\n\t *\n\t * @see https://mantle.ngrok.com/components/toast#toastroot\n\t *\n\t * @example\n\t * ```tsx\n\t * <Toast.Root priority=\"success\">\n\t * <Toast.Icon />\n\t * <Toast.Message>Changes saved successfully!</Toast.Message>\n\t * <Toast.Action>Undo</Toast.Action>\n\t * </Toast.Root>\n\t * ```\n\t */\n\tRoot,\n\t/**\n\t * A button that dismisses the toast when clicked.\n\t *\n\t * @see https://mantle.ngrok.com/components/toast#toastaction\n\t *\n\t * @example\n\t * ```tsx\n\t * <Toast.Root priority=\"info\">\n\t * <Toast.Icon />\n\t * <Toast.Message>File uploaded successfully</Toast.Message>\n\t * <Toast.Action>View File</Toast.Action>\n\t * </Toast.Root>\n\t * ```\n\t */\n\tAction,\n\t/**\n\t * An icon that visually represents the priority of the toast.\n\t *\n\t * @see https://mantle.ngrok.com/components/toast#toasticon\n\t *\n\t * @example\n\t * ```tsx\n\t * <Toast.Root priority=\"warning\">\n\t * <Toast.Icon />\n\t * <Toast.Message>Warning message</Toast.Message>\n\t * </Toast.Root>\n\t * ```\n\t */\n\tIcon,\n\t/**\n\t * The message content of the toast.\n\t *\n\t * @see https://mantle.ngrok.com/components/toast#toastmessage\n\t *\n\t * @example\n\t * ```tsx\n\t * <Toast.Root priority=\"success\">\n\t * <Toast.Icon />\n\t * <Toast.Message>Your changes have been saved</Toast.Message>\n\t * </Toast.Root>\n\t * ```\n\t */\n\tMessage,\n} as const;\n\nexport {\n\t//,\n\tmakeToast,\n\tToast,\n\tToaster,\n};\n\nexport type {\n\t//,\n\tPriority,\n};\n\n/**\n * @private\n *\n * Allows any mantle floating prompt (e.g. toasts and notifications) to be interacted with\n * even when a modaled view (e.g. dialog, sheet, etc) is open and a focus trap is active.\n *\n * Without this, interacting with the prompt would close the modaled view.\n *\n * @example\n * ```tsx\n * <Dialog.Root onInteractOutside={preventCloseOnPromptInteraction}>\n * <Dialog.Content>\n * <p>Dialog content</p>\n * </Dialog.Content>\n * </Dialog.Root>\n * ```\n */\nexport function preventCloseOnPromptInteraction(\n\tevent: CustomEvent | PointerEvent | MouseEvent | TouchEvent | FocusEvent,\n) {\n\tif (!(event.target instanceof Element)) {\n\t\treturn;\n\t}\n\n\tif (event.target.closest(\".overlay-prompt\")) {\n\t\tevent.preventDefault();\n\t}\n}\n\nconst priorityBackgroundColor = {\n\tinfo: \"bg-accent-600\",\n\twarning: \"bg-warning-600\",\n\tsuccess: \"bg-success-600\",\n\tdanger: \"bg-danger-600\",\n} as const satisfies Record<Priority, string>;\n\ntype PriorityBarAccentProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n\tpriority: Priority;\n};\n\n/**\n * @private\n *\n * A colored bar that visually represents the priority of the toast.\n */\nfunction PriorityBarAccent({ className, priority, ...props }: PriorityBarAccentProps) {\n\treturn (\n\t\t<div\n\t\t\taria-hidden\n\t\t\tclassName={cx(\n\t\t\t\t//\n\t\t\t\t\"z-1 absolute -inset-px right-auto w-1.5 rounded-l\",\n\t\t\t\tpriorityBackgroundColor[priority],\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n"],"mappings":"gjBAoDA,MAAM,GAAW,CAEhB,YACA,qBACA,MACA,cAAc,IACd,WAAW,aACX,WACmB,CACnB,IAAM,EAAQ,GAAiB,CAE/B,OACC,EAAC,EAAe,QAAhB,CACC,UAAW,EAAG,4DAA6D,EAAU,CACjE,qBACf,MACL,SAAU,EACV,IAAK,GACL,SAAU,GAAY,aACf,QACA,QACP,aAAc,CACb,SAAU,GACV,CACA,CAAA,EAGJ,EAAQ,YAAc,UAEtB,MAAM,EAAiB,EAA+B,GAAG,CA4CzD,SAAS,EAAU,EAAqB,EAA4B,CACnE,IAAI,EAAW,GAAS,YAKxB,OAJI,OAAO,GAAa,UAAY,GAAY,IAC/C,EAAW,KAGL,EAAe,MAAM,OAC1B,GAAY,EAAC,EAAe,SAAhB,CAAyB,MAAO,EAAU,WAAmC,CAAA,CAC1F,CAEC,WAGA,GAAI,GAAS,GAAK,CAAE,GAAI,EAAQ,GAAI,CAAG,EAAE,CACzC,SAAU,GACV,CACD,CAgBF,MAAM,EAAoB,EAA0B,CACnD,SAAU,OACV,CAAC,CAsBI,EAAO,GACX,CAAE,UAAS,WAAU,YAAW,WAAU,GAAG,GAAS,IAAQ,CAC9D,IAAM,EAAY,EAAU,EAAO,MAEnC,OACC,EAAC,EAAkB,SAAnB,CAA4B,MAAO,CAAE,WAAU,UAC9C,EAAC,EAAD,CACC,UAAW,EACV,oDACA,cACA,4GAMA,EACA,CACI,MACL,GAAI,WAbL,CAeC,EAAC,EAAD,CAA6B,WAAY,CAAA,CACxC,EACU,GACgB,CAAA,EAG/B,CACD,EAAK,YAAc,QAkBnB,MAAM,EAAO,GACX,CAAE,YAAW,MAAK,GAAG,GAAS,IAAQ,CACtC,IAAM,EAAM,EAAW,EAAkB,CAEzC,OAAQ,EAAI,SAAZ,CACC,IAAK,SACJ,OACC,EAACA,EAAD,CACC,UAAW,EAAG,kBAAmB,EAAU,CACtC,MACL,IAAK,GAAO,EAAC,EAAD,CAAa,OAAO,OAAS,CAAA,CACzC,GAAI,EACH,CAAA,CAEJ,IAAK,UACJ,OACC,EAACA,EAAD,CACC,UAAW,EAAG,mBAAoB,EAAU,CACvC,MACL,IAAK,GAAO,EAAC,EAAD,CAAoB,OAAO,OAAS,CAAA,CAChD,GAAI,EACH,CAAA,CAEJ,IAAK,UACJ,OACC,EAACA,EAAD,CACC,UAAW,EAAG,mBAAoB,EAAU,CACvC,MACL,IAAK,GAAO,EAAC,EAAD,CAAiB,OAAO,OAAS,CAAA,CAC7C,GAAI,EACH,CAAA,CAEJ,IAAK,OACJ,OACC,EAACA,EAAD,CAEC,UAAW,EAAG,kBAAmB,EAAU,CACtC,MACL,IAAK,EAAC,EAAD,CAAU,OAAO,OAAS,CAAA,CAC/B,GAAI,EACH,CAAA,CAEJ,QACC,MAAU,MAAM,qBAAqB,EAAI,WAAW,GAGvD,CACD,EAAK,YAAc,YAmBnB,MAAM,EAAS,GACb,CAAE,UAAS,YAAW,UAAS,GAAG,GAAS,IAAQ,CACnD,IAAM,EAAM,EAAW,EAAe,CAItC,OACC,EAHiB,EAAU,EAAO,SAGlC,CACC,UAAW,EAEV,WAEA,gFACA,EACA,CACD,QAAU,GAAU,CACnB,IAAU,EAAM,CACZ,GAAM,kBAGV,EAAe,MAAM,QAAQ,EAAI,EAE7B,MACL,GAAI,EACH,CAAA,EAGJ,CACD,EAAO,YAAc,cAiBrB,MAAM,EAAU,GACd,CAAE,UAAS,YAAW,GAAG,GAAS,IAIjC,EAHiB,EAAU,EAAO,IAGlC,CAEC,UAAW,EAAG,uCAAwC,EAAU,CAC3D,MACL,GAAI,EACH,CAAA,CAGJ,CACD,EAAQ,YAAc,eAmBtB,MAAM,EAAQ,CAeb,OAeA,SAcA,OAcA,UACA,CA+BD,SAAgB,EACf,EACC,CACK,EAAM,kBAAkB,SAI1B,EAAM,OAAO,QAAQ,kBAAkB,EAC1C,EAAM,gBAAgB,CAIxB,MAAM,EAA0B,CAC/B,KAAM,gBACN,QAAS,iBACT,QAAS,iBACT,OAAQ,gBACR,CAWD,SAAS,EAAkB,CAAE,YAAW,WAAU,GAAG,GAAiC,CACrF,OACC,EAAC,MAAD,CACC,cAAA,GACA,UAAW,EAEV,oDACA,EAAwB,GACxB,EACA,CACD,GAAI,EACH,CAAA"}
|
package/dist/toast.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e,r as t,t as n}from"./toast-
|
|
1
|
+
import{n as e,r as t,t as n}from"./toast-BhAyUXKL.js";export{n as Toast,e as Toaster,t as makeToast};
|
package/dist/tooltip.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./cx-
|
|
1
|
+
import{t as e}from"./cx-D1HYnpvA.js";import{forwardRef as t}from"react";import{jsx as n,jsxs as r}from"react/jsx-runtime";import*as i from"@radix-ui/react-tooltip";const a=({delayDuration:e=0,...t})=>n(i.Provider,{"data-slot":`tooltip-provider`,delayDuration:e??0,...t});a.displayName=`Tooltip.Provider`;function o(e){return n(i.Root,{"data-slot":`tooltip`,...e})}o.displayName=`Tooltip.Root`;function s(e){return n(i.Trigger,{"data-slot":`tooltip-trigger`,...e})}s.displayName=`Tooltip.Trigger`;const c=t(({children:t,className:a,sideOffset:o=4,...s},c)=>n(i.Portal,{children:r(i.Content,{className:e(`bg-tooltip text-tooltip animate-in fade-in-0 zoom-in-95 data-side-bottom:slide-in-from-top-2 data-side-left:slide-in-from-right-2 data-side-right:slide-in-from-left-2 data-side-top:slide-in-from-bottom-2 data-state-closed:animate-out data-state-closed:fade-out-0 data-state-closed:zoom-out-95 z-50 max-w-72 overflow-visible wrap-break-word rounded-md px-3 py-1.5 text-sm font-sans shadow`,a),"data-slot":`tooltip-content`,ref:c,sideOffset:o,...s,children:[t,n(i.Arrow,{asChild:!0,children:n(`div`,{className:`bg-tooltip z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-xs`})})]})}));c.displayName=`Tooltip.Content`;const l={Root:o,Content:c,Trigger:s};export{l as Tooltip,a as TooltipProvider};
|
|
2
2
|
//# sourceMappingURL=tooltip.js.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";function n(n){return t(`svg`,{fill:`currentColor`,height:`1em`,viewBox:`0 0 256 256`,width:`1em`,...n,children:[e(`path`,{fill:`none`,d:`M0 0h256v256H0z`}),e(`path`,{d:`m213.7 82.3-56-56c-1.5-1.5-3.5-2.3-5.7-2.3H56c-8.8 0-16 7.2-16 16v88c0 4.4 3.6 8 8 8s8-3.6 8-8V40h88v48c0 4.4 3.6 8 8 8h48v120h-40c-4.4 0-8 3.6-8 8s3.6 8 8 8h40c8.8 0 16-7.2 16-16V88c0-2.1-.8-4.2-2.3-5.7zm-53.7-31L188.7 80H160V51.3z`}),e(`path`,{d:`M124.6 194.5h-6.8v-27.3h6.8c1.9 0 3.4-1.5 3.4-3.4s-1.5-3.4-3.4-3.4h-6.8v-10.2c0-3.8-3.1-6.8-6.8-6.8H63.3c-3.8 0-6.8 3.1-6.8 6.8v10.2h-6.8c-1.9 0-3.4 1.5-3.4 3.4s1.5 3.4 3.4 3.4h6.8v27.3h-6.8c-1.9 0-3.4 1.5-3.4 3.4s1.5 3.4 3.4 3.4h6.8v23.9c0 3.8 3.1 6.8 6.8 6.8H111c3.8 0 6.8-3.1 6.8-6.8v-23.9h6.8c1.9 0 3.4-1.5 3.4-3.4s-1.5-3.4-3.4-3.4zm-37.5-11.9c-6.6 0-11.9-5.3-11.9-11.9s5.3-11.9 11.9-11.9S99 164.1 99 170.7s-5.3 11.9-11.9 11.9zm0 10.2c6.6 0 11.9 5.3 11.9 11.9s-5.3 11.9-11.9 11.9-11.9-5.3-11.9-11.9 5.3-11.9 11.9-11.9z`})]})}n.displayName=`TrafficPolicyFileIcon`;export{n as t};
|
|
2
|
+
//# sourceMappingURL=traffic-policy-file-CroUMbsd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traffic-policy-file-CroUMbsd.js","names":[],"sources":["../src/components/icons/traffic-policy-file.tsx"],"sourcesContent":["import type { SvgAttributes } from \"../icon/types.js\";\n\n/**\n * An icon representing a traffic policy file.\n */\nfunction TrafficPolicyFileIcon(props: SvgAttributes) {\n\treturn (\n\t\t<svg fill=\"currentColor\" height=\"1em\" viewBox=\"0 0 256 256\" width=\"1em\" {...props}>\n\t\t\t<path fill=\"none\" d=\"M0 0h256v256H0z\" />\n\t\t\t<path d=\"m213.7 82.3-56-56c-1.5-1.5-3.5-2.3-5.7-2.3H56c-8.8 0-16 7.2-16 16v88c0 4.4 3.6 8 8 8s8-3.6 8-8V40h88v48c0 4.4 3.6 8 8 8h48v120h-40c-4.4 0-8 3.6-8 8s3.6 8 8 8h40c8.8 0 16-7.2 16-16V88c0-2.1-.8-4.2-2.3-5.7zm-53.7-31L188.7 80H160V51.3z\" />\n\t\t\t<path d=\"M124.6 194.5h-6.8v-27.3h6.8c1.9 0 3.4-1.5 3.4-3.4s-1.5-3.4-3.4-3.4h-6.8v-10.2c0-3.8-3.1-6.8-6.8-6.8H63.3c-3.8 0-6.8 3.1-6.8 6.8v10.2h-6.8c-1.9 0-3.4 1.5-3.4 3.4s1.5 3.4 3.4 3.4h6.8v27.3h-6.8c-1.9 0-3.4 1.5-3.4 3.4s1.5 3.4 3.4 3.4h6.8v23.9c0 3.8 3.1 6.8 6.8 6.8H111c3.8 0 6.8-3.1 6.8-6.8v-23.9h6.8c1.9 0 3.4-1.5 3.4-3.4s-1.5-3.4-3.4-3.4zm-37.5-11.9c-6.6 0-11.9-5.3-11.9-11.9s5.3-11.9 11.9-11.9S99 164.1 99 170.7s-5.3 11.9-11.9 11.9zm0 10.2c6.6 0 11.9 5.3 11.9 11.9s-5.3 11.9-11.9 11.9-11.9-5.3-11.9-11.9 5.3-11.9 11.9-11.9z\" />\n\t\t</svg>\n\t);\n}\nTrafficPolicyFileIcon.displayName = \"TrafficPolicyFileIcon\";\n\nexport {\n\t//,\n\tTrafficPolicyFileIcon,\n};\n"],"mappings":"kDAKA,SAAS,EAAsB,EAAsB,CACpD,OACC,EAAC,MAAD,CAAK,KAAK,eAAe,OAAO,MAAM,QAAQ,cAAc,MAAM,MAAM,GAAI,WAA5E,CACC,EAAC,OAAD,CAAM,KAAK,OAAO,EAAE,kBAAoB,CAAA,CACxC,EAAC,OAAD,CAAM,EAAE,2OAA6O,CAAA,CACrP,EAAC,OAAD,CAAM,EAAE,6gBAA+gB,CAAA,CAClhB,GAGR,EAAsB,YAAc"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ngrok/mantle",
|
|
3
|
-
"version": "0.66.
|
|
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.
|
|
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.
|
|
353
|
+
"tsdown": "0.21.3",
|
|
354
354
|
"typescript": "5.9.3",
|
|
355
355
|
"@cfg/tsconfig": "1.0.0"
|
|
356
356
|
},
|
package/dist/cx-bKromGBh.js
DELETED
package/dist/cx-bKromGBh.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cx-bKromGBh.js","names":[],"sources":["../src/utils/cx/cx.ts"],"sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { extendTailwindMerge } from \"tailwind-merge\";\n\nconst twMerge = extendTailwindMerge({\n\textend: {\n\t\tclassGroups: {\n\t\t\t\"font-size\": [\"text-mono\", \"text-size-inherit\"],\n\t\t},\n\t},\n});\n\n/**\n * Conditionally add Tailwind (and other) CSS classes.\n *\n * Allows for tailwind overrides in LTR-specificity-like order of applied classes.\n */\nexport function cx(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n"],"mappings":"iFAGA,MAAM,EAAU,EAAoB,CACnC,OAAQ,CACP,YAAa,CACZ,YAAa,CAAC,YAAa,oBAAoB,CAC/C,CACD,CACD,CAAC,CAOF,SAAgB,EAAG,GAAG,EAAsB,CAC3C,OAAO,EAAQ,EAAK,EAAO,CAAC"}
|
package/dist/icon-B5oNYYrJ.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./cx-bKromGBh.js";import{t}from"./svg-only-BVLlbQ4e.js";import{forwardRef as n}from"react";import{jsx as r}from"react/jsx-runtime";const i=n(({className:n,style:i,svg:a,...o},s)=>r(t,{ref:s,className:e(`size-5`,n),style:i,svg:a,...o}));i.displayName=`Icon`;export{i as t};
|
|
2
|
-
//# sourceMappingURL=icon-B5oNYYrJ.js.map
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{jsx as e,jsxs as t}from"react/jsx-runtime";function n(n){return t(`svg`,{xmlns:`http://www.w3.org/2000/svg`,width:`1em`,height:`1em`,fill:`currentColor`,viewBox:`0 0 256 256`,...n,children:[e(`path`,{fill:`none`,d:`M0 0h256v256H0z`}),e(`path`,{d:`m213.7 82.3-56-56c-1.5-1.5-3.5-2.3-5.7-2.3H56c-8.8 0-16 7.2-16 16v88c0 4.4 3.6 8 8 8s8-3.6 8-8V40h88v48c0 4.4 3.6 8 8 8h48v120h-40c-4.4 0-8 3.6-8 8s3.6 8 8 8h40c8.8 0 16-7.2 16-16V88c0-2.1-.8-4.2-2.3-5.7zm-53.7-31L188.7 80H160V51.3z`}),e(`path`,{d:`M124.6 194.5h-6.8v-27.3h6.8c1.9 0 3.4-1.5 3.4-3.4s-1.5-3.4-3.4-3.4h-6.8v-10.2c0-3.8-3.1-6.8-6.8-6.8H63.3c-3.8 0-6.8 3.1-6.8 6.8v10.2h-6.8c-1.9 0-3.4 1.5-3.4 3.4s1.5 3.4 3.4 3.4h6.8v27.3h-6.8c-1.9 0-3.4 1.5-3.4 3.4s1.5 3.4 3.4 3.4h6.8v23.9c0 3.8 3.1 6.8 6.8 6.8H111c3.8 0 6.8-3.1 6.8-6.8v-23.9h6.8c1.9 0 3.4-1.5 3.4-3.4s-1.5-3.4-3.4-3.4zm-37.5-11.9c-6.6 0-11.9-5.3-11.9-11.9s5.3-11.9 11.9-11.9S99 164.1 99 170.7s-5.3 11.9-11.9 11.9zm0 10.2c6.6 0 11.9 5.3 11.9 11.9s-5.3 11.9-11.9 11.9-11.9-5.3-11.9-11.9 5.3-11.9 11.9-11.9z`})]})}n.displayName=`TrafficPolicyFileIcon`;export{n as t};
|
|
2
|
-
//# sourceMappingURL=traffic-policy-file-QnF-2YkY.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"traffic-policy-file-QnF-2YkY.js","names":[],"sources":["../src/components/icons/traffic-policy-file.tsx"],"sourcesContent":["import type { SvgAttributes } from \"../icon/types.js\";\n\n/**\n * An icon representing a traffic policy file.\n */\nfunction TrafficPolicyFileIcon(props: SvgAttributes) {\n\treturn (\n\t\t<svg\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\twidth=\"1em\"\n\t\t\theight=\"1em\"\n\t\t\tfill=\"currentColor\"\n\t\t\tviewBox=\"0 0 256 256\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<path fill=\"none\" d=\"M0 0h256v256H0z\" />\n\t\t\t<path d=\"m213.7 82.3-56-56c-1.5-1.5-3.5-2.3-5.7-2.3H56c-8.8 0-16 7.2-16 16v88c0 4.4 3.6 8 8 8s8-3.6 8-8V40h88v48c0 4.4 3.6 8 8 8h48v120h-40c-4.4 0-8 3.6-8 8s3.6 8 8 8h40c8.8 0 16-7.2 16-16V88c0-2.1-.8-4.2-2.3-5.7zm-53.7-31L188.7 80H160V51.3z\" />\n\t\t\t<path d=\"M124.6 194.5h-6.8v-27.3h6.8c1.9 0 3.4-1.5 3.4-3.4s-1.5-3.4-3.4-3.4h-6.8v-10.2c0-3.8-3.1-6.8-6.8-6.8H63.3c-3.8 0-6.8 3.1-6.8 6.8v10.2h-6.8c-1.9 0-3.4 1.5-3.4 3.4s1.5 3.4 3.4 3.4h6.8v27.3h-6.8c-1.9 0-3.4 1.5-3.4 3.4s1.5 3.4 3.4 3.4h6.8v23.9c0 3.8 3.1 6.8 6.8 6.8H111c3.8 0 6.8-3.1 6.8-6.8v-23.9h6.8c1.9 0 3.4-1.5 3.4-3.4s-1.5-3.4-3.4-3.4zm-37.5-11.9c-6.6 0-11.9-5.3-11.9-11.9s5.3-11.9 11.9-11.9S99 164.1 99 170.7s-5.3 11.9-11.9 11.9zm0 10.2c6.6 0 11.9 5.3 11.9 11.9s-5.3 11.9-11.9 11.9-11.9-5.3-11.9-11.9 5.3-11.9 11.9-11.9z\" />\n\t\t</svg>\n\t);\n}\nTrafficPolicyFileIcon.displayName = \"TrafficPolicyFileIcon\";\n\nexport {\n\t//,\n\tTrafficPolicyFileIcon,\n};\n"],"mappings":"kDAKA,SAAS,EAAsB,EAAsB,CACpD,OACC,EAAC,MAAD,CACC,MAAM,6BACN,MAAM,MACN,OAAO,MACP,KAAK,eACL,QAAQ,cACR,GAAI,WANL,CAQC,EAAC,OAAD,CAAM,KAAK,OAAO,EAAE,kBAAoB,CAAA,CACxC,EAAC,OAAD,CAAM,EAAE,2OAA6O,CAAA,CACrP,EAAC,OAAD,CAAM,EAAE,6gBAA+gB,CAAA,CAClhB,GAGR,EAAsB,YAAc"}
|