@tribepad/themis 1.0.11 → 1.0.12
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/elements/Accordion/Accordion.types.d.ts +44 -4
- package/dist/elements/Accordion/Accordion.types.d.ts.map +1 -1
- package/dist/elements/Accordion/index.js +1 -1
- package/dist/elements/Accordion/index.js.map +1 -1
- package/dist/elements/Accordion/index.mjs +1 -1
- package/dist/elements/Accordion/index.mjs.map +1 -1
- package/dist/elements/AlertDialog/AlertDialog.types.d.ts +19 -2
- package/dist/elements/AlertDialog/AlertDialog.types.d.ts.map +1 -1
- package/dist/elements/AlertDialog/index.js +1 -1
- package/dist/elements/AlertDialog/index.js.map +1 -1
- package/dist/elements/AlertDialog/index.mjs +1 -1
- package/dist/elements/AlertDialog/index.mjs.map +1 -1
- package/dist/elements/Avatar/Avatar.d.ts +1 -21
- package/dist/elements/Avatar/Avatar.d.ts.map +1 -1
- package/dist/elements/Avatar/Avatar.types.d.ts +30 -11
- package/dist/elements/Avatar/Avatar.types.d.ts.map +1 -1
- package/dist/elements/Avatar/AvatarGroup.d.ts +1 -17
- package/dist/elements/Avatar/AvatarGroup.d.ts.map +1 -1
- package/dist/elements/Avatar/index.js +1 -1
- package/dist/elements/Avatar/index.js.map +1 -1
- package/dist/elements/Avatar/index.mjs +1 -1
- package/dist/elements/Avatar/index.mjs.map +1 -1
- package/dist/elements/Badge/Badge.types.d.ts +16 -9
- package/dist/elements/Badge/Badge.types.d.ts.map +1 -1
- package/dist/elements/Badge/index.js +1 -1
- package/dist/elements/Badge/index.js.map +1 -1
- package/dist/elements/Badge/index.mjs +1 -1
- package/dist/elements/Badge/index.mjs.map +1 -1
- package/dist/elements/Breadcrumbs/Breadcrumbs.d.ts +2 -34
- package/dist/elements/Breadcrumbs/Breadcrumbs.d.ts.map +1 -1
- package/dist/elements/Breadcrumbs/Breadcrumbs.types.d.ts +25 -2
- package/dist/elements/Breadcrumbs/Breadcrumbs.types.d.ts.map +1 -1
- package/dist/elements/Breadcrumbs/index.js +1 -1
- package/dist/elements/Breadcrumbs/index.js.map +1 -1
- package/dist/elements/Breadcrumbs/index.mjs +1 -1
- package/dist/elements/Breadcrumbs/index.mjs.map +1 -1
- package/dist/elements/Button/Button.d.ts +1 -24
- package/dist/elements/Button/Button.d.ts.map +1 -1
- package/dist/elements/Button/Button.types.d.ts +28 -7
- package/dist/elements/Button/Button.types.d.ts.map +1 -1
- package/dist/elements/ButtonGroup/ButtonGroup.d.ts +3 -14
- package/dist/elements/ButtonGroup/ButtonGroup.d.ts.map +1 -1
- package/dist/elements/ButtonGroup/ButtonGroup.types.d.ts +14 -5
- package/dist/elements/ButtonGroup/ButtonGroup.types.d.ts.map +1 -1
- package/dist/elements/ButtonGroup/index.js +1 -1
- package/dist/elements/ButtonGroup/index.js.map +1 -1
- package/dist/elements/ButtonGroup/index.mjs +1 -1
- package/dist/elements/ButtonGroup/index.mjs.map +1 -1
- package/dist/elements/Carousel/Carousel.types.d.ts +26 -7
- package/dist/elements/Carousel/Carousel.types.d.ts.map +1 -1
- package/dist/elements/Chart/Chart.d.ts +2 -40
- package/dist/elements/Chart/Chart.d.ts.map +1 -1
- package/dist/elements/Chart/Chart.types.d.ts +25 -10
- package/dist/elements/Chart/Chart.types.d.ts.map +1 -1
- package/dist/elements/Chart/index.js +1 -1
- package/dist/elements/Chart/index.js.map +1 -1
- package/dist/elements/Chart/index.mjs +1 -1
- package/dist/elements/Chart/index.mjs.map +1 -1
- package/dist/elements/Checkbox/Checkbox.d.ts +1 -31
- package/dist/elements/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/elements/Checkbox/Checkbox.types.d.ts +33 -1
- package/dist/elements/Checkbox/Checkbox.types.d.ts.map +1 -1
- package/dist/elements/Checkbox/index.js +1 -1
- package/dist/elements/Checkbox/index.js.map +1 -1
- package/dist/elements/Checkbox/index.mjs +1 -1
- package/dist/elements/Checkbox/index.mjs.map +1 -1
- package/dist/elements/CheckboxGroup/CheckboxGroup.d.ts +1 -26
- package/dist/elements/CheckboxGroup/CheckboxGroup.d.ts.map +1 -1
- package/dist/elements/CheckboxGroup/CheckboxGroup.types.d.ts +48 -4
- package/dist/elements/CheckboxGroup/CheckboxGroup.types.d.ts.map +1 -1
- package/dist/elements/CheckboxGroup/index.js +1 -1
- package/dist/elements/CheckboxGroup/index.js.map +1 -1
- package/dist/elements/CheckboxGroup/index.mjs +1 -1
- package/dist/elements/CheckboxGroup/index.mjs.map +1 -1
- package/dist/elements/Combobox/Combobox.d.ts +1 -30
- package/dist/elements/Combobox/Combobox.d.ts.map +1 -1
- package/dist/elements/Combobox/Combobox.types.d.ts +38 -4
- package/dist/elements/Combobox/Combobox.types.d.ts.map +1 -1
- package/dist/elements/Combobox/index.js +1 -1
- package/dist/elements/Combobox/index.js.map +1 -1
- package/dist/elements/Combobox/index.mjs +1 -1
- package/dist/elements/Combobox/index.mjs.map +1 -1
- package/dist/elements/Dropdown/Dropdown.d.ts +10 -124
- package/dist/elements/Dropdown/Dropdown.d.ts.map +1 -1
- package/dist/elements/Dropdown/Dropdown.types.d.ts +53 -8
- package/dist/elements/Dropdown/Dropdown.types.d.ts.map +1 -1
- package/dist/elements/FileField/FileField.d.ts +2 -39
- package/dist/elements/FileField/FileField.d.ts.map +1 -1
- package/dist/elements/FileField/FileField.types.d.ts +31 -13
- package/dist/elements/FileField/FileField.types.d.ts.map +1 -1
- package/dist/elements/FileField/index.js +1 -1
- package/dist/elements/FileField/index.js.map +1 -1
- package/dist/elements/FileField/index.mjs +1 -1
- package/dist/elements/FileField/index.mjs.map +1 -1
- package/dist/elements/FormLayout/FormLayout.d.ts +2 -37
- package/dist/elements/FormLayout/FormLayout.d.ts.map +1 -1
- package/dist/elements/FormLayout/FormLayout.types.d.ts +19 -3
- package/dist/elements/FormLayout/FormLayout.types.d.ts.map +1 -1
- package/dist/elements/MatrixGrid/MatrixGrid.d.ts +2 -29
- package/dist/elements/MatrixGrid/MatrixGrid.d.ts.map +1 -1
- package/dist/elements/MatrixGrid/MatrixGrid.types.d.ts +22 -1
- package/dist/elements/MatrixGrid/MatrixGrid.types.d.ts.map +1 -1
- package/dist/elements/MatrixGrid/index.js +1 -1
- package/dist/elements/MatrixGrid/index.js.map +1 -1
- package/dist/elements/MatrixGrid/index.mjs +1 -1
- package/dist/elements/MatrixGrid/index.mjs.map +1 -1
- package/dist/elements/Modal/Modal.types.d.ts +69 -9
- package/dist/elements/Modal/Modal.types.d.ts.map +1 -1
- package/dist/elements/Pagination/Pagination.d.ts +2 -28
- package/dist/elements/Pagination/Pagination.d.ts.map +1 -1
- package/dist/elements/Pagination/Pagination.types.d.ts +26 -1
- package/dist/elements/Pagination/Pagination.types.d.ts.map +1 -1
- package/dist/elements/Pagination/index.js +1 -1
- package/dist/elements/Pagination/index.js.map +1 -1
- package/dist/elements/Pagination/index.mjs +1 -1
- package/dist/elements/Pagination/index.mjs.map +1 -1
- package/dist/elements/Panel/Panel.types.d.ts +67 -9
- package/dist/elements/Panel/Panel.types.d.ts.map +1 -1
- package/dist/elements/PasswordField/PasswordField.d.ts +2 -26
- package/dist/elements/PasswordField/PasswordField.d.ts.map +1 -1
- package/dist/elements/PasswordField/PasswordField.types.d.ts +27 -2
- package/dist/elements/PasswordField/PasswordField.types.d.ts.map +1 -1
- package/dist/elements/PasswordField/index.js +1 -1
- package/dist/elements/PasswordField/index.js.map +1 -1
- package/dist/elements/PasswordField/index.mjs +1 -1
- package/dist/elements/PasswordField/index.mjs.map +1 -1
- package/dist/elements/RadioGroup/RadioGroup.d.ts +1 -26
- package/dist/elements/RadioGroup/RadioGroup.d.ts.map +1 -1
- package/dist/elements/RadioGroup/RadioGroup.types.d.ts +43 -4
- package/dist/elements/RadioGroup/RadioGroup.types.d.ts.map +1 -1
- package/dist/elements/RadioGroup/index.js +1 -1
- package/dist/elements/RadioGroup/index.js.map +1 -1
- package/dist/elements/RadioGroup/index.mjs +1 -1
- package/dist/elements/RadioGroup/index.mjs.map +1 -1
- package/dist/elements/RatingScale/RatingScale.d.ts +2 -30
- package/dist/elements/RatingScale/RatingScale.d.ts.map +1 -1
- package/dist/elements/RatingScale/RatingScale.types.d.ts +29 -1
- package/dist/elements/RatingScale/RatingScale.types.d.ts.map +1 -1
- package/dist/elements/RatingScale/index.js +1 -1
- package/dist/elements/RatingScale/index.js.map +1 -1
- package/dist/elements/RatingScale/index.mjs +1 -1
- package/dist/elements/RatingScale/index.mjs.map +1 -1
- package/dist/elements/SearchField/SearchField.d.ts +2 -26
- package/dist/elements/SearchField/SearchField.d.ts.map +1 -1
- package/dist/elements/SearchField/SearchField.types.d.ts +26 -2
- package/dist/elements/SearchField/SearchField.types.d.ts.map +1 -1
- package/dist/elements/SearchField/index.js +1 -1
- package/dist/elements/SearchField/index.js.map +1 -1
- package/dist/elements/SearchField/index.mjs +1 -1
- package/dist/elements/SearchField/index.mjs.map +1 -1
- package/dist/elements/Select/Select.d.ts +3 -61
- package/dist/elements/Select/Select.d.ts.map +1 -1
- package/dist/elements/Select/Select.types.d.ts +52 -4
- package/dist/elements/Select/Select.types.d.ts.map +1 -1
- package/dist/elements/Select/index.js +1 -1
- package/dist/elements/Select/index.js.map +1 -1
- package/dist/elements/Select/index.mjs +1 -1
- package/dist/elements/Select/index.mjs.map +1 -1
- package/dist/elements/SituationalJudgement/SituationalJudgement.d.ts +2 -28
- package/dist/elements/SituationalJudgement/SituationalJudgement.d.ts.map +1 -1
- package/dist/elements/SituationalJudgement/SituationalJudgement.types.d.ts +23 -1
- package/dist/elements/SituationalJudgement/SituationalJudgement.types.d.ts.map +1 -1
- package/dist/elements/SituationalJudgement/index.js +1 -1
- package/dist/elements/SituationalJudgement/index.js.map +1 -1
- package/dist/elements/SituationalJudgement/index.mjs +1 -1
- package/dist/elements/SituationalJudgement/index.mjs.map +1 -1
- package/dist/elements/Skeleton/Skeleton.types.d.ts +11 -6
- package/dist/elements/Skeleton/Skeleton.types.d.ts.map +1 -1
- package/dist/elements/Skeleton/index.js +1 -1
- package/dist/elements/Skeleton/index.js.map +1 -1
- package/dist/elements/Skeleton/index.mjs +1 -1
- package/dist/elements/Skeleton/index.mjs.map +1 -1
- package/dist/elements/Switch/Switch.types.d.ts +31 -7
- package/dist/elements/Switch/Switch.types.d.ts.map +1 -1
- package/dist/elements/Switch/index.js +1 -1
- package/dist/elements/Switch/index.js.map +1 -1
- package/dist/elements/Switch/index.mjs +1 -1
- package/dist/elements/Switch/index.mjs.map +1 -1
- package/dist/elements/Table/Table.d.ts +7 -79
- package/dist/elements/Table/Table.d.ts.map +1 -1
- package/dist/elements/Table/Table.types.d.ts +82 -17
- package/dist/elements/Table/Table.types.d.ts.map +1 -1
- package/dist/elements/Table/index.js +1 -1
- package/dist/elements/Table/index.js.map +1 -1
- package/dist/elements/Table/index.mjs +1 -1
- package/dist/elements/Table/index.mjs.map +1 -1
- package/dist/elements/Tabs/Tabs.types.d.ts +61 -5
- package/dist/elements/Tabs/Tabs.types.d.ts.map +1 -1
- package/dist/elements/Tabs/index.js +1 -1
- package/dist/elements/Tabs/index.js.map +1 -1
- package/dist/elements/Tabs/index.mjs +1 -1
- package/dist/elements/Tabs/index.mjs.map +1 -1
- package/dist/elements/TextField/TextField.d.ts +6 -71
- package/dist/elements/TextField/TextField.d.ts.map +1 -1
- package/dist/elements/TextField/TextField.types.d.ts +76 -12
- package/dist/elements/TextField/TextField.types.d.ts.map +1 -1
- package/dist/elements/TextField/index.js +1 -1
- package/dist/elements/TextField/index.js.map +1 -1
- package/dist/elements/TextField/index.mjs +1 -1
- package/dist/elements/TextField/index.mjs.map +1 -1
- package/dist/elements/Toast/Toast.types.d.ts +12 -20
- package/dist/elements/Toast/Toast.types.d.ts.map +1 -1
- package/dist/elements/Toast/Toaster.d.ts +2 -5
- package/dist/elements/Toast/Toaster.d.ts.map +1 -1
- package/dist/elements/Toast/index.js +1 -1
- package/dist/elements/Toast/index.js.map +1 -1
- package/dist/elements/Toast/index.mjs +1 -1
- package/dist/elements/Toast/index.mjs.map +1 -1
- package/dist/elements/Tooltip/Tooltip.types.d.ts +39 -4
- package/dist/elements/Tooltip/Tooltip.types.d.ts.map +1 -1
- package/dist/elements/Tooltip/index.js +1 -1
- package/dist/elements/Tooltip/index.js.map +1 -1
- package/dist/elements/Tooltip/index.mjs +1 -1
- package/dist/elements/Tooltip/index.mjs.map +1 -1
- package/dist/elements/index.js.map +1 -1
- package/dist/elements/index.mjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
'use strict';var react=require('react'),reactAria=require('react-aria'),reactStately=require('react-stately'),classVarianceAuthority=require('class-variance-authority'),lucideReact=require('lucide-react'),clsx=require('clsx'),tailwindMerge=require('tailwind-merge'),reactAriaComponents=require('react-aria-components'),jsxRuntime=require('react/jsx-runtime'),zod=require('zod');function i(...t){return tailwindMerge.twMerge(clsx.clsx(t))}var z=classVarianceAuthority.cva("inline-flex justify-center min-h-[44px] min-w-[44px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50",{variants:{fullWidth:{true:"w-full",false:""},inVerticalGroup:{true:"items-stretch",false:"items-center"}},defaultVariants:{fullWidth:false,inVerticalGroup:false}}),G=classVarianceAuthority.cva("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 relative cursor-pointer",{variants:{variant:{default:"bg-[var(--primary-action)] text-[var(--primary-action-foreground)] shadow-md hover:bg-[var(--primary-action-hover)] data-[pressed]:bg-[var(--primary-action)]/80",destructive:"bg-[var(--destructive-background)] text-[var(--destructive-foreground)] shadow-md hover:bg-[var(--destructive-background)]/90 data-[pressed]:bg-[var(--destructive-background)]/80",outline:"border border-[var(--input-border)] bg-[var(--page-background)] hover:bg-[var(--input-border)] data-[pressed]:bg-[var(--input-border)]",secondary:"bg-[var(--secondary)] text-[var(--secondary-foreground)] shadow-md hover:bg-[var(--secondary)]/80 data-[pressed]:bg-[var(--secondary)]/70",ghost:"hover:bg-[var(--accent)] hover:text-[var(--accent-foreground)] data-[pressed]:bg-[var(--accent)]",link:"text-[var(--text-link)] underline-offset-4 hover:underline data-[pressed]:text-[var(--text-link-hover)]"},fullWidth:{true:"w-full",false:""},visualSize:{default:"h-10 px-4 py-2",sm:"h-9 rounded-md px-3 text-xs",lg:"h-11 rounded-md px-8",icon:"h-10 w-10",dot:"h-5 w-5 rounded-full p-0 min-h-0 min-w-0"},paywall:{true:"!bg-[var(--paywall)] !text-[var(--paywall-foreground)] !shadow-md hover:!bg-[var(--paywall)]/90 !cursor-not-allowed !border-transparent",false:""}},defaultVariants:{variant:"default",visualSize:"default",paywall:false}});var H="data-[pressed]:scale-[0.97]";var L="data-[hovered]:shadow-md";var w="hc:data-[hovered]:outline hc:data-[hovered]:outline-2 hc:data-[hovered]:outline-foreground",I="hc:data-[pressed]:outline hc:data-[pressed]:outline-2 hc:data-[pressed]:outline-offset-1 hc:data-[pressed]:outline-foreground";var ot=react.createContext(null);ot.displayName="ButtonGroupContext";function at(){return react.useContext(ot)}var rt=react.createContext(null);rt.displayName="ButtonGroupItemContext";function nt(){return react.useContext(rt)}classVarianceAuthority.cva("inline-flex items-center gap-0",{variants:{orientation:{horizontal:"flex-row",vertical:"flex-col w-full"}},defaultVariants:{orientation:"horizontal"}});var st=classVarianceAuthority.cva("",{variants:{orientation:{horizontal:"min-w-[44px]",vertical:"flex min-h-[44px]"},position:{first:"",middle:"",last:"",only:""}},compoundVariants:[{orientation:"horizontal",position:"first",className:"rounded-r-none border-r-0"},{orientation:"horizontal",position:"middle",className:"rounded-none border-r-0"},{orientation:"horizontal",position:"last",className:"rounded-l-none"},{orientation:"vertical",position:"first",className:"rounded-b-none border-b-0"},{orientation:"vertical",position:"middle",className:"rounded-none border-b-0"},{orientation:"vertical",position:"last",className:"rounded-t-none"}],defaultVariants:{orientation:"horizontal",position:"only"}});classVarianceAuthority.cva("bg-[var(--border)]",{variants:{orientation:{horizontal:"w-px h-6 mx-1",vertical:"h-px w-full my-1"}},defaultVariants:{orientation:"horizontal"}});var S=react.memo(react.forwardRef(({className:t,buttonVisualClassName:e,variant:a,size:n,visualSize:u,fullWidth:c,loading:f=false,loadingText:d="Loading...",shortcut:v,children:o,isDisabled:h,paywall:s=false,paywallRedirect:x,paywallDescription:P,onPress:p,...B},m)=>{let N=react.useId(),T=at(),g=nt(),b=a??T?.variant??"default",k=n??T?.size,xt=h??T?.isDisabled??false,q=T?.orientation==="vertical",K=c||q,gt=g?st({orientation:T?.orientation??"horizontal",position:g.position}):"",_=u??k??"default";return process.env.NODE_ENV!=="production"&&(_==="dot"||_==="icon")&&!B["aria-label"]&&!o&&console.warn('[Button] visualSize="dot" or "icon" requires aria-label when no visible text is provided (WCAG 1.1.1)'),jsxRuntime.jsx(reactAriaComponents.Button,{ref:m,isDisabled:xt||f||void 0,"aria-disabled":s?true:void 0,"aria-describedby":s?N:void 0,onPress:V=>{if(s){x&&window.open(x,"_blank","noopener,noreferrer");return}p?.(V);},className:i(z({fullWidth:K,inVerticalGroup:q}),t),...B,children:V=>jsxRuntime.jsxs("span",{className:i(G({variant:b,visualSize:_,paywall:s,fullWidth:K}),gt,e,H,L,w,I),"data-pressed":V.isPressed||void 0,children:[f&&jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(lucideReact.Loader2,{className:"motion-safe:animate-spin","aria-hidden":"true"}),jsxRuntime.jsx("span",{className:"sr-only","aria-live":"polite",children:d})]}),!f&&o,s&&jsxRuntime.jsx(lucideReact.Zap,{"data-testid":"zap-icon","aria-hidden":"true",className:"ml-1"}),s&&jsxRuntime.jsxs("span",{id:N,className:"sr-only",children:["Premium feature: ",P||"Upgrade required to access this feature"]}),V.isFocusVisible&&v&&jsxRuntime.jsx("kbd",{className:"ml-auto hidden text-xs opacity-60 lg:inline",children:v}),V.isPressed&&jsxRuntime.jsx("span",{className:"absolute inset-0 rounded-[inherit] bg-current opacity-10 motion-safe:animate-in motion-safe:zoom-in-95","aria-hidden":"true"})]})})}));S.displayName="Button";var dt=classVarianceAuthority.cva(["pointer-events-auto relative flex w-[356px] items-center justify-between","space-x-4 overflow-hidden rounded-lg border p-4 pr-12 shadow-lg","transition-all duration-300","motion-reduce:transition-none motion-reduce:animate-none","data-[entering]:animate-in data-[entering]:slide-in-from-left","data-[exiting]:animate-out data-[exiting]:slide-out-to-right-full","data-[exiting]:fade-out-80","touch-pan-y"],{variants:{variant:{default:["bg-[var(--content-background)]","text-[var(--content-foreground)]","border-[var(--border)]"],primary:["bg-[var(--primary)]","text-[var(--primary-foreground)]","border-[var(--primary)]"],destructive:["bg-[var(--destructive-background)]","text-[var(--destructive-foreground)]","border-[var(--destructive-background)]"],accent:["bg-[var(--accent-background)]","text-[var(--accent-foreground)]","border-[var(--accent-background)]"]}},defaultVariants:{variant:"default"}}),W=classVarianceAuthority.cva(["fixed z-50 flex flex-col gap-2 p-4","pointer-events-none","max-h-screen overflow-hidden"],{variants:{position:{"top-left":"top-0 left-0 items-start","top-center":"top-0 left-1/2 -translate-x-1/2 items-center","top-right":"top-0 right-0 items-end","bottom-left":"bottom-0 left-0 items-start","bottom-center":"bottom-0 left-1/2 -translate-x-1/2 items-center","bottom-right":"bottom-0 right-0 items-end"}},defaultVariants:{position:"bottom-right"}}),pt=react.memo(react.forwardRef(({variant:t="default",className:e,onPress:a},n)=>jsxRuntime.jsx(S,{ref:n,variant:"ghost",visualSize:"icon",onPress:a,className:i("absolute right-2 top-2",t==="default"?"hover:bg-[var(--accent-background)]/20":"hover:bg-[var(--page-background)]/20",e),"aria-label":"Dismiss notification",children:jsxRuntime.jsx(lucideReact.X,{className:"h-4 w-4","aria-hidden":"true"})})));pt.displayName="CloseButton";var l=new reactStately.ToastQueue({maxVisibleToasts:3}),R=0,D=new Set;function Ot(){R++,D.forEach(t=>t());}function _t(){R=Math.max(0,R-1),D.forEach(t=>t());}function Y(){return R}function mt(t){return D.add(t),()=>D.delete(t)}var ft={add:(t,e)=>l.add(t,e),close:t=>{l.close(t);},closeAll:()=>{l.visibleToasts.forEach(t=>{l.close(t.key);});},pauseAll:()=>{l.pauseAll();},resumeAll:()=>{l.resumeAll();},get visibleToasts(){return l.visibleToasts},subscribe:t=>l.subscribe(t),get totalCount(){return R}};function O(){return l}function zt(t){return t===0?0:Math.max(t,5e3)}function Gt(t={}){return {variant:t.variant??"default",timeout:t.timeout??5e3,onClose:t.onClose}}function Ht(t,e){let a=Gt(e),n=zt(a.timeout),u="timeout";Ot();let c=ft.add({key:"",content:t,variant:a.variant,timeout:n,onClose:a.onClose},{timeout:n,onClose:()=>{_t(),a.onClose&&a.onClose(c,u);}});return c}var X=react.memo(react.forwardRef(({toast:t,state:e},a)=>{let n=react.useRef(null),u=a||n,{toastProps:c,contentProps:f}=reactAria.useToast({toast:t},e,u),[d,v]=react.useState(null),[o,h]=react.useState(0),s=50,x=react.useCallback(g=>{let b=g.touches[0];b&&v(b.clientX);},[]),P=react.useCallback(g=>{if(d===null)return;let b=g.touches[0];if(!b)return;let k=b.clientX-d;k>0&&h(k);},[d]),p=react.useCallback(()=>{o>s&&e.close(t.key),h(0),v(null);},[o,e,t.key]),B=react.useCallback(()=>{e.close(t.key);},[e,t.key]),{content:m}=t,N=m.variant??"default",T=typeof m.content=="string"?m.content:"Notification";return jsxRuntime.jsxs("div",{...c,ref:u,className:i(dt({variant:N})),"aria-label":T,style:{transform:o>0?`translateX(${o}px)`:void 0,opacity:o>0?1-o/200:void 0},onTouchStart:x,onTouchMove:P,onTouchEnd:p,children:[jsxRuntime.jsx("div",{...f,className:"flex-1 min-w-0",children:typeof m.content=="string"?jsxRuntime.jsx("p",{className:"text-sm font-medium",children:m.content}):m.content}),jsxRuntime.jsx(pt,{variant:N,onPress:B})]})}));X.displayName="ToastItem";var Qt=zod.z.enum(["timeout","user","programmatic"]),vt=zod.z.enum(["default","primary","destructive","accent"]),Tt=zod.z.enum(["top-left","top-center","top-right","bottom-left","bottom-center","bottom-right"]),Mt=zod.z.object({variant:vt.default("default"),timeout:zod.z.number().min(0).default(5e3),onClose:zod.z.function().optional()}),F=zod.z.object({maxVisibleToasts:zod.z.number().min(1).default(3)}),U=zod.z.object({position:Tt.default("bottom-right"),"aria-label":zod.z.string().default("Notifications"),className:zod.z.string().optional()});var Yt=react.createContext(null);function bt({children:t,maxVisibleToasts:e=3}){F.parse({maxVisibleToasts:e});let a=react.useMemo(()=>O(),[]);return jsxRuntime.jsx(Yt.Provider,{value:a,children:t})}bt.displayName="ToastProvider";function te({queuedCount:t}){if(t<=0)return null;let e=Math.min(t,2);return jsxRuntime.jsxs("div",{className:"relative w-[356px] pointer-events-none","aria-hidden":"true",children:[jsxRuntime.jsx("div",{className:i("absolute left-1/2 -translate-x-1/2 w-[340px] h-4 rounded-lg shadow-lg","bg-[var(--content-background)] border border-[var(--border)]","opacity-60","transition-all duration-200"),style:{top:0}}),e>=2&&jsxRuntime.jsx("div",{className:i("absolute left-1/2 -translate-x-1/2 w-[324px] h-2 rounded-b-lg shadow-lg","bg-[var(--content-background)] border border-t-0 border-[var(--border)]","opacity-40","transition-all duration-200"),style:{top:14}}),jsxRuntime.jsx("div",{className:"h-4"}),jsxRuntime.jsxs("div",{className:"text-sm text-[var(--content-foreground)] opacity-70 text-center py-2 pointer-events-auto","aria-live":"polite",children:["+",t," more notification",t>1?"s":""]})]})}var ht=react.memo(react.forwardRef((t,e)=>{let a=U.parse(t),{position:n="bottom-right","aria-label":u="Notifications",className:c}=a,f=react.useRef(null),d=e||f,v=O(),o=reactStately.useToastQueue(v),h=react.useSyncExternalStore(mt,Y,Y),{regionProps:s}=reactAria.useToastRegion({"aria-label":u},o,d),x=Math.max(0,h-o.visibleToasts.length),P=react.useCallback(()=>{o.visibleToasts.forEach(p=>{o.close(p.key);});},[o]);return o.visibleToasts.length===0?null:jsxRuntime.jsxs("div",{...s,ref:d,className:i(W({position:n}),c),children:[o.visibleToasts.map(p=>jsxRuntime.jsx(X,{toast:p,state:o},p.key)),jsxRuntime.jsx(te,{queuedCount:x}),o.visibleToasts.length>=2&&jsxRuntime.jsx(S,{variant:"secondary",visualSize:"sm",onPress:P,"aria-label":"Clear all notifications",className:"mt-2 pointer-events-auto",children:"Clear All"})]})}));ht.displayName="Toaster";exports.DismissReasonSchema=Qt;exports.ToastOptionsSchema=Mt;exports.ToastProvider=bt;exports.ToastProviderPropsSchema=F;exports.ToastVariantSchema=vt;exports.Toaster=ht;exports.ToasterPositionSchema=Tt;exports.ToasterPropsSchema=U;exports.toast=Ht;exports.toastQueue=ft;exports.toastVariants=dt;exports.toasterVariants=W;//# sourceMappingURL=index.js.map
|
|
2
|
+
'use strict';var react=require('react'),reactAria=require('react-aria'),reactStately=require('react-stately'),classVarianceAuthority=require('class-variance-authority'),lucideReact=require('lucide-react'),clsx=require('clsx'),tailwindMerge=require('tailwind-merge'),reactAriaComponents=require('react-aria-components'),jsxRuntime=require('react/jsx-runtime'),zod=require('zod');function i(...t){return tailwindMerge.twMerge(clsx.clsx(t))}var O=classVarianceAuthority.cva("inline-flex justify-center min-h-[44px] min-w-[44px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50",{variants:{fullWidth:{true:"w-full",false:""},inVerticalGroup:{true:"items-stretch",false:"items-center"}},defaultVariants:{fullWidth:false,inVerticalGroup:false}}),z=classVarianceAuthority.cva("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 relative cursor-pointer",{variants:{variant:{default:"bg-[var(--primary-action)] text-[var(--primary-action-foreground)] shadow-md hover:bg-[var(--primary-action-hover)] data-[pressed]:bg-[var(--primary-action)]/80",destructive:"bg-[var(--destructive-background)] text-[var(--destructive-foreground)] shadow-md hover:bg-[var(--destructive-background)]/90 data-[pressed]:bg-[var(--destructive-background)]/80",outline:"border border-[var(--input-border)] bg-[var(--page-background)] hover:bg-[var(--input-border)] data-[pressed]:bg-[var(--input-border)]",secondary:"bg-[var(--secondary)] text-[var(--secondary-foreground)] shadow-md hover:bg-[var(--secondary)]/80 data-[pressed]:bg-[var(--secondary)]/70",ghost:"hover:bg-[var(--accent)] hover:text-[var(--accent-foreground)] data-[pressed]:bg-[var(--accent)]",link:"text-[var(--text-link)] underline-offset-4 hover:underline data-[pressed]:text-[var(--text-link-hover)]"},fullWidth:{true:"w-full",false:""},visualSize:{default:"h-10 px-4 py-2",sm:"h-9 rounded-md px-3 text-xs",lg:"h-11 rounded-md px-8",icon:"h-10 w-10",dot:"h-5 w-5 rounded-full p-0 min-h-0 min-w-0"},paywall:{true:"!bg-[var(--paywall)] !text-[var(--paywall-foreground)] !shadow-md hover:!bg-[var(--paywall)]/90 !cursor-not-allowed !border-transparent",false:""}},defaultVariants:{variant:"default",visualSize:"default",paywall:false}});var H="data-[pressed]:scale-[0.97]";var L="data-[hovered]:shadow-md";var w="hc:data-[hovered]:outline hc:data-[hovered]:outline-2 hc:data-[hovered]:outline-foreground",I="hc:data-[pressed]:outline hc:data-[pressed]:outline-2 hc:data-[pressed]:outline-offset-1 hc:data-[pressed]:outline-foreground";var ot=react.createContext(null);ot.displayName="ButtonGroupContext";function at(){return react.useContext(ot)}var rt=react.createContext(null);rt.displayName="ButtonGroupItemContext";function nt(){return react.useContext(rt)}classVarianceAuthority.cva("inline-flex items-center gap-0",{variants:{orientation:{horizontal:"flex-row",vertical:"flex-col w-full"}},defaultVariants:{orientation:"horizontal"}});var st=classVarianceAuthority.cva("",{variants:{orientation:{horizontal:"min-w-[44px]",vertical:"flex min-h-[44px]"},position:{first:"",middle:"",last:"",only:""}},compoundVariants:[{orientation:"horizontal",position:"first",className:"rounded-r-none border-r-0"},{orientation:"horizontal",position:"middle",className:"rounded-none border-r-0"},{orientation:"horizontal",position:"last",className:"rounded-l-none"},{orientation:"vertical",position:"first",className:"rounded-b-none border-b-0"},{orientation:"vertical",position:"middle",className:"rounded-none border-b-0"},{orientation:"vertical",position:"last",className:"rounded-t-none"}],defaultVariants:{orientation:"horizontal",position:"only"}});classVarianceAuthority.cva("bg-[var(--border)]",{variants:{orientation:{horizontal:"w-px h-6 mx-1",vertical:"h-px w-full my-1"}},defaultVariants:{orientation:"horizontal"}});var S=react.memo(react.forwardRef(({className:t,buttonVisualClassName:e,variant:a,size:n,visualSize:u,fullWidth:c,loading:f=false,loadingText:d="Loading...",shortcut:v,children:o,isDisabled:h,paywall:s=false,paywallRedirect:x,paywallDescription:P,onPress:p,...k},m)=>{let N=react.useId(),b=at(),g=nt(),T=a??b?.variant??"default",B=n??b?.size,xt=h??b?.isDisabled??false,q=b?.orientation==="vertical",K=c||q,gt=g?st({orientation:b?.orientation??"horizontal",position:g.position}):"",G=u??B??"default";return process.env.NODE_ENV!=="production"&&(G==="dot"||G==="icon")&&!k["aria-label"]&&!o&&console.warn('[Button] visualSize="dot" or "icon" requires aria-label when no visible text is provided (WCAG 1.1.1)'),jsxRuntime.jsx(reactAriaComponents.Button,{ref:m,isDisabled:xt||f||void 0,"aria-disabled":s?true:void 0,"aria-describedby":s?N:void 0,onPress:V=>{if(s){x&&window.open(x,"_blank","noopener,noreferrer");return}p?.(V);},className:i(O({fullWidth:K,inVerticalGroup:q}),t),...k,children:V=>jsxRuntime.jsxs("span",{className:i(z({variant:T,visualSize:G,paywall:s,fullWidth:K}),gt,e,H,L,w,I),"data-pressed":V.isPressed||void 0,children:[f&&jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(lucideReact.Loader2,{className:"motion-safe:animate-spin","aria-hidden":"true"}),jsxRuntime.jsx("span",{className:"sr-only","aria-live":"polite",children:d})]}),!f&&o,s&&jsxRuntime.jsx(lucideReact.Zap,{"data-testid":"zap-icon","aria-hidden":"true",className:"ml-1"}),s&&jsxRuntime.jsxs("span",{id:N,className:"sr-only",children:["Premium feature: ",P||"Upgrade required to access this feature"]}),V.isFocusVisible&&v&&jsxRuntime.jsx("kbd",{className:"ml-auto hidden text-xs opacity-60 lg:inline",children:v}),V.isPressed&&jsxRuntime.jsx("span",{className:"absolute inset-0 rounded-[inherit] bg-current opacity-10 motion-safe:animate-in motion-safe:zoom-in-95","aria-hidden":"true"})]})})}));S.displayName="Button";var dt=classVarianceAuthority.cva(["pointer-events-auto relative flex w-[356px] items-center justify-between","space-x-4 overflow-hidden rounded-lg border p-4 pr-12 shadow-lg","transition-all duration-300","motion-reduce:transition-none motion-reduce:animate-none","data-[entering]:animate-in data-[entering]:slide-in-from-left","data-[exiting]:animate-out data-[exiting]:slide-out-to-right-full","data-[exiting]:fade-out-80","touch-pan-y"],{variants:{variant:{default:["bg-[var(--content-background)]","text-[var(--content-foreground)]","border-[var(--border)]"],primary:["bg-[var(--primary)]","text-[var(--primary-foreground)]","border-[var(--primary)]"],destructive:["bg-[var(--destructive-background)]","text-[var(--destructive-foreground)]","border-[var(--destructive-background)]"],accent:["bg-[var(--accent-background)]","text-[var(--accent-foreground)]","border-[var(--accent-background)]"]}},defaultVariants:{variant:"default"}}),W=classVarianceAuthority.cva(["fixed z-50 flex flex-col gap-2 p-4","pointer-events-none","max-h-screen overflow-hidden"],{variants:{position:{"top-left":"top-0 left-0 items-start","top-center":"top-0 left-1/2 -translate-x-1/2 items-center","top-right":"top-0 right-0 items-end","bottom-left":"bottom-0 left-0 items-start","bottom-center":"bottom-0 left-1/2 -translate-x-1/2 items-center","bottom-right":"bottom-0 right-0 items-end"}},defaultVariants:{position:"bottom-right"}}),pt=react.memo(react.forwardRef(({variant:t="default",className:e,onPress:a},n)=>jsxRuntime.jsx(S,{ref:n,variant:"ghost",visualSize:"icon",onPress:a,className:i("absolute right-2 top-2",t==="default"?"hover:bg-[var(--accent-background)]/20":"hover:bg-[var(--page-background)]/20",e),"aria-label":"Dismiss notification",children:jsxRuntime.jsx(lucideReact.X,{className:"h-4 w-4","aria-hidden":"true"})})));pt.displayName="CloseButton";var l=new reactStately.ToastQueue({maxVisibleToasts:3}),R=0,D=new Set;function _t(){R++,D.forEach(t=>t());}function Gt(){R=Math.max(0,R-1),D.forEach(t=>t());}function Y(){return R}function mt(t){return D.add(t),()=>D.delete(t)}var ft={add:(t,e)=>l.add(t,e),close:t=>{l.close(t);},closeAll:()=>{l.visibleToasts.forEach(t=>{l.close(t.key);});},pauseAll:()=>{l.pauseAll();},resumeAll:()=>{l.resumeAll();},get visibleToasts(){return l.visibleToasts},subscribe:t=>l.subscribe(t),get totalCount(){return R}};function _(){return l}function Ot(t){return t===0?0:Math.max(t,5e3)}function zt(t={}){return {variant:t.variant??"default",timeout:t.timeout??5e3,onClose:t.onClose}}function Ht(t,e){let a=zt(e),n=Ot(a.timeout),u="timeout";_t();let c=ft.add({key:"",content:t,variant:a.variant,timeout:n,onClose:a.onClose},{timeout:n,onClose:()=>{Gt(),a.onClose&&a.onClose(c,u);}});return c}var X=react.memo(react.forwardRef(({toast:t,state:e},a)=>{let n=react.useRef(null),u=a||n,{toastProps:c,contentProps:f}=reactAria.useToast({toast:t},e,u),[d,v]=react.useState(null),[o,h]=react.useState(0),s=50,x=react.useCallback(g=>{let T=g.touches[0];T&&v(T.clientX);},[]),P=react.useCallback(g=>{if(d===null)return;let T=g.touches[0];if(!T)return;let B=T.clientX-d;B>0&&h(B);},[d]),p=react.useCallback(()=>{o>s&&e.close(t.key),h(0),v(null);},[o,e,t.key]),k=react.useCallback(()=>{e.close(t.key);},[e,t.key]),{content:m}=t,N=m.variant??"default",b=typeof m.content=="string"?m.content:"Notification";return jsxRuntime.jsxs("div",{...c,ref:u,className:i(dt({variant:N})),"aria-label":b,style:{transform:o>0?`translateX(${o}px)`:void 0,opacity:o>0?1-o/200:void 0},onTouchStart:x,onTouchMove:P,onTouchEnd:p,children:[jsxRuntime.jsx("div",{...f,className:"flex-1 min-w-0",children:typeof m.content=="string"?jsxRuntime.jsx("p",{className:"text-sm font-medium",children:m.content}):m.content}),jsxRuntime.jsx(pt,{variant:N,onPress:k})]})}));X.displayName="ToastItem";var Qt=zod.z.enum(["timeout","user","programmatic"]),vt=zod.z.enum(["default","primary","destructive","accent"]),bt=zod.z.enum(["top-left","top-center","top-right","bottom-left","bottom-center","bottom-right"]),Mt=zod.z.object({variant:vt.default("default"),timeout:zod.z.number().min(0).default(5e3),onClose:zod.z.function().optional()}),F=zod.z.object({maxVisibleToasts:zod.z.number().min(1).default(3)}),U=zod.z.object({position:bt.default("bottom-right"),"aria-label":zod.z.string().default("Notifications"),className:zod.z.string().optional()});var Yt=react.createContext(null);function Tt({children:t,maxVisibleToasts:e=3}){F.parse({maxVisibleToasts:e});let a=react.useMemo(()=>_(),[]);return jsxRuntime.jsx(Yt.Provider,{value:a,children:t})}Tt.displayName="ToastProvider";function te({queuedCount:t}){if(t<=0)return null;let e=Math.min(t,2);return jsxRuntime.jsxs("div",{className:"relative w-[356px] pointer-events-none","aria-hidden":"true",children:[jsxRuntime.jsx("div",{className:i("absolute left-1/2 -translate-x-1/2 w-[340px] h-4 rounded-lg shadow-lg","bg-[var(--content-background)] border border-[var(--border)]","opacity-60","transition-all duration-200"),style:{top:0}}),e>=2&&jsxRuntime.jsx("div",{className:i("absolute left-1/2 -translate-x-1/2 w-[324px] h-2 rounded-b-lg shadow-lg","bg-[var(--content-background)] border border-t-0 border-[var(--border)]","opacity-40","transition-all duration-200"),style:{top:14}}),jsxRuntime.jsx("div",{className:"h-4"}),jsxRuntime.jsxs("div",{className:"text-sm text-[var(--content-foreground)] opacity-70 text-center py-2 pointer-events-auto","aria-live":"polite",children:["+",t," more notification",t>1?"s":""]})]})}var ht=react.memo(react.forwardRef((t,e)=>{let a=U.parse(t),{position:n="bottom-right","aria-label":u="Notifications",className:c}=a,f=react.useRef(null),d=e||f,v=_(),o=reactStately.useToastQueue(v),h=react.useSyncExternalStore(mt,Y,Y),{regionProps:s}=reactAria.useToastRegion({"aria-label":u},o,d),x=Math.max(0,h-o.visibleToasts.length),P=react.useCallback(()=>{o.visibleToasts.forEach(p=>{o.close(p.key);});},[o]);return o.visibleToasts.length===0?null:jsxRuntime.jsxs("div",{...s,ref:d,className:i(W({position:n}),c),children:[o.visibleToasts.map(p=>jsxRuntime.jsx(X,{toast:p,state:o},p.key)),jsxRuntime.jsx(te,{queuedCount:x}),o.visibleToasts.length>=2&&jsxRuntime.jsx(S,{variant:"secondary",visualSize:"sm",onPress:P,"aria-label":"Clear all notifications",className:"mt-2 pointer-events-auto",children:"Clear All"})]})}));ht.displayName="Toaster";exports.DismissReasonSchema=Qt;exports.ToastOptionsSchema=Mt;exports.ToastProvider=Tt;exports.ToastProviderPropsSchema=F;exports.ToastVariantSchema=vt;exports.Toaster=ht;exports.ToasterPositionSchema=bt;exports.ToasterPropsSchema=U;exports.toast=Ht;exports.toastQueue=ft;exports.toastVariants=dt;exports.toasterVariants=W;//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/cn.ts","../../../src/elements/Button/Button.styles.ts","../../../src/styles/interaction-states.ts","../../../src/elements/ButtonGroup/ButtonGroupContext.tsx","../../../src/elements/ButtonGroup/ButtonGroup.variants.ts","../../../src/elements/Button/Button.tsx","../../../src/elements/Toast/Toast.tsx","../../../src/elements/Toast/Toast.types.ts","../../../src/elements/Toast/ToastProvider.tsx","../../../src/elements/Toast/Toaster.tsx"],"names":["cn","inputs","twMerge","clsx","buttonOuterVariants","cva","buttonVisualVariants","PRESSED_STYLES","HOVER_STYLES","HIGH_CONTRAST_HOVER","HIGH_CONTRAST_PRESSED","ButtonGroupContext","createContext","useButtonGroupContext","useContext","ButtonGroupItemContext","useButtonGroupItemContext","buttonGroupItemVariants","Button","memo","forwardRef","className","buttonVisualClassName","variant","size","visualSize","fullWidth","loading","loadingText","shortcut","children","isDisabled","paywall","paywallRedirect","paywallDescription","onPress","props","ref","paywallDescriptionId","useId","groupContext","itemContext","effectiveVariant","effectiveSize","effectiveIsDisabled","isInVerticalGroup","effectiveFullWidth","positionClassName","effectiveVisualSize","jsx","AriaButton","e","renderProps","jsxs","Fragment","Loader2","Zap","toastVariants","toasterVariants","CloseButton","X","internalQueue","ToastQueue","totalActiveToasts","toastCountListeners","incrementTotalToasts","fn","decrementTotalToasts","getTotalActiveToasts","subscribeToTotalCount","toastQueue","content","options","key","toast","getInternalQueue","enforceMinTimeout","timeout","applyToastDefaults","validated","dismissReason","ToastItem","state","forwardedRef","internalRef","useRef","toastProps","contentProps","useToast","touchStart","setTouchStart","useState","translateX","setTranslateX","SWIPE_THRESHOLD","handleTouchStart","useCallback","touch","handleTouchMove","diff","handleTouchEnd","handleCloseClick","accessibleName","DismissReasonSchema","z","ToastVariantSchema","ToasterPositionSchema","ToastOptionsSchema","ToastProviderPropsSchema","ToasterPropsSchema","ToastContext","ToastProvider","maxVisibleToasts","queue","useMemo","StackedToastIndicator","queuedCount","stackLayers","Toaster","position","ariaLabel","internalQueueRef","useAriaToastQueue","totalCount","useSyncExternalStore","regionProps","useToastRegion","handleClearAll"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","import { cva } from 'class-variance-authority';\n\n/**\n * Layer 1: Transparent outer touch target (44x44px minimum)\n * Handles WCAG 2.2 AAA touch target requirement\n * Always transparent, centers the visual button inside\n * IMPORTANT: Focus ring stays on Layer 1 for AAA compliance (2.4.13)\n *\n * In vertical ButtonGroups, uses items-stretch so the visual layer (Layer 2)\n * can fill the full touch target height, eliminating gaps between buttons.\n */\nexport const buttonOuterVariants = cva(\n \"inline-flex justify-center min-h-[44px] min-w-[44px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n fullWidth: {\n true: \"w-full\",\n false: \"\",\n },\n inVerticalGroup: {\n true: \"items-stretch\",\n false: \"items-center\",\n },\n },\n defaultVariants: {\n fullWidth: false,\n inVerticalGroup: false,\n },\n }\n);\n\n/**\n * Layer 2: Visual button appearance (adjustable size)\n * Provides the visual appearance with configurable size\n * Can be smaller than touch target for use cases like carousel dots\n * NOTE: NO focus-visible styles here - focus ring is on Layer 1\n */\nexport const buttonVisualVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 relative cursor-pointer\",\n {\n variants: {\n variant: {\n default:\n \"bg-[var(--primary-action)] text-[var(--primary-action-foreground)] shadow-md hover:bg-[var(--primary-action-hover)] data-[pressed]:bg-[var(--primary-action)]/80\",\n destructive:\n \"bg-[var(--destructive-background)] text-[var(--destructive-foreground)] shadow-md hover:bg-[var(--destructive-background)]/90 data-[pressed]:bg-[var(--destructive-background)]/80\",\n outline:\n \"border border-[var(--input-border)] bg-[var(--page-background)] hover:bg-[var(--input-border)] data-[pressed]:bg-[var(--input-border)]\",\n secondary:\n \"bg-[var(--secondary)] text-[var(--secondary-foreground)] shadow-md hover:bg-[var(--secondary)]/80 data-[pressed]:bg-[var(--secondary)]/70\",\n ghost:\n \"hover:bg-[var(--accent)] hover:text-[var(--accent-foreground)] data-[pressed]:bg-[var(--accent)]\",\n link: \"text-[var(--text-link)] underline-offset-4 hover:underline data-[pressed]:text-[var(--text-link-hover)]\",\n },\n fullWidth: {\n true: \"w-full\",\n false: \"\",\n },\n visualSize: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3 text-xs\",\n lg: \"h-11 rounded-md px-8\",\n icon: \"h-10 w-10\",\n dot: \"h-5 w-5 rounded-full p-0 min-h-0 min-w-0\",\n },\n paywall: {\n true: \"!bg-[var(--paywall)] !text-[var(--paywall-foreground)] !shadow-md hover:!bg-[var(--paywall)]/90 !cursor-not-allowed !border-transparent\",\n false: \"\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n visualSize: \"default\",\n paywall: false,\n },\n }\n);\n\n/**\n * @deprecated Use buttonVisualVariants instead. This alias is kept for backward compatibility.\n */\nexport const buttonVariants = buttonVisualVariants;\n","/**\n * Global Interaction State Styles\n *\n * Consistent interaction patterns across all Themis components.\n * These styles ensure WCAG 2.2 AAA compliance and predictable UX.\n *\n * @see spec.md FR-010 (Visible focus ring for keyboard navigation)\n * @see spec.md FR-031 (Pressed state feedback)\n * @see spec.md FR-012 (High contrast mode support)\n * @see constitution.md Principle IV (Accessibility First)\n */\n\n/**\n * Focus state styles (FR-010)\n * Visible focus ring for keyboard navigation - WCAG 2.2 Level AAA\n *\n * Components can override by extending these styles:\n * @example\n * cn(FOCUS_STYLES, \"ring-4\") // Increases ring width to 4px\n */\nexport const FOCUS_STYLES = \"data-[focus-visible]:ring-2 data-[focus-visible]:ring-[var(--themis-ring)] data-[focus-visible]:ring-offset-2\";\n\n/**\n * Pressed/Active state styles (FR-031)\n * Visual feedback for press interactions\n *\n * Components can override the scale amount:\n * @example\n * cn(PRESSED_STYLES_BASE, \"data-[pressed]:scale-[0.95]\") // More pronounced scale\n */\nexport const PRESSED_STYLES = \"data-[pressed]:scale-[0.97]\";\n\n/**\n * Base pressed styles without scale (for components that need different feedback)\n */\nexport const PRESSED_STYLES_BASE = \"data-[pressed]:transition-transform data-[pressed]:duration-100\";\n\n/**\n * Hover state styles\n * Elevation change on hover for better affordance\n *\n * Components can override shadow depth:\n * @example\n * cn(HOVER_STYLES_BASE, \"data-[hovered]:shadow-lg\") // Larger shadow\n */\nexport const HOVER_STYLES = \"data-[hovered]:shadow-md\";\n\n/**\n * Base hover styles without shadow (for components that use different hover effects)\n */\nexport const HOVER_STYLES_BASE = \"data-[hovered]:transition-shadow data-[hovered]:duration-200\";\n\n/**\n * High contrast mode focus (FR-012)\n * Enhanced outlines for users requiring high contrast\n *\n * Uses 'hc:' prefix for prefers-contrast: more media query\n */\nexport const HIGH_CONTRAST_FOCUS = \"hc:data-[focus-visible]:outline hc:data-[focus-visible]:outline-4 hc:data-[focus-visible]:outline-offset-2 hc:data-[focus-visible]:outline-foreground\";\n\n/**\n * High contrast mode hover (FR-012)\n * Enhanced outlines for hover in high contrast mode\n */\nexport const HIGH_CONTRAST_HOVER = \"hc:data-[hovered]:outline hc:data-[hovered]:outline-2 hc:data-[hovered]:outline-foreground\";\n\n/**\n * High contrast mode pressed state\n * Enhanced outlines for pressed state in high contrast mode\n */\nexport const HIGH_CONTRAST_PRESSED = \"hc:data-[pressed]:outline hc:data-[pressed]:outline-2 hc:data-[pressed]:outline-offset-1 hc:data-[pressed]:outline-foreground\";\n\n/**\n * Combined high contrast styles\n * Use this for components that need all high contrast interaction states\n */\nexport const HIGH_CONTRAST_INTERACTIONS = `${HIGH_CONTRAST_FOCUS} ${HIGH_CONTRAST_HOVER} ${HIGH_CONTRAST_PRESSED}`;\n\n/**\n * Disabled state styles\n * Consistent disabled appearance across all components\n */\nexport const DISABLED_STYLES = \"disabled:pointer-events-none disabled:opacity-50\";\n\n/**\n * Default interaction bundle\n * Most common combination for interactive components\n *\n * Includes: focus ring, pressed scale, hover shadow, high contrast enhancements\n *\n * @example\n * <button className={cn(DEFAULT_INTERACTIONS, \"bg-primary\")}>Click</button>\n */\nexport const DEFAULT_INTERACTIONS = `${FOCUS_STYLES} ${PRESSED_STYLES} ${HOVER_STYLES} ${HIGH_CONTRAST_FOCUS} ${HIGH_CONTRAST_HOVER} ${HIGH_CONTRAST_PRESSED}`;\n\n/**\n * Subtle interaction bundle\n * For components that need less pronounced feedback\n *\n * Includes: focus ring and high contrast, but no hover shadow or pressed scale\n */\nexport const SUBTLE_INTERACTIONS = `${FOCUS_STYLES} ${HIGH_CONTRAST_FOCUS}`;\n\n/**\n * Non-interactive element styles\n * For elements that should indicate they are not interactive\n */\nexport const NON_INTERACTIVE = \"cursor-default select-none\";\n","\"use client\";\n\nimport { createContext, useContext } from 'react';\nimport type {\n ButtonGroupContextValue,\n ButtonGroupItemContextValue,\n} from './ButtonGroup.types';\n\n/**\n * ButtonGroup Context System (Two-Level)\n *\n * Provides a two-level context pattern for ButtonGroup:\n *\n * 1. ButtonGroupContext (group-level):\n * - Provides: orientation, variant, size, isDisabled\n * - Consumed by: Button (for prop inheritance), Separator (for orientation)\n *\n * 2. ButtonGroupItemContext (item-level):\n * - Provides: position ('first' | 'middle' | 'last' | 'only')\n * - Consumed by: Button (for border-radius styling)\n *\n * Both contexts return null when not in a provider, allowing Button\n * to work standalone without any group context.\n *\n * @see plan.md for architecture details\n * @see ButtonGroup.tsx for Provider implementation\n */\n\n// =============================================================================\n// Group-Level Context\n// =============================================================================\n\n/**\n * Context for group-level props (orientation, variant, size, isDisabled)\n * Default value is null to indicate \"not in a group\"\n */\nconst ButtonGroupContext = createContext<ButtonGroupContextValue | null>(null);\n\nButtonGroupContext.displayName = 'ButtonGroupContext';\n\n/**\n * Hook to access group-level context\n * @returns ButtonGroupContextValue if inside a ButtonGroup, null otherwise\n */\nexport function useButtonGroupContext(): ButtonGroupContextValue | null {\n return useContext(ButtonGroupContext);\n}\n\n// =============================================================================\n// Item-Level Context\n// =============================================================================\n\n/**\n * Context for per-button position information\n * Default value is null to indicate \"not wrapped with position context\"\n */\nconst ButtonGroupItemContext =\n createContext<ButtonGroupItemContextValue | null>(null);\n\nButtonGroupItemContext.displayName = 'ButtonGroupItemContext';\n\n/**\n * Hook to access item-level context (position)\n * @returns ButtonGroupItemContextValue if wrapped with position context, null otherwise\n */\nexport function useButtonGroupItemContext(): ButtonGroupItemContextValue | null {\n return useContext(ButtonGroupItemContext);\n}\n\n// =============================================================================\n// Exports\n// =============================================================================\n\nexport { ButtonGroupContext, ButtonGroupItemContext };\n","import { cva } from 'class-variance-authority';\n\n/**\n * ButtonGroup CVA Variants\n *\n * Defines Class Variance Authority (CVA) variants for:\n * - ButtonGroup container (orientation-based layout)\n * - ButtonGroupItem (position-based border-radius)\n * - ButtonGroupSeparator (orientation-based styling)\n *\n * @see plan.md Phase 1: Design & Contracts - CVA Variants\n * @see constitution.md Principle V (Component Quality Standards)\n */\n\n// =============================================================================\n// Container Variants\n// =============================================================================\n\n/**\n * ButtonGroup container variants\n * Controls the layout direction based on orientation\n * Uses gap-0 to ensure buttons are connected (share borders)\n */\nexport const buttonGroupVariants = cva('inline-flex items-center gap-0', {\n variants: {\n orientation: {\n horizontal: 'flex-row',\n vertical: 'flex-col w-full',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n },\n});\n\n// =============================================================================\n// Item Position Variants\n// =============================================================================\n\n/**\n * ButtonGroupItem position variants\n * Applied to Button's visual layer (Layer 2) for position-aware border-radius\n *\n * Compound variants handle both orientation and position combinations:\n * - Horizontal: left/right borders and radii\n * - Vertical: top/bottom borders and radii\n */\nexport const buttonGroupItemVariants = cva('', {\n variants: {\n orientation: {\n // min-w-[44px] ensures visual layer fills touch target width (for icon buttons)\n horizontal: 'min-w-[44px]',\n // flex (overrides inline-flex) + min-h-[44px] makes visual layer fill touch target,\n // eliminating gaps between stacked buttons in vertical orientation\n vertical: 'flex min-h-[44px]',\n },\n position: {\n first: '',\n middle: '',\n last: '',\n only: '', // Single button - no modifications needed\n },\n },\n compoundVariants: [\n // ==========================================================================\n // Horizontal Orientation\n // ==========================================================================\n {\n orientation: 'horizontal',\n position: 'first',\n className: 'rounded-r-none border-r-0',\n },\n {\n orientation: 'horizontal',\n position: 'middle',\n className: 'rounded-none border-r-0',\n },\n {\n orientation: 'horizontal',\n position: 'last',\n className: 'rounded-l-none',\n },\n // ==========================================================================\n // Vertical Orientation\n // Note: w-full is handled by Button's effectiveFullWidth for both layers\n // ==========================================================================\n {\n orientation: 'vertical',\n position: 'first',\n className: 'rounded-b-none border-b-0',\n },\n {\n orientation: 'vertical',\n position: 'middle',\n className: 'rounded-none border-b-0',\n },\n {\n orientation: 'vertical',\n position: 'last',\n className: 'rounded-t-none',\n },\n ],\n defaultVariants: {\n orientation: 'horizontal',\n position: 'only',\n },\n});\n\n// =============================================================================\n// Separator Variants\n// =============================================================================\n\n/**\n * ButtonGroupSeparator variants\n * Orientation-aware visual divider between button groups\n */\nexport const buttonGroupSeparatorVariants = cva('bg-[var(--border)]', {\n variants: {\n orientation: {\n horizontal: 'w-px h-6 mx-1',\n vertical: 'h-px w-full my-1',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n },\n});\n","\"use client\";\n\n/**\n * Button Component - 3-Layer Architecture\n * Accessible button with React Aria primitives and CVA styling\n *\n * Architecture:\n * - Layer 1: Touch Target (44x44px WCAG AAA compliant, transparent)\n * - Layer 2: Visual Button (configurable size, colors, borders)\n * - Layer 3: Content & Effects (text, icons, ripple, loading spinner)\n *\n * @see 3layer-plan.md for architecture details\n * @see spec.md FR-029 to FR-037 (Button Component Requirements)\n * @see spec.md FR-009 (WCAG 2.2 AAA - 7:1 contrast ratio)\n * @see spec.md FR-014 (44x44px minimum touch targets)\n * @see constitution.md Principle IV (Accessibility First)\n */\n\nimport { forwardRef, memo, useId } from 'react';\nimport {\n Button as AriaButton,\n type ButtonProps as AriaButtonProps,\n} from 'react-aria-components';\nimport { Loader2, Zap } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport type { ButtonProps } from './Button.types';\nimport { buttonOuterVariants, buttonVisualVariants, buttonVariants } from './Button.styles';\nimport { PRESSED_STYLES, HOVER_STYLES, HIGH_CONTRAST_HOVER, HIGH_CONTRAST_PRESSED } from '../../styles/interaction-states';\nimport {\n useButtonGroupContext,\n useButtonGroupItemContext,\n} from '../ButtonGroup/ButtonGroupContext';\nimport { buttonGroupItemVariants } from '../ButtonGroup/ButtonGroup.variants';\n\n/**\n * Button Component - 3-Layer Architecture\n * Fully accessible button with React Aria and themed styling\n *\n * Layer 1: Touch Target (AriaButton) - 44x44px WCAG AAA compliant\n * Layer 2: Visual Button (span) - configurable appearance\n * Layer 3: Content (children) - text, icons, effects\n */\nconst Button = memo(forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n buttonVisualClassName,\n variant,\n size,\n visualSize,\n fullWidth,\n loading = false,\n loadingText = \"Loading...\",\n shortcut,\n children,\n isDisabled,\n paywall = false,\n paywallRedirect,\n paywallDescription,\n onPress,\n ...props\n },\n ref\n ) => {\n const paywallDescriptionId = useId();\n\n // ==========================================================================\n // ButtonGroup Context Integration\n // ==========================================================================\n\n // Consume group-level context (variant, size, isDisabled, orientation)\n const groupContext = useButtonGroupContext();\n\n // Consume item-level context (position for border-radius styling)\n const itemContext = useButtonGroupItemContext();\n\n // Merge context values with props (props take precedence)\n const effectiveVariant = variant ?? groupContext?.variant ?? 'default';\n const effectiveSize = size ?? groupContext?.size;\n const effectiveIsDisabled = isDisabled ?? groupContext?.isDisabled ?? false;\n\n // In vertical groups, buttons should be full width automatically\n const isInVerticalGroup = groupContext?.orientation === 'vertical';\n const effectiveFullWidth = fullWidth || isInVerticalGroup;\n\n // Position styling for ButtonGroup (only applied when in a group)\n const positionClassName = itemContext\n ? buttonGroupItemVariants({\n orientation: groupContext?.orientation ?? 'horizontal',\n position: itemContext.position,\n })\n : '';\n\n // Default visualSize to size for backward compatibility\n const effectiveVisualSize = visualSize ?? effectiveSize ?? 'default';\n\n // AAA Accessibility: Warn in dev/test if icon/dot variant lacks accessible name\n if (process.env.NODE_ENV !== 'production') {\n if (\n (effectiveVisualSize === 'dot' || effectiveVisualSize === 'icon') &&\n !props['aria-label'] &&\n !children\n ) {\n console.warn(\n '[Button] visualSize=\"dot\" or \"icon\" requires aria-label when no visible text is provided (WCAG 1.1.1)'\n );\n }\n }\n\n /**\n * Handle button press - intercepts action when paywalled\n * If paywalled with redirect URL, opens in new tab\n * Otherwise, calls the normal onPress handler\n */\n const handlePress = (e: Parameters<NonNullable<AriaButtonProps['onPress']>>[0]): void => {\n if (paywall) {\n if (paywallRedirect) {\n window.open(paywallRedirect, '_blank', 'noopener,noreferrer');\n }\n // Don't call onPress when paywalled\n return;\n }\n onPress?.(e);\n };\n\n // Only set isDisabled when we have a reason to disable\n // Otherwise, let slot system control disabled state (e.g., in NumberField)\n const computedIsDisabled = effectiveIsDisabled || loading || undefined;\n\n return (\n <AriaButton\n ref={ref}\n isDisabled={computedIsDisabled}\n aria-disabled={paywall ? true : undefined}\n aria-describedby={paywall ? paywallDescriptionId : undefined}\n onPress={handlePress}\n className={cn(buttonOuterVariants({ fullWidth: effectiveFullWidth, inVerticalGroup: isInVerticalGroup }), className)}\n {...props}\n >\n {(renderProps) => (\n /* Layer 2: Visual Button */\n <span\n className={cn(\n buttonVisualVariants({\n variant: effectiveVariant,\n visualSize: effectiveVisualSize,\n paywall,\n fullWidth: effectiveFullWidth,\n }),\n // Position styling from ButtonGroup context (border-radius adjustments)\n positionClassName,\n buttonVisualClassName,\n // Layer 2 interaction styles (no focus - focus ring is on Layer 1)\n PRESSED_STYLES,\n HOVER_STYLES,\n HIGH_CONTRAST_HOVER,\n HIGH_CONTRAST_PRESSED\n )}\n data-pressed={renderProps.isPressed || undefined}\n >\n {/* Layer 3: Content & Effects */}\n\n {/* FR-033: Loading spinner with screen reader announcement */}\n {/* Uses motion-safe: for WCAG 2.3.3 AAA (Animation from Interactions) */}\n {loading && (\n <>\n <Loader2 className=\"motion-safe:animate-spin\" aria-hidden=\"true\" />\n <span className=\"sr-only\" aria-live=\"polite\">\n {loadingText}\n </span>\n </>\n )}\n\n {/* Hide children during loading */}\n {!loading && children}\n\n {/* Paywall: Lightning bolt icon */}\n {paywall && (\n <Zap\n data-testid=\"zap-icon\"\n aria-hidden=\"true\"\n className=\"ml-1\"\n />\n )}\n\n {/* Paywall: Screen reader description */}\n {paywall && (\n <span id={paywallDescriptionId} className=\"sr-only\">\n Premium feature: {paywallDescription || \"Upgrade required to access this feature\"}\n </span>\n )}\n\n {/* FR-034: Keyboard shortcut display on focus */}\n {renderProps.isFocusVisible && shortcut && (\n <kbd className=\"ml-auto hidden text-xs opacity-60 lg:inline\">\n {shortcut}\n </kbd>\n )}\n\n {/* Touch/press ripple effect - FR-031: Pressed state feedback */}\n {/* Uses motion-safe: for WCAG 2.3.3 AAA (Animation from Interactions) */}\n {renderProps.isPressed && (\n <span\n className=\"absolute inset-0 rounded-[inherit] bg-current opacity-10 motion-safe:animate-in motion-safe:zoom-in-95\"\n aria-hidden=\"true\"\n />\n )}\n </span>\n )}\n </AriaButton>\n );\n }\n));\n\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants, buttonOuterVariants, buttonVisualVariants };\nexport type { ButtonProps } from './Button.types';\n","'use client';\n\n/**\n * Toast Component - Accessible notification system\n * Built with React Aria toast primitives and CVA styling\n *\n * @see toast-prd.md - Product Requirements Document\n * @see plan.md - Implementation Plan\n * @see constitution.md Principle IV (Accessibility First - WCAG 2.2 AAA)\n */\n\nimport {\n forwardRef,\n memo,\n useRef,\n useState,\n useCallback,\n type TouchEvent as ReactTouchEvent,\n} from 'react';\nimport { useToast } from 'react-aria';\nimport { ToastQueue, type QueuedToast, type ToastState as AriaToastState } from 'react-stately';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { X } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { Button } from '../Button';\nimport type {\n ToastVariant,\n ToastContent,\n ToastState,\n DismissReason,\n ToastConfig,\n} from './Toast.types';\n\n// =============================================================================\n// CVA Variants\n// =============================================================================\n\n/**\n * Toast container variants with theme-aligned styling\n * Fixed 356px width for consistent appearance across all toasts\n */\nexport const toastVariants = cva(\n [\n // Fixed width for consistent appearance\n 'pointer-events-auto relative flex w-[356px] items-center justify-between',\n 'space-x-4 overflow-hidden rounded-lg border p-4 pr-12 shadow-lg',\n // Animations with reduced-motion support\n 'transition-all duration-300',\n 'motion-reduce:transition-none motion-reduce:animate-none',\n 'data-[entering]:animate-in data-[entering]:slide-in-from-left',\n 'data-[exiting]:animate-out data-[exiting]:slide-out-to-right-full',\n 'data-[exiting]:fade-out-80',\n // Touch interaction for swipe\n 'touch-pan-y',\n ],\n {\n variants: {\n variant: {\n default: [\n 'bg-[var(--content-background)]',\n 'text-[var(--content-foreground)]',\n 'border-[var(--border)]',\n ],\n primary: [\n 'bg-[var(--primary)]',\n 'text-[var(--primary-foreground)]',\n 'border-[var(--primary)]',\n ],\n destructive: [\n 'bg-[var(--destructive-background)]',\n 'text-[var(--destructive-foreground)]',\n 'border-[var(--destructive-background)]',\n ],\n accent: [\n 'bg-[var(--accent-background)]',\n 'text-[var(--accent-foreground)]',\n 'border-[var(--accent-background)]',\n ],\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n }\n);\n\n/**\n * Toaster position variants for the toast region container\n */\nexport const toasterVariants = cva(\n [\n 'fixed z-50 flex flex-col gap-2 p-4',\n 'pointer-events-none',\n 'max-h-screen overflow-hidden',\n ],\n {\n variants: {\n position: {\n 'top-left': 'top-0 left-0 items-start',\n 'top-center': 'top-0 left-1/2 -translate-x-1/2 items-center',\n 'top-right': 'top-0 right-0 items-end',\n 'bottom-left': 'bottom-0 left-0 items-start',\n 'bottom-center': 'bottom-0 left-1/2 -translate-x-1/2 items-center',\n 'bottom-right': 'bottom-0 right-0 items-end',\n },\n },\n defaultVariants: {\n position: 'bottom-right',\n },\n }\n);\n\n// =============================================================================\n// Close Button Component\n// =============================================================================\n\ninterface CloseButtonProps {\n variant?: ToastVariant;\n className?: string;\n onPress?: () => void;\n}\n\n/**\n * Close button component with 44x44px touch target\n * WCAG 2.2 AAA compliant touch target size\n */\nconst CloseButton = memo(\n forwardRef<HTMLButtonElement, CloseButtonProps>(\n ({ variant = 'default', className, onPress}, ref) => {\n // Variant-aware hover styles using theme tokens\n const hoverStyles =\n variant === 'default'\n ? 'hover:bg-[var(--accent-background)]/20'\n : 'hover:bg-[var(--page-background)]/20';\n\n return (\n <Button\n ref={ref}\n variant=\"ghost\"\n visualSize=\"icon\"\n onPress={onPress}\n className={cn(\n 'absolute right-2 top-2',\n hoverStyles,\n className\n )}\n aria-label=\"Dismiss notification\"\n >\n <X className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n );\n }\n )\n);\n\nCloseButton.displayName = 'CloseButton';\n\n// =============================================================================\n// Global Toast Queue\n// =============================================================================\n\n/**\n * Internal toast queue instance\n */\nconst internalQueue = new ToastQueue<ToastState>({\n maxVisibleToasts: 3,\n});\n\n/**\n * Track total active toasts (visible + queued)\n * React Stately doesn't expose queued toasts, so we track manually\n */\nlet totalActiveToasts = 0;\nconst toastCountListeners = new Set<() => void>();\n\nfunction incrementTotalToasts(): void {\n totalActiveToasts++;\n toastCountListeners.forEach((fn) => fn());\n}\n\nfunction decrementTotalToasts(): void {\n totalActiveToasts = Math.max(0, totalActiveToasts - 1);\n toastCountListeners.forEach((fn) => fn());\n}\n\n/**\n * Get the total number of active toasts (visible + queued)\n */\nexport function getTotalActiveToasts(): number {\n return totalActiveToasts;\n}\n\n/**\n * Subscribe to total toast count changes\n */\nexport function subscribeToTotalCount(fn: () => void): () => void {\n toastCountListeners.add(fn);\n return () => toastCountListeners.delete(fn);\n}\n\n/**\n * Enhanced toast queue API with additional methods\n * Provides closeAll, pauseAll, resumeAll on top of base ToastQueue\n */\nexport const toastQueue = {\n /**\n * Add a toast to the queue\n */\n add: (content: ToastState, options?: { timeout?: number; onClose?: () => void }) =>\n internalQueue.add(content, options),\n\n /**\n * Close a specific toast by key\n */\n close: (key: string) => {\n internalQueue.close(key);\n },\n\n /**\n * Close all toasts (visible and queued)\n */\n closeAll: () => {\n // Close all visible toasts\n internalQueue.visibleToasts.forEach((toast) => {\n internalQueue.close(toast.key);\n });\n },\n\n /**\n * Pause all toast timers\n */\n pauseAll: () => {\n internalQueue.pauseAll();\n },\n\n /**\n * Resume all toast timers\n */\n resumeAll: () => {\n internalQueue.resumeAll();\n },\n\n /**\n * Get the visible toasts\n */\n get visibleToasts() {\n return internalQueue.visibleToasts;\n },\n\n /**\n * Subscribe to queue changes\n */\n subscribe: (fn: () => void) => internalQueue.subscribe(fn),\n\n /**\n * Get total active toasts (visible + queued)\n */\n get totalCount() {\n return totalActiveToasts;\n },\n};\n\n/**\n * Get the internal queue for use in components\n */\nexport function getInternalQueue(): ToastQueue<ToastState> {\n return internalQueue;\n}\n\n// =============================================================================\n// Toast Function (Programmatic API)\n// =============================================================================\n\n/**\n * Enforce minimum timeout of 5000ms for accessibility\n * WCAG 2.2 timing guidelines require sufficient time for reading\n * Timeout of 0 bypasses this (persistent toast)\n */\nfunction enforceMinTimeout(timeout: number): number {\n if (timeout === 0) return 0; // Persistent toast\n return Math.max(timeout, 5000);\n}\n\n/**\n * Add a toast notification programmatically\n *\n * @param content - Content to display (string or ReactNode)\n * @param options - Toast configuration options\n * @returns Unique key for the created toast\n *\n * @example\n * ```tsx\n * // Simple string\n * toast(\"Message saved!\");\n *\n * // With variant\n * toast(\"Error occurred\", { variant: \"destructive\" });\n *\n * // With callback\n * toast(\"Action completed\", {\n * variant: \"primary\",\n * onClose: (key, reason) => console.log(`Closed: ${reason}`)\n * });\n * ```\n */\n/**\n * Apply defaults to toast options without Zod runtime overhead.\n */\nfunction applyToastDefaults(options: ToastConfig = {}): { variant: ToastVariant; timeout: number; onClose?: (key: string, reason: DismissReason) => void } {\n return {\n variant: options.variant ?? 'default',\n timeout: options.timeout ?? 5000,\n onClose: options.onClose,\n };\n}\n\nexport function toast(content: ToastContent, options?: ToastConfig): string {\n // Apply defaults (TypeScript handles validation at compile time)\n const validated = applyToastDefaults(options);\n\n // Enforce minimum timeout for accessibility\n const timeout = enforceMinTimeout(validated.timeout);\n\n // Track dismissal reason for callback\n const dismissReason: DismissReason = 'timeout';\n\n // Increment total toast count\n incrementTotalToasts();\n\n const key = toastQueue.add(\n {\n key: '', // Will be set by queue\n content,\n variant: validated.variant,\n timeout,\n onClose: validated.onClose,\n },\n {\n timeout,\n onClose: () => {\n // Decrement total toast count\n decrementTotalToasts();\n\n // Fire the user's onClose callback with reason\n if (validated.onClose) {\n validated.onClose(key, dismissReason);\n }\n },\n }\n );\n\n return key;\n}\n\n// =============================================================================\n// Toast Item Component\n// =============================================================================\n\nexport interface ToastItemProps {\n /** Toast from the queue */\n toast: QueuedToast<ToastState>;\n /** Toast state for managing dismissal */\n state: AriaToastState<ToastState>;\n}\n\n/**\n * Individual toast notification component\n * Uses React Aria useToast hook for accessibility\n */\nexport const ToastItem = memo(\n forwardRef<HTMLDivElement, ToastItemProps>(({ toast, state }, forwardedRef) => {\n const internalRef = useRef<HTMLDivElement>(null);\n const ref = (forwardedRef as React.RefObject<HTMLDivElement>) || internalRef;\n\n // React Aria toast hook for accessibility\n const { toastProps, contentProps } = useToast(\n { toast },\n state,\n ref\n );\n\n // Swipe-to-dismiss state\n const [touchStart, setTouchStart] = useState<number | null>(null);\n const [translateX, setTranslateX] = useState(0);\n\n const SWIPE_THRESHOLD = 50; // Minimum swipe distance to dismiss\n\n const handleTouchStart = useCallback((e: ReactTouchEvent) => {\n const touch = e.touches[0];\n if (touch) {\n setTouchStart(touch.clientX);\n }\n }, []);\n\n const handleTouchMove = useCallback(\n (e: ReactTouchEvent) => {\n if (touchStart === null) return;\n const touch = e.touches[0];\n if (!touch) return;\n const diff = touch.clientX - touchStart;\n // Only allow right swipe (positive diff)\n if (diff > 0) {\n setTranslateX(diff);\n }\n },\n [touchStart]\n );\n\n const handleTouchEnd = useCallback(() => {\n if (translateX > SWIPE_THRESHOLD) {\n // Dismiss the toast\n state.close(toast.key);\n }\n // Reset swipe state\n setTranslateX(0);\n setTouchStart(null);\n }, [translateX, state, toast.key]);\n\n const handleCloseClick = useCallback(() => {\n state.close(toast.key);\n }, [state, toast.key]);\n\n const { content } = toast;\n const variant = content.variant ?? 'default';\n\n // Generate accessible name for the toast\n const accessibleName = typeof content.content === 'string'\n ? content.content\n : 'Notification';\n\n return (\n <div\n {...toastProps}\n ref={ref}\n className={cn(toastVariants({ variant }))}\n aria-label={accessibleName}\n style={{\n transform: translateX > 0 ? `translateX(${translateX}px)` : undefined,\n opacity: translateX > 0 ? 1 - translateX / 200 : undefined,\n }}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleTouchEnd}\n >\n {/* Toast content */}\n <div {...contentProps} className=\"flex-1 min-w-0\">\n {typeof content.content === 'string' ? (\n <p className=\"text-sm font-medium\">{content.content}</p>\n ) : (\n content.content\n )}\n </div>\n\n {/* Close button */}\n <CloseButton\n variant={variant}\n onPress={handleCloseClick}\n />\n </div>\n );\n })\n);\n\nToastItem.displayName = 'ToastItem';\n\n// =============================================================================\n// Export Types\n// =============================================================================\n\nexport type { VariantProps };\n","import { z } from 'zod';\nimport type { ReactNode } from 'react';\n\n/**\n * Toast Component Type Definitions\n *\n * @see toast-prd.md - Product Requirements Document\n * @see plan.md - Implementation Plan\n * @see constitution.md Principle IV (Accessibility First - WCAG 2.2 AAA)\n */\n\n// =============================================================================\n// Enums and Primitives\n// =============================================================================\n\n/**\n * Reason why a toast was dismissed\n * - 'timeout': Auto-dismissed after timeout period\n * - 'user': User clicked close button or swiped to dismiss\n * - 'programmatic': Closed via toastQueue.close() or toastQueue.closeAll()\n */\nexport const DismissReasonSchema = z.enum(['timeout', 'user', 'programmatic']);\nexport type DismissReason = z.infer<typeof DismissReasonSchema>;\n\n/**\n * Toast visual variants matching the Themis theme system\n * Each variant maps to specific theme tokens for background/foreground colors\n *\n * @see toast-prd.md Variant Token Mapping table\n */\nexport const ToastVariantSchema = z.enum([\n 'default',\n 'primary',\n 'destructive',\n 'accent',\n]);\nexport type ToastVariant = z.infer<typeof ToastVariantSchema>;\n\n/**\n * Toast region positioning options\n * Toasts appear in a fixed position on the viewport\n */\nexport const ToasterPositionSchema = z.enum([\n 'top-left',\n 'top-center',\n 'top-right',\n 'bottom-left',\n 'bottom-center',\n 'bottom-right',\n]);\nexport type ToasterPosition = z.infer<typeof ToasterPositionSchema>;\n\n// =============================================================================\n// Toast Options Schema\n// =============================================================================\n\n/**\n * Options for creating a toast notification\n *\n * @example\n * ```tsx\n * toast(\"Message\", {\n * variant: \"primary\",\n * timeout: 8000,\n * onClose: (key, reason) => console.log(`Toast ${key} closed: ${reason}`)\n * });\n * ```\n */\nexport const ToastOptionsSchema = z.object({\n /**\n * Visual variant determining background and foreground colors\n * @default 'default'\n */\n variant: ToastVariantSchema.default('default'),\n\n /**\n * Auto-dismiss timeout in milliseconds\n * - Values 1-4999ms are clamped to 5000ms (WCAG 2.2 timing requirement)\n * - Set to 0 for persistent toasts (no auto-dismiss)\n * @default 5000\n */\n timeout: z.number().min(0).default(5000),\n\n /**\n * Callback fired when toast is dismissed\n * @param key - Unique identifier of the toast\n * @param reason - Why the toast was dismissed ('timeout' | 'user' | 'programmatic')\n */\n onClose: z.function().optional(),\n});\n\n/**\n * Inferred type from Zod schema\n * Note: onClose is typed as generic function in Zod, but we override with proper signature below\n */\ntype ToastOptionsBase = z.infer<typeof ToastOptionsSchema>;\n\n/**\n * Toast options with properly typed onClose callback\n */\nexport type ToastOptions = Omit<ToastOptionsBase, 'onClose'> & {\n /**\n * Callback fired when toast is dismissed\n * @param key - Unique identifier of the toast\n * @param reason - Why the toast was dismissed\n */\n onClose?: (key: string, reason: DismissReason) => void;\n};\n\n// =============================================================================\n// Provider Props Schema\n// =============================================================================\n\n/**\n * Props for the ToastProvider component\n * Wraps the application to provide toast queue context\n *\n * @example\n * ```tsx\n * <ToastProvider maxVisibleToasts={3}>\n * <App />\n * <Toaster position=\"bottom-right\" />\n * </ToastProvider>\n * ```\n */\nexport const ToastProviderPropsSchema = z.object({\n /**\n * Maximum number of toasts visible at once\n * Additional toasts are queued and shown as others dismiss\n * @default 3\n */\n maxVisibleToasts: z.number().min(1).default(3),\n});\n\nexport type ToastProviderProps = z.infer<typeof ToastProviderPropsSchema> & {\n /** Child components that can trigger toasts */\n children: ReactNode;\n};\n\n// =============================================================================\n// Toaster Props Schema\n// =============================================================================\n\n/**\n * Props for the Toaster component (toast region container)\n * Renders the toast region with positioning and accessibility attributes\n *\n * @example\n * ```tsx\n * <Toaster\n * position=\"bottom-right\"\n * aria-label=\"Notifications\"\n * className=\"custom-toast-region\"\n * />\n * ```\n */\nexport const ToasterPropsSchema = z.object({\n /**\n * Position of the toast region on screen\n * @default 'bottom-right'\n */\n position: ToasterPositionSchema.default('bottom-right'),\n\n /**\n * Accessible label for the toast region landmark\n * Used by screen readers to identify the notification area\n * @default 'Notifications'\n */\n 'aria-label': z.string().default('Notifications'),\n\n /**\n * Additional CSS classes for the toast region\n */\n className: z.string().optional(),\n});\n\nexport type ToasterProps = z.infer<typeof ToasterPropsSchema>;\n\n// =============================================================================\n// Internal Types (not exported from index.ts)\n// =============================================================================\n\n/**\n * Internal toast state stored in the queue\n * Contains all information needed to render and manage a toast\n */\nexport interface ToastState {\n /** Unique identifier for the toast */\n key: string;\n\n /** Content to render inside the toast (ReactNode or string) */\n content: ReactNode;\n\n /** Visual variant for styling */\n variant: ToastVariant;\n\n /** Auto-dismiss timeout in milliseconds (0 = persistent) */\n timeout: number;\n\n /** Callback fired when toast is dismissed */\n onClose?: (key: string, reason: DismissReason) => void;\n}\n\n/**\n * Toast content type - fully customizable by developer\n * Can be a simple string or complex ReactNode with custom layout\n */\nexport type ToastContent = ReactNode;\n\n/**\n * Props for the internal ToastItem component\n */\nexport interface ToastItemProps {\n /** Toast state from the queue */\n toast: {\n key: string;\n content: ToastState;\n animation: 'entering' | 'queued' | 'exiting';\n priority: number;\n };\n\n /** Toast queue state for managing dismissal */\n state: {\n close: (key: string) => void;\n };\n}\n\n// =============================================================================\n// Toast Queue API Types\n// =============================================================================\n\n/**\n * Public API for the toast queue\n * Allows programmatic control of toasts from anywhere in the app\n */\nexport interface ToastQueueAPI {\n /**\n * Add a new toast to the queue\n * @param content - Content to display in the toast\n * @param options - Toast configuration options\n * @returns Unique key for the created toast\n */\n add: (content: ToastContent, options?: Partial<ToastOptions>) => string;\n\n /**\n * Close a specific toast by key\n * @param key - Unique identifier of the toast to close\n */\n close: (key: string) => void;\n\n /**\n * Close all toasts (visible and queued)\n */\n closeAll: () => void;\n\n /**\n * Pause all toast timers\n * Useful when user is interacting with the toast region\n */\n pauseAll: () => void;\n\n /**\n * Resume all paused toast timers\n */\n resumeAll: () => void;\n}\n\n// =============================================================================\n// Utility Types\n// =============================================================================\n\n/**\n * Configuration for the toast() function\n * Partial version of ToastOptions for convenience\n */\nexport type ToastConfig = Partial<ToastOptions>;\n\n/**\n * Return type of the toast() function\n */\nexport type ToastKey = string;\n","'use client';\n\n/**\n * ToastProvider Component\n * Provides toast queue context for the application\n *\n * @see toast-prd.md - Product Requirements Document\n * @see plan.md - Implementation Plan\n */\n\nimport { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport { ToastQueue } from 'react-stately';\nimport type { ToastState, ToastProviderProps } from './Toast.types';\nimport { ToastProviderPropsSchema } from './Toast.types';\nimport { getInternalQueue } from './Toast';\n\n// =============================================================================\n// Context\n// =============================================================================\n\n/**\n * Context for the toast queue\n * Allows child components to access the toast queue state\n */\nexport const ToastContext = createContext<ToastQueue<ToastState> | null>(null);\n\n/**\n * Hook to access the toast queue from context\n * @returns The toast queue instance\n * @throws Error if used outside ToastProvider\n */\nexport function useToastQueue(): ToastQueue<ToastState> {\n const context = useContext(ToastContext);\n if (!context) {\n throw new Error('useToastQueue must be used within a ToastProvider');\n }\n return context;\n}\n\n// =============================================================================\n// Provider Component\n// =============================================================================\n\n/**\n * ToastProvider - Wraps application to provide toast functionality\n *\n * @example\n * ```tsx\n * <ToastProvider maxVisibleToasts={3}>\n * <App />\n * <Toaster position=\"bottom-right\" />\n * </ToastProvider>\n * ```\n */\nexport function ToastProvider({\n children,\n maxVisibleToasts = 3,\n}: ToastProviderProps): ReactNode {\n // Validate props with Zod schema (throws on invalid input)\n ToastProviderPropsSchema.parse({ maxVisibleToasts });\n\n // Create queue with configured maxVisibleToasts\n // Note: We use the global toastQueue for now for simplicity\n // In a more complex setup, we could create a new queue per provider\n const queue = useMemo(() => {\n // Get the internal queue instance\n // Note: maxVisibleToasts is set on the global queue, not per-provider\n return getInternalQueue();\n }, []);\n\n return (\n <ToastContext.Provider value={queue}>{children}</ToastContext.Provider>\n );\n}\n\nToastProvider.displayName = 'ToastProvider';\n","'use client';\n\n/**\n * Toaster Component - Toast region container\n * Renders visible toasts with positioning and accessibility attributes\n *\n * @see toast-prd.md - Product Requirements Document\n * @see plan.md - Implementation Plan\n * @see constitution.md Principle IV (Accessibility First - WCAG 2.2 AAA)\n */\n\nimport { forwardRef, useRef, memo, useCallback, useSyncExternalStore, type ReactElement } from 'react';\nimport { useToastRegion } from 'react-aria';\nimport { useToastQueue as useAriaToastQueue } from 'react-stately';\nimport { cn } from '../../utils/cn';\nimport { Button } from '../Button';\nimport { toasterVariants, getInternalQueue, ToastItem, getTotalActiveToasts, subscribeToTotalCount } from './Toast';\nimport type { ToasterProps } from './Toast.types';\nimport { ToasterPropsSchema } from './Toast.types';\n\n// =============================================================================\n// Stacked Toast Visual Effect\n// =============================================================================\n\ninterface StackedToastIndicatorProps {\n queuedCount: number;\n}\n\n/**\n * Visual stacking effect showing ghost toasts behind visible ones\n * Creates depth perception for queued notifications\n */\nfunction StackedToastIndicator({ queuedCount }: StackedToastIndicatorProps): ReactElement | null {\n if (queuedCount <= 0) return null;\n\n // Show up to 2 visual stack layers\n const stackLayers = Math.min(queuedCount, 2);\n\n return (\n <div className=\"relative w-[356px] pointer-events-none\" aria-hidden=\"true\">\n {/* Stack layer 1 (closest to visible toasts) */}\n <div\n className={cn(\n 'absolute left-1/2 -translate-x-1/2 w-[340px] h-4 rounded-lg shadow-lg',\n 'bg-[var(--content-background)] border border-[var(--border)]',\n 'opacity-60',\n 'transition-all duration-200'\n )}\n style={{ top: 0 }}\n />\n\n {/* Stack layer 2 (further back, if 2+ queued) */}\n {stackLayers >= 2 && (\n <div\n className={cn(\n 'absolute left-1/2 -translate-x-1/2 w-[324px] h-2 rounded-b-lg shadow-lg',\n 'bg-[var(--content-background)] border border-t-0 border-[var(--border)]',\n 'opacity-40',\n 'transition-all duration-200'\n )}\n style={{ top: 14 }}\n />\n )}\n\n {/* Spacer to account for stack height */}\n <div className=\"h-4\" />\n\n {/* Text indicator */}\n <div\n className=\"text-sm text-[var(--content-foreground)] opacity-70 text-center py-2 pointer-events-auto\"\n aria-live=\"polite\"\n >\n +{queuedCount} more notification{queuedCount > 1 ? 's' : ''}\n </div>\n </div>\n );\n}\n\n// =============================================================================\n// Toaster Component\n// =============================================================================\n\n/**\n * Toaster - Toast region container\n * Renders the toast notification area with proper ARIA attributes\n *\n * @example\n * ```tsx\n * <ToastProvider>\n * <App />\n * <Toaster position=\"bottom-right\" aria-label=\"Notifications\" />\n * </ToastProvider>\n * ```\n */\nexport const Toaster = memo(\n forwardRef<HTMLDivElement, ToasterProps>((props, forwardedRef) => {\n // Validate props with Zod schema\n const validated = ToasterPropsSchema.parse(props);\n const {\n position = 'bottom-right',\n 'aria-label': ariaLabel = 'Notifications',\n className,\n } = validated;\n\n const internalRef = useRef<HTMLDivElement>(null);\n const ref = (forwardedRef as React.RefObject<HTMLDivElement>) || internalRef;\n\n // Use the global toast queue state\n const internalQueueRef = getInternalQueue();\n const state = useAriaToastQueue(internalQueueRef);\n\n // Subscribe to total toast count (includes queued toasts)\n const totalCount = useSyncExternalStore(\n subscribeToTotalCount,\n getTotalActiveToasts,\n getTotalActiveToasts\n );\n\n // React Aria toast region hook for accessibility\n const { regionProps } = useToastRegion(\n { 'aria-label': ariaLabel },\n state,\n ref\n );\n\n // Calculate queued toast count for collapsed stack indicator\n // totalCount = all toasts (visible + queued), visibleToasts.length = currently shown\n const queuedCount = Math.max(0, totalCount - state.visibleToasts.length);\n\n // Handle \"Clear All\" button click\n const handleClearAll = useCallback(() => {\n // Close all visible toasts\n state.visibleToasts.forEach((toast) => {\n state.close(toast.key);\n });\n }, [state]);\n\n // Don't render if no toasts\n if (state.visibleToasts.length === 0) {\n return null;\n }\n\n return (\n <div\n {...regionProps}\n ref={ref}\n className={cn(toasterVariants({ position }), className)}\n >\n {/* Toast list */}\n {state.visibleToasts.map((toast) => (\n <ToastItem key={toast.key} toast={toast} state={state} />\n ))}\n\n {/* Visual stacking effect with ghost toasts */}\n <StackedToastIndicator queuedCount={queuedCount} />\n\n {/* \"Clear All\" button (when 2+ toasts visible) */}\n {state.visibleToasts.length >= 2 && (\n <Button\n variant=\"secondary\"\n visualSize=\"sm\"\n onPress={handleClearAll}\n aria-label=\"Clear all notifications\"\n className=\"mt-2 pointer-events-auto\"\n >\n Clear All\n </Button>\n )}\n </div>\n );\n })\n);\n\nToaster.displayName = 'Toaster';\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/cn.ts","../../../src/elements/Button/Button.styles.ts","../../../src/styles/interaction-states.ts","../../../src/elements/ButtonGroup/ButtonGroupContext.tsx","../../../src/elements/ButtonGroup/ButtonGroup.variants.ts","../../../src/elements/Button/Button.tsx","../../../src/elements/Toast/Toast.tsx","../../../src/elements/Toast/Toast.types.ts","../../../src/elements/Toast/ToastProvider.tsx","../../../src/elements/Toast/Toaster.tsx"],"names":["cn","inputs","twMerge","clsx","buttonOuterVariants","cva","buttonVisualVariants","PRESSED_STYLES","HOVER_STYLES","HIGH_CONTRAST_HOVER","HIGH_CONTRAST_PRESSED","ButtonGroupContext","createContext","useButtonGroupContext","useContext","ButtonGroupItemContext","useButtonGroupItemContext","buttonGroupItemVariants","Button","memo","forwardRef","className","buttonVisualClassName","variant","size","visualSize","fullWidth","loading","loadingText","shortcut","children","isDisabled","paywall","paywallRedirect","paywallDescription","onPress","props","ref","paywallDescriptionId","useId","groupContext","itemContext","effectiveVariant","effectiveSize","effectiveIsDisabled","isInVerticalGroup","effectiveFullWidth","positionClassName","effectiveVisualSize","jsx","AriaButton","e","renderProps","jsxs","Fragment","Loader2","Zap","toastVariants","toasterVariants","CloseButton","X","internalQueue","ToastQueue","totalActiveToasts","toastCountListeners","incrementTotalToasts","fn","decrementTotalToasts","getTotalActiveToasts","subscribeToTotalCount","toastQueue","content","options","key","toast","getInternalQueue","enforceMinTimeout","timeout","applyToastDefaults","validated","dismissReason","ToastItem","state","forwardedRef","internalRef","useRef","toastProps","contentProps","useToast","touchStart","setTouchStart","useState","translateX","setTranslateX","SWIPE_THRESHOLD","handleTouchStart","useCallback","touch","handleTouchMove","diff","handleTouchEnd","handleCloseClick","accessibleName","DismissReasonSchema","z","ToastVariantSchema","ToasterPositionSchema","ToastOptionsSchema","ToastProviderPropsSchema","ToasterPropsSchema","ToastContext","ToastProvider","maxVisibleToasts","queue","useMemo","StackedToastIndicator","queuedCount","stackLayers","Toaster","position","ariaLabel","internalQueueRef","useAriaToastQueue","totalCount","useSyncExternalStore","regionProps","useToastRegion","handleClearAll"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","import { cva } from 'class-variance-authority';\n\n/**\n * Layer 1: Transparent outer touch target (44x44px minimum)\n * Handles WCAG 2.2 AAA touch target requirement\n * Always transparent, centers the visual button inside\n * IMPORTANT: Focus ring stays on Layer 1 for AAA compliance (2.4.13)\n *\n * In vertical ButtonGroups, uses items-stretch so the visual layer (Layer 2)\n * can fill the full touch target height, eliminating gaps between buttons.\n */\nexport const buttonOuterVariants = cva(\n \"inline-flex justify-center min-h-[44px] min-w-[44px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n fullWidth: {\n true: \"w-full\",\n false: \"\",\n },\n inVerticalGroup: {\n true: \"items-stretch\",\n false: \"items-center\",\n },\n },\n defaultVariants: {\n fullWidth: false,\n inVerticalGroup: false,\n },\n }\n);\n\n/**\n * Layer 2: Visual button appearance (adjustable size)\n * Provides the visual appearance with configurable size\n * Can be smaller than touch target for use cases like carousel dots\n * NOTE: NO focus-visible styles here - focus ring is on Layer 1\n */\nexport const buttonVisualVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 relative cursor-pointer\",\n {\n variants: {\n variant: {\n default:\n \"bg-[var(--primary-action)] text-[var(--primary-action-foreground)] shadow-md hover:bg-[var(--primary-action-hover)] data-[pressed]:bg-[var(--primary-action)]/80\",\n destructive:\n \"bg-[var(--destructive-background)] text-[var(--destructive-foreground)] shadow-md hover:bg-[var(--destructive-background)]/90 data-[pressed]:bg-[var(--destructive-background)]/80\",\n outline:\n \"border border-[var(--input-border)] bg-[var(--page-background)] hover:bg-[var(--input-border)] data-[pressed]:bg-[var(--input-border)]\",\n secondary:\n \"bg-[var(--secondary)] text-[var(--secondary-foreground)] shadow-md hover:bg-[var(--secondary)]/80 data-[pressed]:bg-[var(--secondary)]/70\",\n ghost:\n \"hover:bg-[var(--accent)] hover:text-[var(--accent-foreground)] data-[pressed]:bg-[var(--accent)]\",\n link: \"text-[var(--text-link)] underline-offset-4 hover:underline data-[pressed]:text-[var(--text-link-hover)]\",\n },\n fullWidth: {\n true: \"w-full\",\n false: \"\",\n },\n visualSize: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3 text-xs\",\n lg: \"h-11 rounded-md px-8\",\n icon: \"h-10 w-10\",\n dot: \"h-5 w-5 rounded-full p-0 min-h-0 min-w-0\",\n },\n paywall: {\n true: \"!bg-[var(--paywall)] !text-[var(--paywall-foreground)] !shadow-md hover:!bg-[var(--paywall)]/90 !cursor-not-allowed !border-transparent\",\n false: \"\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n visualSize: \"default\",\n paywall: false,\n },\n }\n);\n\n/**\n * @deprecated Use buttonVisualVariants instead. This alias is kept for backward compatibility.\n */\nexport const buttonVariants = buttonVisualVariants;\n","/**\n * Global Interaction State Styles\n *\n * Consistent interaction patterns across all Themis components.\n * These styles ensure WCAG 2.2 AAA compliance and predictable UX.\n *\n * @see spec.md FR-010 (Visible focus ring for keyboard navigation)\n * @see spec.md FR-031 (Pressed state feedback)\n * @see spec.md FR-012 (High contrast mode support)\n * @see constitution.md Principle IV (Accessibility First)\n */\n\n/**\n * Focus state styles (FR-010)\n * Visible focus ring for keyboard navigation - WCAG 2.2 Level AAA\n *\n * Components can override by extending these styles:\n * @example\n * cn(FOCUS_STYLES, \"ring-4\") // Increases ring width to 4px\n */\nexport const FOCUS_STYLES = \"data-[focus-visible]:ring-2 data-[focus-visible]:ring-[var(--themis-ring)] data-[focus-visible]:ring-offset-2\";\n\n/**\n * Pressed/Active state styles (FR-031)\n * Visual feedback for press interactions\n *\n * Components can override the scale amount:\n * @example\n * cn(PRESSED_STYLES_BASE, \"data-[pressed]:scale-[0.95]\") // More pronounced scale\n */\nexport const PRESSED_STYLES = \"data-[pressed]:scale-[0.97]\";\n\n/**\n * Base pressed styles without scale (for components that need different feedback)\n */\nexport const PRESSED_STYLES_BASE = \"data-[pressed]:transition-transform data-[pressed]:duration-100\";\n\n/**\n * Hover state styles\n * Elevation change on hover for better affordance\n *\n * Components can override shadow depth:\n * @example\n * cn(HOVER_STYLES_BASE, \"data-[hovered]:shadow-lg\") // Larger shadow\n */\nexport const HOVER_STYLES = \"data-[hovered]:shadow-md\";\n\n/**\n * Base hover styles without shadow (for components that use different hover effects)\n */\nexport const HOVER_STYLES_BASE = \"data-[hovered]:transition-shadow data-[hovered]:duration-200\";\n\n/**\n * High contrast mode focus (FR-012)\n * Enhanced outlines for users requiring high contrast\n *\n * Uses 'hc:' prefix for prefers-contrast: more media query\n */\nexport const HIGH_CONTRAST_FOCUS = \"hc:data-[focus-visible]:outline hc:data-[focus-visible]:outline-4 hc:data-[focus-visible]:outline-offset-2 hc:data-[focus-visible]:outline-foreground\";\n\n/**\n * High contrast mode hover (FR-012)\n * Enhanced outlines for hover in high contrast mode\n */\nexport const HIGH_CONTRAST_HOVER = \"hc:data-[hovered]:outline hc:data-[hovered]:outline-2 hc:data-[hovered]:outline-foreground\";\n\n/**\n * High contrast mode pressed state\n * Enhanced outlines for pressed state in high contrast mode\n */\nexport const HIGH_CONTRAST_PRESSED = \"hc:data-[pressed]:outline hc:data-[pressed]:outline-2 hc:data-[pressed]:outline-offset-1 hc:data-[pressed]:outline-foreground\";\n\n/**\n * Combined high contrast styles\n * Use this for components that need all high contrast interaction states\n */\nexport const HIGH_CONTRAST_INTERACTIONS = `${HIGH_CONTRAST_FOCUS} ${HIGH_CONTRAST_HOVER} ${HIGH_CONTRAST_PRESSED}`;\n\n/**\n * Disabled state styles\n * Consistent disabled appearance across all components\n */\nexport const DISABLED_STYLES = \"disabled:pointer-events-none disabled:opacity-50\";\n\n/**\n * Default interaction bundle\n * Most common combination for interactive components\n *\n * Includes: focus ring, pressed scale, hover shadow, high contrast enhancements\n *\n * @example\n * <button className={cn(DEFAULT_INTERACTIONS, \"bg-primary\")}>Click</button>\n */\nexport const DEFAULT_INTERACTIONS = `${FOCUS_STYLES} ${PRESSED_STYLES} ${HOVER_STYLES} ${HIGH_CONTRAST_FOCUS} ${HIGH_CONTRAST_HOVER} ${HIGH_CONTRAST_PRESSED}`;\n\n/**\n * Subtle interaction bundle\n * For components that need less pronounced feedback\n *\n * Includes: focus ring and high contrast, but no hover shadow or pressed scale\n */\nexport const SUBTLE_INTERACTIONS = `${FOCUS_STYLES} ${HIGH_CONTRAST_FOCUS}`;\n\n/**\n * Non-interactive element styles\n * For elements that should indicate they are not interactive\n */\nexport const NON_INTERACTIVE = \"cursor-default select-none\";\n","\"use client\";\n\nimport { createContext, useContext } from 'react';\nimport type {\n ButtonGroupContextValue,\n ButtonGroupItemContextValue,\n} from './ButtonGroup.types';\n\n/**\n * ButtonGroup Context System (Two-Level)\n *\n * Provides a two-level context pattern for ButtonGroup:\n *\n * 1. ButtonGroupContext (group-level):\n * - Provides: orientation, variant, size, isDisabled\n * - Consumed by: Button (for prop inheritance), Separator (for orientation)\n *\n * 2. ButtonGroupItemContext (item-level):\n * - Provides: position ('first' | 'middle' | 'last' | 'only')\n * - Consumed by: Button (for border-radius styling)\n *\n * Both contexts return null when not in a provider, allowing Button\n * to work standalone without any group context.\n *\n * @see plan.md for architecture details\n * @see ButtonGroup.tsx for Provider implementation\n */\n\n// =============================================================================\n// Group-Level Context\n// =============================================================================\n\n/**\n * Context for group-level props (orientation, variant, size, isDisabled)\n * Default value is null to indicate \"not in a group\"\n */\nconst ButtonGroupContext = createContext<ButtonGroupContextValue | null>(null);\n\nButtonGroupContext.displayName = 'ButtonGroupContext';\n\n/**\n * Hook to access group-level context\n * @returns ButtonGroupContextValue if inside a ButtonGroup, null otherwise\n */\nexport function useButtonGroupContext(): ButtonGroupContextValue | null {\n return useContext(ButtonGroupContext);\n}\n\n// =============================================================================\n// Item-Level Context\n// =============================================================================\n\n/**\n * Context for per-button position information\n * Default value is null to indicate \"not wrapped with position context\"\n */\nconst ButtonGroupItemContext =\n createContext<ButtonGroupItemContextValue | null>(null);\n\nButtonGroupItemContext.displayName = 'ButtonGroupItemContext';\n\n/**\n * Hook to access item-level context (position)\n * @returns ButtonGroupItemContextValue if wrapped with position context, null otherwise\n */\nexport function useButtonGroupItemContext(): ButtonGroupItemContextValue | null {\n return useContext(ButtonGroupItemContext);\n}\n\n// =============================================================================\n// Exports\n// =============================================================================\n\nexport { ButtonGroupContext, ButtonGroupItemContext };\n","import { cva } from 'class-variance-authority';\n\n/**\n * ButtonGroup CVA Variants\n *\n * Defines Class Variance Authority (CVA) variants for:\n * - ButtonGroup container (orientation-based layout)\n * - ButtonGroupItem (position-based border-radius)\n * - ButtonGroupSeparator (orientation-based styling)\n *\n * @see plan.md Phase 1: Design & Contracts - CVA Variants\n * @see constitution.md Principle V (Component Quality Standards)\n */\n\n// =============================================================================\n// Container Variants\n// =============================================================================\n\n/**\n * ButtonGroup container variants\n * Controls the layout direction based on orientation\n * Uses gap-0 to ensure buttons are connected (share borders)\n */\nexport const buttonGroupVariants = cva('inline-flex items-center gap-0', {\n variants: {\n orientation: {\n horizontal: 'flex-row',\n vertical: 'flex-col w-full',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n },\n});\n\n// =============================================================================\n// Item Position Variants\n// =============================================================================\n\n/**\n * ButtonGroupItem position variants\n * Applied to Button's visual layer (Layer 2) for position-aware border-radius\n *\n * Compound variants handle both orientation and position combinations:\n * - Horizontal: left/right borders and radii\n * - Vertical: top/bottom borders and radii\n */\nexport const buttonGroupItemVariants = cva('', {\n variants: {\n orientation: {\n // min-w-[44px] ensures visual layer fills touch target width (for icon buttons)\n horizontal: 'min-w-[44px]',\n // flex (overrides inline-flex) + min-h-[44px] makes visual layer fill touch target,\n // eliminating gaps between stacked buttons in vertical orientation\n vertical: 'flex min-h-[44px]',\n },\n position: {\n first: '',\n middle: '',\n last: '',\n only: '', // Single button - no modifications needed\n },\n },\n compoundVariants: [\n // ==========================================================================\n // Horizontal Orientation\n // ==========================================================================\n {\n orientation: 'horizontal',\n position: 'first',\n className: 'rounded-r-none border-r-0',\n },\n {\n orientation: 'horizontal',\n position: 'middle',\n className: 'rounded-none border-r-0',\n },\n {\n orientation: 'horizontal',\n position: 'last',\n className: 'rounded-l-none',\n },\n // ==========================================================================\n // Vertical Orientation\n // Note: w-full is handled by Button's effectiveFullWidth for both layers\n // ==========================================================================\n {\n orientation: 'vertical',\n position: 'first',\n className: 'rounded-b-none border-b-0',\n },\n {\n orientation: 'vertical',\n position: 'middle',\n className: 'rounded-none border-b-0',\n },\n {\n orientation: 'vertical',\n position: 'last',\n className: 'rounded-t-none',\n },\n ],\n defaultVariants: {\n orientation: 'horizontal',\n position: 'only',\n },\n});\n\n// =============================================================================\n// Separator Variants\n// =============================================================================\n\n/**\n * ButtonGroupSeparator variants\n * Orientation-aware visual divider between button groups\n */\nexport const buttonGroupSeparatorVariants = cva('bg-[var(--border)]', {\n variants: {\n orientation: {\n horizontal: 'w-px h-6 mx-1',\n vertical: 'h-px w-full my-1',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n },\n});\n","\"use client\";\n\n/**\n * Button Component - 3-Layer Architecture\n * Accessible button with React Aria primitives and CVA styling\n *\n * Architecture:\n * - Layer 1: Touch Target (44x44px WCAG AAA compliant, transparent)\n * - Layer 2: Visual Button (configurable size, colors, borders)\n * - Layer 3: Content & Effects (text, icons, ripple, loading spinner)\n *\n * @see 3layer-plan.md for architecture details\n * @see spec.md FR-029 to FR-037 (Button Component Requirements)\n * @see spec.md FR-009 (WCAG 2.2 AAA - 7:1 contrast ratio)\n * @see spec.md FR-014 (44x44px minimum touch targets)\n * @see constitution.md Principle IV (Accessibility First)\n */\n\nimport { forwardRef, memo, useId } from 'react';\nimport {\n Button as AriaButton,\n type ButtonProps as AriaButtonProps,\n} from 'react-aria-components';\nimport { Loader2, Zap } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport type { ButtonProps } from './Button.types';\nimport { buttonOuterVariants, buttonVisualVariants, buttonVariants } from './Button.styles';\nimport { PRESSED_STYLES, HOVER_STYLES, HIGH_CONTRAST_HOVER, HIGH_CONTRAST_PRESSED } from '../../styles/interaction-states';\nimport {\n useButtonGroupContext,\n useButtonGroupItemContext,\n} from '../ButtonGroup/ButtonGroupContext';\nimport { buttonGroupItemVariants } from '../ButtonGroup/ButtonGroup.variants';\n\n/**\n * Button Component - 3-Layer Architecture\n * Fully accessible button with React Aria and themed styling\n *\n * Layer 1: Touch Target (AriaButton) - 44x44px WCAG AAA compliant\n * Layer 2: Visual Button (span) - configurable appearance\n * Layer 3: Content (children) - text, icons, effects\n */\nconst Button = memo(forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n buttonVisualClassName,\n variant,\n size,\n visualSize,\n fullWidth,\n loading = false,\n loadingText = \"Loading...\",\n shortcut,\n children,\n isDisabled,\n paywall = false,\n paywallRedirect,\n paywallDescription,\n onPress,\n ...props\n },\n ref\n ) => {\n const paywallDescriptionId = useId();\n\n // ==========================================================================\n // ButtonGroup Context Integration\n // ==========================================================================\n\n // Consume group-level context (variant, size, isDisabled, orientation)\n const groupContext = useButtonGroupContext();\n\n // Consume item-level context (position for border-radius styling)\n const itemContext = useButtonGroupItemContext();\n\n // Merge context values with props (props take precedence)\n const effectiveVariant = variant ?? groupContext?.variant ?? 'default';\n const effectiveSize = size ?? groupContext?.size;\n const effectiveIsDisabled = isDisabled ?? groupContext?.isDisabled ?? false;\n\n // In vertical groups, buttons should be full width automatically\n const isInVerticalGroup = groupContext?.orientation === 'vertical';\n const effectiveFullWidth = fullWidth || isInVerticalGroup;\n\n // Position styling for ButtonGroup (only applied when in a group)\n const positionClassName = itemContext\n ? buttonGroupItemVariants({\n orientation: groupContext?.orientation ?? 'horizontal',\n position: itemContext.position,\n })\n : '';\n\n // Default visualSize to size for backward compatibility\n const effectiveVisualSize = visualSize ?? effectiveSize ?? 'default';\n\n // AAA Accessibility: Warn in dev/test if icon/dot variant lacks accessible name\n if (process.env.NODE_ENV !== 'production') {\n if (\n (effectiveVisualSize === 'dot' || effectiveVisualSize === 'icon') &&\n !props['aria-label'] &&\n !children\n ) {\n console.warn(\n '[Button] visualSize=\"dot\" or \"icon\" requires aria-label when no visible text is provided (WCAG 1.1.1)'\n );\n }\n }\n\n /**\n * Handle button press - intercepts action when paywalled\n * If paywalled with redirect URL, opens in new tab\n * Otherwise, calls the normal onPress handler\n */\n const handlePress = (e: Parameters<NonNullable<AriaButtonProps['onPress']>>[0]): void => {\n if (paywall) {\n if (paywallRedirect) {\n window.open(paywallRedirect, '_blank', 'noopener,noreferrer');\n }\n // Don't call onPress when paywalled\n return;\n }\n onPress?.(e);\n };\n\n // Only set isDisabled when we have a reason to disable\n // Otherwise, let slot system control disabled state (e.g., in NumberField)\n const computedIsDisabled = effectiveIsDisabled || loading || undefined;\n\n return (\n <AriaButton\n ref={ref}\n isDisabled={computedIsDisabled}\n aria-disabled={paywall ? true : undefined}\n aria-describedby={paywall ? paywallDescriptionId : undefined}\n onPress={handlePress}\n className={cn(buttonOuterVariants({ fullWidth: effectiveFullWidth, inVerticalGroup: isInVerticalGroup }), className)}\n {...props}\n >\n {(renderProps) => (\n /* Layer 2: Visual Button */\n <span\n className={cn(\n buttonVisualVariants({\n variant: effectiveVariant,\n visualSize: effectiveVisualSize,\n paywall,\n fullWidth: effectiveFullWidth,\n }),\n // Position styling from ButtonGroup context (border-radius adjustments)\n positionClassName,\n buttonVisualClassName,\n // Layer 2 interaction styles (no focus - focus ring is on Layer 1)\n PRESSED_STYLES,\n HOVER_STYLES,\n HIGH_CONTRAST_HOVER,\n HIGH_CONTRAST_PRESSED\n )}\n data-pressed={renderProps.isPressed || undefined}\n >\n {/* Layer 3: Content & Effects */}\n\n {/* FR-033: Loading spinner with screen reader announcement */}\n {/* Uses motion-safe: for WCAG 2.3.3 AAA (Animation from Interactions) */}\n {loading && (\n <>\n <Loader2 className=\"motion-safe:animate-spin\" aria-hidden=\"true\" />\n <span className=\"sr-only\" aria-live=\"polite\">\n {loadingText}\n </span>\n </>\n )}\n\n {/* Hide children during loading */}\n {!loading && children}\n\n {/* Paywall: Lightning bolt icon */}\n {paywall && (\n <Zap\n data-testid=\"zap-icon\"\n aria-hidden=\"true\"\n className=\"ml-1\"\n />\n )}\n\n {/* Paywall: Screen reader description */}\n {paywall && (\n <span id={paywallDescriptionId} className=\"sr-only\">\n Premium feature: {paywallDescription || \"Upgrade required to access this feature\"}\n </span>\n )}\n\n {/* FR-034: Keyboard shortcut display on focus */}\n {renderProps.isFocusVisible && shortcut && (\n <kbd className=\"ml-auto hidden text-xs opacity-60 lg:inline\">\n {shortcut}\n </kbd>\n )}\n\n {/* Touch/press ripple effect - FR-031: Pressed state feedback */}\n {/* Uses motion-safe: for WCAG 2.3.3 AAA (Animation from Interactions) */}\n {renderProps.isPressed && (\n <span\n className=\"absolute inset-0 rounded-[inherit] bg-current opacity-10 motion-safe:animate-in motion-safe:zoom-in-95\"\n aria-hidden=\"true\"\n />\n )}\n </span>\n )}\n </AriaButton>\n );\n }\n));\n\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants, buttonOuterVariants, buttonVisualVariants };\nexport type { ButtonProps } from './Button.types';\n","'use client';\n\n/**\n * Toast Component - Accessible notification system\n * Built with React Aria toast primitives and CVA styling\n *\n * @see toast-prd.md - Product Requirements Document\n * @see plan.md - Implementation Plan\n * @see constitution.md Principle IV (Accessibility First - WCAG 2.2 AAA)\n */\n\nimport {\n forwardRef,\n memo,\n useRef,\n useState,\n useCallback,\n type TouchEvent as ReactTouchEvent,\n} from 'react';\nimport { useToast } from 'react-aria';\nimport { ToastQueue, type QueuedToast, type ToastState as AriaToastState } from 'react-stately';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { X } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { Button } from '../Button';\nimport type {\n ToastVariant,\n ToastContent,\n ToastState,\n DismissReason,\n ToastConfig,\n} from './Toast.types';\n\n// =============================================================================\n// CVA Variants\n// =============================================================================\n\n/**\n * Toast container variants with theme-aligned styling\n * Fixed 356px width for consistent appearance across all toasts\n */\nexport const toastVariants = cva(\n [\n // Fixed width for consistent appearance\n 'pointer-events-auto relative flex w-[356px] items-center justify-between',\n 'space-x-4 overflow-hidden rounded-lg border p-4 pr-12 shadow-lg',\n // Animations with reduced-motion support\n 'transition-all duration-300',\n 'motion-reduce:transition-none motion-reduce:animate-none',\n 'data-[entering]:animate-in data-[entering]:slide-in-from-left',\n 'data-[exiting]:animate-out data-[exiting]:slide-out-to-right-full',\n 'data-[exiting]:fade-out-80',\n // Touch interaction for swipe\n 'touch-pan-y',\n ],\n {\n variants: {\n variant: {\n default: [\n 'bg-[var(--content-background)]',\n 'text-[var(--content-foreground)]',\n 'border-[var(--border)]',\n ],\n primary: [\n 'bg-[var(--primary)]',\n 'text-[var(--primary-foreground)]',\n 'border-[var(--primary)]',\n ],\n destructive: [\n 'bg-[var(--destructive-background)]',\n 'text-[var(--destructive-foreground)]',\n 'border-[var(--destructive-background)]',\n ],\n accent: [\n 'bg-[var(--accent-background)]',\n 'text-[var(--accent-foreground)]',\n 'border-[var(--accent-background)]',\n ],\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n }\n);\n\n/**\n * Toaster position variants for the toast region container\n */\nexport const toasterVariants = cva(\n [\n 'fixed z-50 flex flex-col gap-2 p-4',\n 'pointer-events-none',\n 'max-h-screen overflow-hidden',\n ],\n {\n variants: {\n position: {\n 'top-left': 'top-0 left-0 items-start',\n 'top-center': 'top-0 left-1/2 -translate-x-1/2 items-center',\n 'top-right': 'top-0 right-0 items-end',\n 'bottom-left': 'bottom-0 left-0 items-start',\n 'bottom-center': 'bottom-0 left-1/2 -translate-x-1/2 items-center',\n 'bottom-right': 'bottom-0 right-0 items-end',\n },\n },\n defaultVariants: {\n position: 'bottom-right',\n },\n }\n);\n\n// =============================================================================\n// Close Button Component\n// =============================================================================\n\ninterface CloseButtonProps {\n variant?: ToastVariant;\n className?: string;\n onPress?: () => void;\n}\n\n/**\n * Close button component with 44x44px touch target\n * WCAG 2.2 AAA compliant touch target size\n */\nconst CloseButton = memo(\n forwardRef<HTMLButtonElement, CloseButtonProps>(\n ({ variant = 'default', className, onPress}, ref) => {\n // Variant-aware hover styles using theme tokens\n const hoverStyles =\n variant === 'default'\n ? 'hover:bg-[var(--accent-background)]/20'\n : 'hover:bg-[var(--page-background)]/20';\n\n return (\n <Button\n ref={ref}\n variant=\"ghost\"\n visualSize=\"icon\"\n onPress={onPress}\n className={cn(\n 'absolute right-2 top-2',\n hoverStyles,\n className\n )}\n aria-label=\"Dismiss notification\"\n >\n <X className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n );\n }\n )\n);\n\nCloseButton.displayName = 'CloseButton';\n\n// =============================================================================\n// Global Toast Queue\n// =============================================================================\n\n/**\n * Internal toast queue instance\n */\nconst internalQueue = new ToastQueue<ToastState>({\n maxVisibleToasts: 3,\n});\n\n/**\n * Track total active toasts (visible + queued)\n * React Stately doesn't expose queued toasts, so we track manually\n */\nlet totalActiveToasts = 0;\nconst toastCountListeners = new Set<() => void>();\n\nfunction incrementTotalToasts(): void {\n totalActiveToasts++;\n toastCountListeners.forEach((fn) => fn());\n}\n\nfunction decrementTotalToasts(): void {\n totalActiveToasts = Math.max(0, totalActiveToasts - 1);\n toastCountListeners.forEach((fn) => fn());\n}\n\n/**\n * Get the total number of active toasts (visible + queued)\n */\nexport function getTotalActiveToasts(): number {\n return totalActiveToasts;\n}\n\n/**\n * Subscribe to total toast count changes\n */\nexport function subscribeToTotalCount(fn: () => void): () => void {\n toastCountListeners.add(fn);\n return () => toastCountListeners.delete(fn);\n}\n\n/**\n * Enhanced toast queue API with additional methods\n * Provides closeAll, pauseAll, resumeAll on top of base ToastQueue\n */\nexport const toastQueue = {\n /**\n * Add a toast to the queue\n */\n add: (content: ToastState, options?: { timeout?: number; onClose?: () => void }) =>\n internalQueue.add(content, options),\n\n /**\n * Close a specific toast by key\n */\n close: (key: string) => {\n internalQueue.close(key);\n },\n\n /**\n * Close all toasts (visible and queued)\n */\n closeAll: () => {\n // Close all visible toasts\n internalQueue.visibleToasts.forEach((toast) => {\n internalQueue.close(toast.key);\n });\n },\n\n /**\n * Pause all toast timers\n */\n pauseAll: () => {\n internalQueue.pauseAll();\n },\n\n /**\n * Resume all toast timers\n */\n resumeAll: () => {\n internalQueue.resumeAll();\n },\n\n /**\n * Get the visible toasts\n */\n get visibleToasts() {\n return internalQueue.visibleToasts;\n },\n\n /**\n * Subscribe to queue changes\n */\n subscribe: (fn: () => void) => internalQueue.subscribe(fn),\n\n /**\n * Get total active toasts (visible + queued)\n */\n get totalCount() {\n return totalActiveToasts;\n },\n};\n\n/**\n * Get the internal queue for use in components\n */\nexport function getInternalQueue(): ToastQueue<ToastState> {\n return internalQueue;\n}\n\n// =============================================================================\n// Toast Function (Programmatic API)\n// =============================================================================\n\n/**\n * Enforce minimum timeout of 5000ms for accessibility\n * WCAG 2.2 timing guidelines require sufficient time for reading\n * Timeout of 0 bypasses this (persistent toast)\n */\nfunction enforceMinTimeout(timeout: number): number {\n if (timeout === 0) return 0; // Persistent toast\n return Math.max(timeout, 5000);\n}\n\n/**\n * Add a toast notification programmatically\n *\n * @param content - Content to display (string or ReactNode)\n * @param options - Toast configuration options\n * @returns Unique key for the created toast\n *\n * @example\n * ```tsx\n * // Simple string\n * toast(\"Message saved!\");\n *\n * // With variant\n * toast(\"Error occurred\", { variant: \"destructive\" });\n *\n * // With callback\n * toast(\"Action completed\", {\n * variant: \"primary\",\n * onClose: (key, reason) => console.log(`Closed: ${reason}`)\n * });\n * ```\n */\n/**\n * Apply defaults to toast options without Zod runtime overhead.\n */\nfunction applyToastDefaults(options: ToastConfig = {}): { variant: ToastVariant; timeout: number; onClose?: (key: string, reason: DismissReason) => void } {\n return {\n variant: options.variant ?? 'default',\n timeout: options.timeout ?? 5000,\n onClose: options.onClose,\n };\n}\n\nexport function toast(content: ToastContent, options?: ToastConfig): string {\n // Apply defaults (TypeScript handles validation at compile time)\n const validated = applyToastDefaults(options);\n\n // Enforce minimum timeout for accessibility\n const timeout = enforceMinTimeout(validated.timeout);\n\n // Track dismissal reason for callback\n const dismissReason: DismissReason = 'timeout';\n\n // Increment total toast count\n incrementTotalToasts();\n\n const key = toastQueue.add(\n {\n key: '', // Will be set by queue\n content,\n variant: validated.variant,\n timeout,\n onClose: validated.onClose,\n },\n {\n timeout,\n onClose: () => {\n // Decrement total toast count\n decrementTotalToasts();\n\n // Fire the user's onClose callback with reason\n if (validated.onClose) {\n validated.onClose(key, dismissReason);\n }\n },\n }\n );\n\n return key;\n}\n\n// =============================================================================\n// Toast Item Component\n// =============================================================================\n\nexport interface ToastItemProps {\n /** Toast from the queue */\n toast: QueuedToast<ToastState>;\n /** Toast state for managing dismissal */\n state: AriaToastState<ToastState>;\n}\n\n/**\n * Individual toast notification component\n * Uses React Aria useToast hook for accessibility\n */\nexport const ToastItem = memo(\n forwardRef<HTMLDivElement, ToastItemProps>(({ toast, state }, forwardedRef) => {\n const internalRef = useRef<HTMLDivElement>(null);\n const ref = (forwardedRef as React.RefObject<HTMLDivElement>) || internalRef;\n\n // React Aria toast hook for accessibility\n const { toastProps, contentProps } = useToast(\n { toast },\n state,\n ref\n );\n\n // Swipe-to-dismiss state\n const [touchStart, setTouchStart] = useState<number | null>(null);\n const [translateX, setTranslateX] = useState(0);\n\n const SWIPE_THRESHOLD = 50; // Minimum swipe distance to dismiss\n\n const handleTouchStart = useCallback((e: ReactTouchEvent) => {\n const touch = e.touches[0];\n if (touch) {\n setTouchStart(touch.clientX);\n }\n }, []);\n\n const handleTouchMove = useCallback(\n (e: ReactTouchEvent) => {\n if (touchStart === null) return;\n const touch = e.touches[0];\n if (!touch) return;\n const diff = touch.clientX - touchStart;\n // Only allow right swipe (positive diff)\n if (diff > 0) {\n setTranslateX(diff);\n }\n },\n [touchStart]\n );\n\n const handleTouchEnd = useCallback(() => {\n if (translateX > SWIPE_THRESHOLD) {\n // Dismiss the toast\n state.close(toast.key);\n }\n // Reset swipe state\n setTranslateX(0);\n setTouchStart(null);\n }, [translateX, state, toast.key]);\n\n const handleCloseClick = useCallback(() => {\n state.close(toast.key);\n }, [state, toast.key]);\n\n const { content } = toast;\n const variant = content.variant ?? 'default';\n\n // Generate accessible name for the toast\n const accessibleName = typeof content.content === 'string'\n ? content.content\n : 'Notification';\n\n return (\n <div\n {...toastProps}\n ref={ref}\n className={cn(toastVariants({ variant }))}\n aria-label={accessibleName}\n style={{\n transform: translateX > 0 ? `translateX(${translateX}px)` : undefined,\n opacity: translateX > 0 ? 1 - translateX / 200 : undefined,\n }}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleTouchEnd}\n >\n {/* Toast content */}\n <div {...contentProps} className=\"flex-1 min-w-0\">\n {typeof content.content === 'string' ? (\n <p className=\"text-sm font-medium\">{content.content}</p>\n ) : (\n content.content\n )}\n </div>\n\n {/* Close button */}\n <CloseButton\n variant={variant}\n onPress={handleCloseClick}\n />\n </div>\n );\n })\n);\n\nToastItem.displayName = 'ToastItem';\n\n// =============================================================================\n// Export Types\n// =============================================================================\n\nexport type { VariantProps };\n","import { z } from 'zod';\nimport type { ReactNode } from 'react';\n\n/**\n * Toast Component Type Definitions\n *\n * @see toast-prd.md - Product Requirements Document\n * @see plan.md - Implementation Plan\n * @see constitution.md Principle IV (Accessibility First - WCAG 2.2 AAA)\n */\n\n// =============================================================================\n// Enums and Primitives\n// =============================================================================\n\n/**\n * Reason why a toast was dismissed\n * - 'timeout': Auto-dismissed after timeout period\n * - 'user': User clicked close button or swiped to dismiss\n * - 'programmatic': Closed via toastQueue.close() or toastQueue.closeAll()\n */\nexport const DismissReasonSchema = z.enum(['timeout', 'user', 'programmatic']);\nexport type DismissReason = z.infer<typeof DismissReasonSchema>;\n\n/**\n * Toast visual variants matching the Themis theme system\n * Each variant maps to specific theme tokens for background/foreground colors\n *\n * @see toast-prd.md Variant Token Mapping table\n */\nexport const ToastVariantSchema = z.enum([\n 'default',\n 'primary',\n 'destructive',\n 'accent',\n]);\nexport type ToastVariant = z.infer<typeof ToastVariantSchema>;\n\n/**\n * Toast region positioning options\n * Toasts appear in a fixed position on the viewport\n */\nexport const ToasterPositionSchema = z.enum([\n 'top-left',\n 'top-center',\n 'top-right',\n 'bottom-left',\n 'bottom-center',\n 'bottom-right',\n]);\nexport type ToasterPosition = z.infer<typeof ToasterPositionSchema>;\n\n// =============================================================================\n// Toast Options Schema\n// =============================================================================\n\n/**\n * Options for creating a toast notification\n *\n * @example\n * ```tsx\n * toast(\"Message\", {\n * variant: \"primary\",\n * timeout: 8000,\n * onClose: (key, reason) => console.log(`Toast ${key} closed: ${reason}`)\n * });\n * ```\n */\nexport const ToastOptionsSchema = z.object({\n /**\n * Visual variant determining background and foreground colors\n * @default 'default'\n */\n variant: ToastVariantSchema.default('default'),\n\n /**\n * Auto-dismiss timeout in milliseconds\n * - Values 1-4999ms are clamped to 5000ms (WCAG 2.2 timing requirement)\n * - Set to 0 for persistent toasts (no auto-dismiss)\n * @default 5000\n */\n timeout: z.number().min(0).default(5000),\n\n /**\n * Callback fired when toast is dismissed\n * @param key - Unique identifier of the toast\n * @param reason - Why the toast was dismissed ('timeout' | 'user' | 'programmatic')\n */\n onClose: z.function().optional(),\n});\n\nexport interface ToastOptions {\n variant?: ToastVariant;\n timeout?: number;\n onClose?: (key: string, reason: DismissReason) => void;\n}\n\n// =============================================================================\n// Provider Props Schema\n// =============================================================================\n\n/**\n * Props for the ToastProvider component\n * Wraps the application to provide toast queue context\n *\n * @example\n * ```tsx\n * <ToastProvider maxVisibleToasts={3}>\n * <App />\n * <Toaster position=\"bottom-right\" />\n * </ToastProvider>\n * ```\n */\nexport const ToastProviderPropsSchema = z.object({\n /**\n * Maximum number of toasts visible at once\n * Additional toasts are queued and shown as others dismiss\n * @default 3\n */\n maxVisibleToasts: z.number().min(1).default(3),\n});\n\nexport interface ToastProviderProps {\n maxVisibleToasts?: number;\n children: ReactNode;\n}\n\n// =============================================================================\n// Toaster Props Schema\n// =============================================================================\n\n/**\n * Props for the Toaster component (toast region container)\n * Renders the toast region with positioning and accessibility attributes\n *\n * @example\n * ```tsx\n * <Toaster\n * position=\"bottom-right\"\n * aria-label=\"Notifications\"\n * className=\"custom-toast-region\"\n * />\n * ```\n */\nexport const ToasterPropsSchema = z.object({\n /**\n * Position of the toast region on screen\n * @default 'bottom-right'\n */\n position: ToasterPositionSchema.default('bottom-right'),\n\n /**\n * Accessible label for the toast region landmark\n * Used by screen readers to identify the notification area\n * @default 'Notifications'\n */\n 'aria-label': z.string().default('Notifications'),\n\n /**\n * Additional CSS classes for the toast region\n */\n className: z.string().optional(),\n});\n\nexport interface ToasterProps {\n position?: ToasterPosition;\n 'aria-label'?: string;\n className?: string;\n}\n\n// =============================================================================\n// Internal Types (not exported from index.ts)\n// =============================================================================\n\n/**\n * Internal toast state stored in the queue\n * Contains all information needed to render and manage a toast\n */\nexport interface ToastState {\n /** Unique identifier for the toast */\n key: string;\n\n /** Content to render inside the toast (ReactNode or string) */\n content: ReactNode;\n\n /** Visual variant for styling */\n variant: ToastVariant;\n\n /** Auto-dismiss timeout in milliseconds (0 = persistent) */\n timeout: number;\n\n /** Callback fired when toast is dismissed */\n onClose?: (key: string, reason: DismissReason) => void;\n}\n\n/**\n * Toast content type - fully customizable by developer\n * Can be a simple string or complex ReactNode with custom layout\n */\nexport type ToastContent = ReactNode;\n\n/**\n * Props for the internal ToastItem component\n */\nexport interface ToastItemProps {\n /** Toast state from the queue */\n toast: {\n key: string;\n content: ToastState;\n animation: 'entering' | 'queued' | 'exiting';\n priority: number;\n };\n\n /** Toast queue state for managing dismissal */\n state: {\n close: (key: string) => void;\n };\n}\n\n// =============================================================================\n// Toast Queue API Types\n// =============================================================================\n\n/**\n * Public API for the toast queue\n * Allows programmatic control of toasts from anywhere in the app\n */\nexport interface ToastQueueAPI {\n /**\n * Add a new toast to the queue\n * @param content - Content to display in the toast\n * @param options - Toast configuration options\n * @returns Unique key for the created toast\n */\n add: (content: ToastContent, options?: Partial<ToastOptions>) => string;\n\n /**\n * Close a specific toast by key\n * @param key - Unique identifier of the toast to close\n */\n close: (key: string) => void;\n\n /**\n * Close all toasts (visible and queued)\n */\n closeAll: () => void;\n\n /**\n * Pause all toast timers\n * Useful when user is interacting with the toast region\n */\n pauseAll: () => void;\n\n /**\n * Resume all paused toast timers\n */\n resumeAll: () => void;\n}\n\n// =============================================================================\n// Utility Types\n// =============================================================================\n\n/**\n * Configuration for the toast() function\n * Partial version of ToastOptions for convenience\n */\nexport type ToastConfig = Partial<ToastOptions>;\n\n/**\n * Return type of the toast() function\n */\nexport type ToastKey = string;\n","'use client';\n\n/**\n * ToastProvider Component\n * Provides toast queue context for the application\n *\n * @see toast-prd.md - Product Requirements Document\n * @see plan.md - Implementation Plan\n */\n\nimport { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport { ToastQueue } from 'react-stately';\nimport type { ToastState, ToastProviderProps } from './Toast.types';\nimport { ToastProviderPropsSchema } from './Toast.types';\nimport { getInternalQueue } from './Toast';\n\n// =============================================================================\n// Context\n// =============================================================================\n\n/**\n * Context for the toast queue\n * Allows child components to access the toast queue state\n */\nexport const ToastContext = createContext<ToastQueue<ToastState> | null>(null);\n\n/**\n * Hook to access the toast queue from context\n * @returns The toast queue instance\n * @throws Error if used outside ToastProvider\n */\nexport function useToastQueue(): ToastQueue<ToastState> {\n const context = useContext(ToastContext);\n if (!context) {\n throw new Error('useToastQueue must be used within a ToastProvider');\n }\n return context;\n}\n\n// =============================================================================\n// Provider Component\n// =============================================================================\n\n/**\n * ToastProvider - Wraps application to provide toast functionality\n *\n * @example\n * ```tsx\n * <ToastProvider maxVisibleToasts={3}>\n * <App />\n * <Toaster position=\"bottom-right\" />\n * </ToastProvider>\n * ```\n */\nexport function ToastProvider({\n children,\n maxVisibleToasts = 3,\n}: ToastProviderProps): ReactNode {\n // Validate props with Zod schema (throws on invalid input)\n ToastProviderPropsSchema.parse({ maxVisibleToasts });\n\n // Create queue with configured maxVisibleToasts\n // Note: We use the global toastQueue for now for simplicity\n // In a more complex setup, we could create a new queue per provider\n const queue = useMemo(() => {\n // Get the internal queue instance\n // Note: maxVisibleToasts is set on the global queue, not per-provider\n return getInternalQueue();\n }, []);\n\n return (\n <ToastContext.Provider value={queue}>{children}</ToastContext.Provider>\n );\n}\n\nToastProvider.displayName = 'ToastProvider';\n","'use client';\n\n/**\n * Toaster Component - Toast region container\n * Renders visible toasts with positioning and accessibility attributes\n *\n * @see toast-prd.md - Product Requirements Document\n * @see plan.md - Implementation Plan\n * @see constitution.md Principle IV (Accessibility First - WCAG 2.2 AAA)\n */\n\nimport { forwardRef, useRef, memo, useCallback, useSyncExternalStore, type ReactElement } from 'react';\nimport { useToastRegion } from 'react-aria';\nimport { useToastQueue as useAriaToastQueue } from 'react-stately';\nimport { cn } from '../../utils/cn';\nimport { Button } from '../Button';\nimport { toasterVariants, getInternalQueue, ToastItem, getTotalActiveToasts, subscribeToTotalCount } from './Toast';\nimport type { ToasterProps } from './Toast.types';\nimport { ToasterPropsSchema } from './Toast.types';\n\n// =============================================================================\n// Stacked Toast Visual Effect\n// =============================================================================\n\ninterface StackedToastIndicatorProps {\n queuedCount: number;\n}\n\n/**\n * Visual stacking effect showing ghost toasts behind visible ones\n * Creates depth perception for queued notifications\n */\nfunction StackedToastIndicator({ queuedCount }: StackedToastIndicatorProps): ReactElement | null {\n if (queuedCount <= 0) return null;\n\n // Show up to 2 visual stack layers\n const stackLayers = Math.min(queuedCount, 2);\n\n return (\n <div className=\"relative w-[356px] pointer-events-none\" aria-hidden=\"true\">\n {/* Stack layer 1 (closest to visible toasts) */}\n <div\n className={cn(\n 'absolute left-1/2 -translate-x-1/2 w-[340px] h-4 rounded-lg shadow-lg',\n 'bg-[var(--content-background)] border border-[var(--border)]',\n 'opacity-60',\n 'transition-all duration-200'\n )}\n style={{ top: 0 }}\n />\n\n {/* Stack layer 2 (further back, if 2+ queued) */}\n {stackLayers >= 2 && (\n <div\n className={cn(\n 'absolute left-1/2 -translate-x-1/2 w-[324px] h-2 rounded-b-lg shadow-lg',\n 'bg-[var(--content-background)] border border-t-0 border-[var(--border)]',\n 'opacity-40',\n 'transition-all duration-200'\n )}\n style={{ top: 14 }}\n />\n )}\n\n {/* Spacer to account for stack height */}\n <div className=\"h-4\" />\n\n {/* Text indicator */}\n <div\n className=\"text-sm text-[var(--content-foreground)] opacity-70 text-center py-2 pointer-events-auto\"\n aria-live=\"polite\"\n >\n +{queuedCount} more notification{queuedCount > 1 ? 's' : ''}\n </div>\n </div>\n );\n}\n\n// =============================================================================\n// Toaster Component\n// =============================================================================\n\n/**\n * Toaster - Toast region container\n * Renders the toast notification area with proper ARIA attributes\n *\n * @example\n * ```tsx\n * <ToastProvider>\n * <App />\n * <Toaster position=\"bottom-right\" aria-label=\"Notifications\" />\n * </ToastProvider>\n * ```\n */\nexport const Toaster = memo(\n forwardRef<HTMLDivElement, ToasterProps>((props, forwardedRef) => {\n // Validate props with Zod schema\n const validated = ToasterPropsSchema.parse(props);\n const {\n position = 'bottom-right',\n 'aria-label': ariaLabel = 'Notifications',\n className,\n } = validated;\n\n const internalRef = useRef<HTMLDivElement>(null);\n const ref = (forwardedRef as React.RefObject<HTMLDivElement>) || internalRef;\n\n // Use the global toast queue state\n const internalQueueRef = getInternalQueue();\n const state = useAriaToastQueue(internalQueueRef);\n\n // Subscribe to total toast count (includes queued toasts)\n const totalCount = useSyncExternalStore(\n subscribeToTotalCount,\n getTotalActiveToasts,\n getTotalActiveToasts\n );\n\n // React Aria toast region hook for accessibility\n const { regionProps } = useToastRegion(\n { 'aria-label': ariaLabel },\n state,\n ref\n );\n\n // Calculate queued toast count for collapsed stack indicator\n // totalCount = all toasts (visible + queued), visibleToasts.length = currently shown\n const queuedCount = Math.max(0, totalCount - state.visibleToasts.length);\n\n // Handle \"Clear All\" button click\n const handleClearAll = useCallback(() => {\n // Close all visible toasts\n state.visibleToasts.forEach((toast) => {\n state.close(toast.key);\n });\n }, [state]);\n\n // Don't render if no toasts\n if (state.visibleToasts.length === 0) {\n return null;\n }\n\n return (\n <div\n {...regionProps}\n ref={ref}\n className={cn(toasterVariants({ position }), className)}\n >\n {/* Toast list */}\n {state.visibleToasts.map((toast) => (\n <ToastItem key={toast.key} toast={toast} state={state} />\n ))}\n\n {/* Visual stacking effect with ghost toasts */}\n <StackedToastIndicator queuedCount={queuedCount} />\n\n {/* \"Clear All\" button (when 2+ toasts visible) */}\n {state.visibleToasts.length >= 2 && (\n <Button\n variant=\"secondary\"\n visualSize=\"sm\"\n onPress={handleClearAll}\n aria-label=\"Clear all notifications\"\n className=\"mt-2 pointer-events-auto\"\n >\n Clear All\n </Button>\n )}\n </div>\n );\n })\n);\n\nToaster.displayName = 'Toaster';\n"]}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {createContext,memo,forwardRef,useId,useRef,useState,useCallback,useSyncExternalStore,useContext,useMemo}from'react';import {useToast,useToastRegion}from'react-aria';import {ToastQueue,useToastQueue}from'react-stately';import {cva}from'class-variance-authority';import {Loader2,Zap,X as X$1}from'lucide-react';import {clsx}from'clsx';import {twMerge}from'tailwind-merge';import {Button}from'react-aria-components';import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {z as z$1}from'zod';function i(...t){return twMerge(clsx(t))}var z=cva("inline-flex justify-center min-h-[44px] min-w-[44px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50",{variants:{fullWidth:{true:"w-full",false:""},inVerticalGroup:{true:"items-stretch",false:"items-center"}},defaultVariants:{fullWidth:false,inVerticalGroup:false}}),G=cva("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 relative cursor-pointer",{variants:{variant:{default:"bg-[var(--primary-action)] text-[var(--primary-action-foreground)] shadow-md hover:bg-[var(--primary-action-hover)] data-[pressed]:bg-[var(--primary-action)]/80",destructive:"bg-[var(--destructive-background)] text-[var(--destructive-foreground)] shadow-md hover:bg-[var(--destructive-background)]/90 data-[pressed]:bg-[var(--destructive-background)]/80",outline:"border border-[var(--input-border)] bg-[var(--page-background)] hover:bg-[var(--input-border)] data-[pressed]:bg-[var(--input-border)]",secondary:"bg-[var(--secondary)] text-[var(--secondary-foreground)] shadow-md hover:bg-[var(--secondary)]/80 data-[pressed]:bg-[var(--secondary)]/70",ghost:"hover:bg-[var(--accent)] hover:text-[var(--accent-foreground)] data-[pressed]:bg-[var(--accent)]",link:"text-[var(--text-link)] underline-offset-4 hover:underline data-[pressed]:text-[var(--text-link-hover)]"},fullWidth:{true:"w-full",false:""},visualSize:{default:"h-10 px-4 py-2",sm:"h-9 rounded-md px-3 text-xs",lg:"h-11 rounded-md px-8",icon:"h-10 w-10",dot:"h-5 w-5 rounded-full p-0 min-h-0 min-w-0"},paywall:{true:"!bg-[var(--paywall)] !text-[var(--paywall-foreground)] !shadow-md hover:!bg-[var(--paywall)]/90 !cursor-not-allowed !border-transparent",false:""}},defaultVariants:{variant:"default",visualSize:"default",paywall:false}});var H="data-[pressed]:scale-[0.97]";var L="data-[hovered]:shadow-md";var w="hc:data-[hovered]:outline hc:data-[hovered]:outline-2 hc:data-[hovered]:outline-foreground",I="hc:data-[pressed]:outline hc:data-[pressed]:outline-2 hc:data-[pressed]:outline-offset-1 hc:data-[pressed]:outline-foreground";var ot=createContext(null);ot.displayName="ButtonGroupContext";function at(){return useContext(ot)}var rt=createContext(null);rt.displayName="ButtonGroupItemContext";function nt(){return useContext(rt)}cva("inline-flex items-center gap-0",{variants:{orientation:{horizontal:"flex-row",vertical:"flex-col w-full"}},defaultVariants:{orientation:"horizontal"}});var st=cva("",{variants:{orientation:{horizontal:"min-w-[44px]",vertical:"flex min-h-[44px]"},position:{first:"",middle:"",last:"",only:""}},compoundVariants:[{orientation:"horizontal",position:"first",className:"rounded-r-none border-r-0"},{orientation:"horizontal",position:"middle",className:"rounded-none border-r-0"},{orientation:"horizontal",position:"last",className:"rounded-l-none"},{orientation:"vertical",position:"first",className:"rounded-b-none border-b-0"},{orientation:"vertical",position:"middle",className:"rounded-none border-b-0"},{orientation:"vertical",position:"last",className:"rounded-t-none"}],defaultVariants:{orientation:"horizontal",position:"only"}});cva("bg-[var(--border)]",{variants:{orientation:{horizontal:"w-px h-6 mx-1",vertical:"h-px w-full my-1"}},defaultVariants:{orientation:"horizontal"}});var S=memo(forwardRef(({className:t,buttonVisualClassName:e,variant:a,size:n,visualSize:u,fullWidth:c,loading:f=false,loadingText:d="Loading...",shortcut:v,children:o,isDisabled:h,paywall:s=false,paywallRedirect:x,paywallDescription:P,onPress:p,...B},m)=>{let N=useId(),T=at(),g=nt(),b=a??T?.variant??"default",k=n??T?.size,xt=h??T?.isDisabled??false,q=T?.orientation==="vertical",K=c||q,gt=g?st({orientation:T?.orientation??"horizontal",position:g.position}):"",_=u??k??"default";return process.env.NODE_ENV!=="production"&&(_==="dot"||_==="icon")&&!B["aria-label"]&&!o&&console.warn('[Button] visualSize="dot" or "icon" requires aria-label when no visible text is provided (WCAG 1.1.1)'),jsx(Button,{ref:m,isDisabled:xt||f||void 0,"aria-disabled":s?true:void 0,"aria-describedby":s?N:void 0,onPress:V=>{if(s){x&&window.open(x,"_blank","noopener,noreferrer");return}p?.(V);},className:i(z({fullWidth:K,inVerticalGroup:q}),t),...B,children:V=>jsxs("span",{className:i(G({variant:b,visualSize:_,paywall:s,fullWidth:K}),gt,e,H,L,w,I),"data-pressed":V.isPressed||void 0,children:[f&&jsxs(Fragment,{children:[jsx(Loader2,{className:"motion-safe:animate-spin","aria-hidden":"true"}),jsx("span",{className:"sr-only","aria-live":"polite",children:d})]}),!f&&o,s&&jsx(Zap,{"data-testid":"zap-icon","aria-hidden":"true",className:"ml-1"}),s&&jsxs("span",{id:N,className:"sr-only",children:["Premium feature: ",P||"Upgrade required to access this feature"]}),V.isFocusVisible&&v&&jsx("kbd",{className:"ml-auto hidden text-xs opacity-60 lg:inline",children:v}),V.isPressed&&jsx("span",{className:"absolute inset-0 rounded-[inherit] bg-current opacity-10 motion-safe:animate-in motion-safe:zoom-in-95","aria-hidden":"true"})]})})}));S.displayName="Button";var dt=cva(["pointer-events-auto relative flex w-[356px] items-center justify-between","space-x-4 overflow-hidden rounded-lg border p-4 pr-12 shadow-lg","transition-all duration-300","motion-reduce:transition-none motion-reduce:animate-none","data-[entering]:animate-in data-[entering]:slide-in-from-left","data-[exiting]:animate-out data-[exiting]:slide-out-to-right-full","data-[exiting]:fade-out-80","touch-pan-y"],{variants:{variant:{default:["bg-[var(--content-background)]","text-[var(--content-foreground)]","border-[var(--border)]"],primary:["bg-[var(--primary)]","text-[var(--primary-foreground)]","border-[var(--primary)]"],destructive:["bg-[var(--destructive-background)]","text-[var(--destructive-foreground)]","border-[var(--destructive-background)]"],accent:["bg-[var(--accent-background)]","text-[var(--accent-foreground)]","border-[var(--accent-background)]"]}},defaultVariants:{variant:"default"}}),W=cva(["fixed z-50 flex flex-col gap-2 p-4","pointer-events-none","max-h-screen overflow-hidden"],{variants:{position:{"top-left":"top-0 left-0 items-start","top-center":"top-0 left-1/2 -translate-x-1/2 items-center","top-right":"top-0 right-0 items-end","bottom-left":"bottom-0 left-0 items-start","bottom-center":"bottom-0 left-1/2 -translate-x-1/2 items-center","bottom-right":"bottom-0 right-0 items-end"}},defaultVariants:{position:"bottom-right"}}),pt=memo(forwardRef(({variant:t="default",className:e,onPress:a},n)=>jsx(S,{ref:n,variant:"ghost",visualSize:"icon",onPress:a,className:i("absolute right-2 top-2",t==="default"?"hover:bg-[var(--accent-background)]/20":"hover:bg-[var(--page-background)]/20",e),"aria-label":"Dismiss notification",children:jsx(X$1,{className:"h-4 w-4","aria-hidden":"true"})})));pt.displayName="CloseButton";var l=new ToastQueue({maxVisibleToasts:3}),R=0,D=new Set;function Ot(){R++,D.forEach(t=>t());}function _t(){R=Math.max(0,R-1),D.forEach(t=>t());}function Y(){return R}function mt(t){return D.add(t),()=>D.delete(t)}var ft={add:(t,e)=>l.add(t,e),close:t=>{l.close(t);},closeAll:()=>{l.visibleToasts.forEach(t=>{l.close(t.key);});},pauseAll:()=>{l.pauseAll();},resumeAll:()=>{l.resumeAll();},get visibleToasts(){return l.visibleToasts},subscribe:t=>l.subscribe(t),get totalCount(){return R}};function O(){return l}function zt(t){return t===0?0:Math.max(t,5e3)}function Gt(t={}){return {variant:t.variant??"default",timeout:t.timeout??5e3,onClose:t.onClose}}function Ht(t,e){let a=Gt(e),n=zt(a.timeout),u="timeout";Ot();let c=ft.add({key:"",content:t,variant:a.variant,timeout:n,onClose:a.onClose},{timeout:n,onClose:()=>{_t(),a.onClose&&a.onClose(c,u);}});return c}var X=memo(forwardRef(({toast:t,state:e},a)=>{let n=useRef(null),u=a||n,{toastProps:c,contentProps:f}=useToast({toast:t},e,u),[d,v]=useState(null),[o,h]=useState(0),s=50,x=useCallback(g=>{let b=g.touches[0];b&&v(b.clientX);},[]),P=useCallback(g=>{if(d===null)return;let b=g.touches[0];if(!b)return;let k=b.clientX-d;k>0&&h(k);},[d]),p=useCallback(()=>{o>s&&e.close(t.key),h(0),v(null);},[o,e,t.key]),B=useCallback(()=>{e.close(t.key);},[e,t.key]),{content:m}=t,N=m.variant??"default",T=typeof m.content=="string"?m.content:"Notification";return jsxs("div",{...c,ref:u,className:i(dt({variant:N})),"aria-label":T,style:{transform:o>0?`translateX(${o}px)`:void 0,opacity:o>0?1-o/200:void 0},onTouchStart:x,onTouchMove:P,onTouchEnd:p,children:[jsx("div",{...f,className:"flex-1 min-w-0",children:typeof m.content=="string"?jsx("p",{className:"text-sm font-medium",children:m.content}):m.content}),jsx(pt,{variant:N,onPress:B})]})}));X.displayName="ToastItem";var Qt=z$1.enum(["timeout","user","programmatic"]),vt=z$1.enum(["default","primary","destructive","accent"]),Tt=z$1.enum(["top-left","top-center","top-right","bottom-left","bottom-center","bottom-right"]),Mt=z$1.object({variant:vt.default("default"),timeout:z$1.number().min(0).default(5e3),onClose:z$1.function().optional()}),F=z$1.object({maxVisibleToasts:z$1.number().min(1).default(3)}),U=z$1.object({position:Tt.default("bottom-right"),"aria-label":z$1.string().default("Notifications"),className:z$1.string().optional()});var Yt=createContext(null);function bt({children:t,maxVisibleToasts:e=3}){F.parse({maxVisibleToasts:e});let a=useMemo(()=>O(),[]);return jsx(Yt.Provider,{value:a,children:t})}bt.displayName="ToastProvider";function te({queuedCount:t}){if(t<=0)return null;let e=Math.min(t,2);return jsxs("div",{className:"relative w-[356px] pointer-events-none","aria-hidden":"true",children:[jsx("div",{className:i("absolute left-1/2 -translate-x-1/2 w-[340px] h-4 rounded-lg shadow-lg","bg-[var(--content-background)] border border-[var(--border)]","opacity-60","transition-all duration-200"),style:{top:0}}),e>=2&&jsx("div",{className:i("absolute left-1/2 -translate-x-1/2 w-[324px] h-2 rounded-b-lg shadow-lg","bg-[var(--content-background)] border border-t-0 border-[var(--border)]","opacity-40","transition-all duration-200"),style:{top:14}}),jsx("div",{className:"h-4"}),jsxs("div",{className:"text-sm text-[var(--content-foreground)] opacity-70 text-center py-2 pointer-events-auto","aria-live":"polite",children:["+",t," more notification",t>1?"s":""]})]})}var ht=memo(forwardRef((t,e)=>{let a=U.parse(t),{position:n="bottom-right","aria-label":u="Notifications",className:c}=a,f=useRef(null),d=e||f,v=O(),o=useToastQueue(v),h=useSyncExternalStore(mt,Y,Y),{regionProps:s}=useToastRegion({"aria-label":u},o,d),x=Math.max(0,h-o.visibleToasts.length),P=useCallback(()=>{o.visibleToasts.forEach(p=>{o.close(p.key);});},[o]);return o.visibleToasts.length===0?null:jsxs("div",{...s,ref:d,className:i(W({position:n}),c),children:[o.visibleToasts.map(p=>jsx(X,{toast:p,state:o},p.key)),jsx(te,{queuedCount:x}),o.visibleToasts.length>=2&&jsx(S,{variant:"secondary",visualSize:"sm",onPress:P,"aria-label":"Clear all notifications",className:"mt-2 pointer-events-auto",children:"Clear All"})]})}));ht.displayName="Toaster";export{Qt as DismissReasonSchema,Mt as ToastOptionsSchema,bt as ToastProvider,F as ToastProviderPropsSchema,vt as ToastVariantSchema,ht as Toaster,Tt as ToasterPositionSchema,U as ToasterPropsSchema,Ht as toast,ft as toastQueue,dt as toastVariants,W as toasterVariants};//# sourceMappingURL=index.mjs.map
|
|
2
|
+
import {createContext,memo,forwardRef,useId,useRef,useState,useCallback,useSyncExternalStore,useContext,useMemo}from'react';import {useToast,useToastRegion}from'react-aria';import {ToastQueue,useToastQueue}from'react-stately';import {cva}from'class-variance-authority';import {Loader2,Zap,X as X$1}from'lucide-react';import {clsx}from'clsx';import {twMerge}from'tailwind-merge';import {Button}from'react-aria-components';import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {z as z$1}from'zod';function i(...t){return twMerge(clsx(t))}var O=cva("inline-flex justify-center min-h-[44px] min-w-[44px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50",{variants:{fullWidth:{true:"w-full",false:""},inVerticalGroup:{true:"items-stretch",false:"items-center"}},defaultVariants:{fullWidth:false,inVerticalGroup:false}}),z=cva("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 relative cursor-pointer",{variants:{variant:{default:"bg-[var(--primary-action)] text-[var(--primary-action-foreground)] shadow-md hover:bg-[var(--primary-action-hover)] data-[pressed]:bg-[var(--primary-action)]/80",destructive:"bg-[var(--destructive-background)] text-[var(--destructive-foreground)] shadow-md hover:bg-[var(--destructive-background)]/90 data-[pressed]:bg-[var(--destructive-background)]/80",outline:"border border-[var(--input-border)] bg-[var(--page-background)] hover:bg-[var(--input-border)] data-[pressed]:bg-[var(--input-border)]",secondary:"bg-[var(--secondary)] text-[var(--secondary-foreground)] shadow-md hover:bg-[var(--secondary)]/80 data-[pressed]:bg-[var(--secondary)]/70",ghost:"hover:bg-[var(--accent)] hover:text-[var(--accent-foreground)] data-[pressed]:bg-[var(--accent)]",link:"text-[var(--text-link)] underline-offset-4 hover:underline data-[pressed]:text-[var(--text-link-hover)]"},fullWidth:{true:"w-full",false:""},visualSize:{default:"h-10 px-4 py-2",sm:"h-9 rounded-md px-3 text-xs",lg:"h-11 rounded-md px-8",icon:"h-10 w-10",dot:"h-5 w-5 rounded-full p-0 min-h-0 min-w-0"},paywall:{true:"!bg-[var(--paywall)] !text-[var(--paywall-foreground)] !shadow-md hover:!bg-[var(--paywall)]/90 !cursor-not-allowed !border-transparent",false:""}},defaultVariants:{variant:"default",visualSize:"default",paywall:false}});var H="data-[pressed]:scale-[0.97]";var L="data-[hovered]:shadow-md";var w="hc:data-[hovered]:outline hc:data-[hovered]:outline-2 hc:data-[hovered]:outline-foreground",I="hc:data-[pressed]:outline hc:data-[pressed]:outline-2 hc:data-[pressed]:outline-offset-1 hc:data-[pressed]:outline-foreground";var ot=createContext(null);ot.displayName="ButtonGroupContext";function at(){return useContext(ot)}var rt=createContext(null);rt.displayName="ButtonGroupItemContext";function nt(){return useContext(rt)}cva("inline-flex items-center gap-0",{variants:{orientation:{horizontal:"flex-row",vertical:"flex-col w-full"}},defaultVariants:{orientation:"horizontal"}});var st=cva("",{variants:{orientation:{horizontal:"min-w-[44px]",vertical:"flex min-h-[44px]"},position:{first:"",middle:"",last:"",only:""}},compoundVariants:[{orientation:"horizontal",position:"first",className:"rounded-r-none border-r-0"},{orientation:"horizontal",position:"middle",className:"rounded-none border-r-0"},{orientation:"horizontal",position:"last",className:"rounded-l-none"},{orientation:"vertical",position:"first",className:"rounded-b-none border-b-0"},{orientation:"vertical",position:"middle",className:"rounded-none border-b-0"},{orientation:"vertical",position:"last",className:"rounded-t-none"}],defaultVariants:{orientation:"horizontal",position:"only"}});cva("bg-[var(--border)]",{variants:{orientation:{horizontal:"w-px h-6 mx-1",vertical:"h-px w-full my-1"}},defaultVariants:{orientation:"horizontal"}});var S=memo(forwardRef(({className:t,buttonVisualClassName:e,variant:a,size:n,visualSize:u,fullWidth:c,loading:f=false,loadingText:d="Loading...",shortcut:v,children:o,isDisabled:h,paywall:s=false,paywallRedirect:x,paywallDescription:P,onPress:p,...k},m)=>{let N=useId(),b=at(),g=nt(),T=a??b?.variant??"default",B=n??b?.size,xt=h??b?.isDisabled??false,q=b?.orientation==="vertical",K=c||q,gt=g?st({orientation:b?.orientation??"horizontal",position:g.position}):"",G=u??B??"default";return process.env.NODE_ENV!=="production"&&(G==="dot"||G==="icon")&&!k["aria-label"]&&!o&&console.warn('[Button] visualSize="dot" or "icon" requires aria-label when no visible text is provided (WCAG 1.1.1)'),jsx(Button,{ref:m,isDisabled:xt||f||void 0,"aria-disabled":s?true:void 0,"aria-describedby":s?N:void 0,onPress:V=>{if(s){x&&window.open(x,"_blank","noopener,noreferrer");return}p?.(V);},className:i(O({fullWidth:K,inVerticalGroup:q}),t),...k,children:V=>jsxs("span",{className:i(z({variant:T,visualSize:G,paywall:s,fullWidth:K}),gt,e,H,L,w,I),"data-pressed":V.isPressed||void 0,children:[f&&jsxs(Fragment,{children:[jsx(Loader2,{className:"motion-safe:animate-spin","aria-hidden":"true"}),jsx("span",{className:"sr-only","aria-live":"polite",children:d})]}),!f&&o,s&&jsx(Zap,{"data-testid":"zap-icon","aria-hidden":"true",className:"ml-1"}),s&&jsxs("span",{id:N,className:"sr-only",children:["Premium feature: ",P||"Upgrade required to access this feature"]}),V.isFocusVisible&&v&&jsx("kbd",{className:"ml-auto hidden text-xs opacity-60 lg:inline",children:v}),V.isPressed&&jsx("span",{className:"absolute inset-0 rounded-[inherit] bg-current opacity-10 motion-safe:animate-in motion-safe:zoom-in-95","aria-hidden":"true"})]})})}));S.displayName="Button";var dt=cva(["pointer-events-auto relative flex w-[356px] items-center justify-between","space-x-4 overflow-hidden rounded-lg border p-4 pr-12 shadow-lg","transition-all duration-300","motion-reduce:transition-none motion-reduce:animate-none","data-[entering]:animate-in data-[entering]:slide-in-from-left","data-[exiting]:animate-out data-[exiting]:slide-out-to-right-full","data-[exiting]:fade-out-80","touch-pan-y"],{variants:{variant:{default:["bg-[var(--content-background)]","text-[var(--content-foreground)]","border-[var(--border)]"],primary:["bg-[var(--primary)]","text-[var(--primary-foreground)]","border-[var(--primary)]"],destructive:["bg-[var(--destructive-background)]","text-[var(--destructive-foreground)]","border-[var(--destructive-background)]"],accent:["bg-[var(--accent-background)]","text-[var(--accent-foreground)]","border-[var(--accent-background)]"]}},defaultVariants:{variant:"default"}}),W=cva(["fixed z-50 flex flex-col gap-2 p-4","pointer-events-none","max-h-screen overflow-hidden"],{variants:{position:{"top-left":"top-0 left-0 items-start","top-center":"top-0 left-1/2 -translate-x-1/2 items-center","top-right":"top-0 right-0 items-end","bottom-left":"bottom-0 left-0 items-start","bottom-center":"bottom-0 left-1/2 -translate-x-1/2 items-center","bottom-right":"bottom-0 right-0 items-end"}},defaultVariants:{position:"bottom-right"}}),pt=memo(forwardRef(({variant:t="default",className:e,onPress:a},n)=>jsx(S,{ref:n,variant:"ghost",visualSize:"icon",onPress:a,className:i("absolute right-2 top-2",t==="default"?"hover:bg-[var(--accent-background)]/20":"hover:bg-[var(--page-background)]/20",e),"aria-label":"Dismiss notification",children:jsx(X$1,{className:"h-4 w-4","aria-hidden":"true"})})));pt.displayName="CloseButton";var l=new ToastQueue({maxVisibleToasts:3}),R=0,D=new Set;function _t(){R++,D.forEach(t=>t());}function Gt(){R=Math.max(0,R-1),D.forEach(t=>t());}function Y(){return R}function mt(t){return D.add(t),()=>D.delete(t)}var ft={add:(t,e)=>l.add(t,e),close:t=>{l.close(t);},closeAll:()=>{l.visibleToasts.forEach(t=>{l.close(t.key);});},pauseAll:()=>{l.pauseAll();},resumeAll:()=>{l.resumeAll();},get visibleToasts(){return l.visibleToasts},subscribe:t=>l.subscribe(t),get totalCount(){return R}};function _(){return l}function Ot(t){return t===0?0:Math.max(t,5e3)}function zt(t={}){return {variant:t.variant??"default",timeout:t.timeout??5e3,onClose:t.onClose}}function Ht(t,e){let a=zt(e),n=Ot(a.timeout),u="timeout";_t();let c=ft.add({key:"",content:t,variant:a.variant,timeout:n,onClose:a.onClose},{timeout:n,onClose:()=>{Gt(),a.onClose&&a.onClose(c,u);}});return c}var X=memo(forwardRef(({toast:t,state:e},a)=>{let n=useRef(null),u=a||n,{toastProps:c,contentProps:f}=useToast({toast:t},e,u),[d,v]=useState(null),[o,h]=useState(0),s=50,x=useCallback(g=>{let T=g.touches[0];T&&v(T.clientX);},[]),P=useCallback(g=>{if(d===null)return;let T=g.touches[0];if(!T)return;let B=T.clientX-d;B>0&&h(B);},[d]),p=useCallback(()=>{o>s&&e.close(t.key),h(0),v(null);},[o,e,t.key]),k=useCallback(()=>{e.close(t.key);},[e,t.key]),{content:m}=t,N=m.variant??"default",b=typeof m.content=="string"?m.content:"Notification";return jsxs("div",{...c,ref:u,className:i(dt({variant:N})),"aria-label":b,style:{transform:o>0?`translateX(${o}px)`:void 0,opacity:o>0?1-o/200:void 0},onTouchStart:x,onTouchMove:P,onTouchEnd:p,children:[jsx("div",{...f,className:"flex-1 min-w-0",children:typeof m.content=="string"?jsx("p",{className:"text-sm font-medium",children:m.content}):m.content}),jsx(pt,{variant:N,onPress:k})]})}));X.displayName="ToastItem";var Qt=z$1.enum(["timeout","user","programmatic"]),vt=z$1.enum(["default","primary","destructive","accent"]),bt=z$1.enum(["top-left","top-center","top-right","bottom-left","bottom-center","bottom-right"]),Mt=z$1.object({variant:vt.default("default"),timeout:z$1.number().min(0).default(5e3),onClose:z$1.function().optional()}),F=z$1.object({maxVisibleToasts:z$1.number().min(1).default(3)}),U=z$1.object({position:bt.default("bottom-right"),"aria-label":z$1.string().default("Notifications"),className:z$1.string().optional()});var Yt=createContext(null);function Tt({children:t,maxVisibleToasts:e=3}){F.parse({maxVisibleToasts:e});let a=useMemo(()=>_(),[]);return jsx(Yt.Provider,{value:a,children:t})}Tt.displayName="ToastProvider";function te({queuedCount:t}){if(t<=0)return null;let e=Math.min(t,2);return jsxs("div",{className:"relative w-[356px] pointer-events-none","aria-hidden":"true",children:[jsx("div",{className:i("absolute left-1/2 -translate-x-1/2 w-[340px] h-4 rounded-lg shadow-lg","bg-[var(--content-background)] border border-[var(--border)]","opacity-60","transition-all duration-200"),style:{top:0}}),e>=2&&jsx("div",{className:i("absolute left-1/2 -translate-x-1/2 w-[324px] h-2 rounded-b-lg shadow-lg","bg-[var(--content-background)] border border-t-0 border-[var(--border)]","opacity-40","transition-all duration-200"),style:{top:14}}),jsx("div",{className:"h-4"}),jsxs("div",{className:"text-sm text-[var(--content-foreground)] opacity-70 text-center py-2 pointer-events-auto","aria-live":"polite",children:["+",t," more notification",t>1?"s":""]})]})}var ht=memo(forwardRef((t,e)=>{let a=U.parse(t),{position:n="bottom-right","aria-label":u="Notifications",className:c}=a,f=useRef(null),d=e||f,v=_(),o=useToastQueue(v),h=useSyncExternalStore(mt,Y,Y),{regionProps:s}=useToastRegion({"aria-label":u},o,d),x=Math.max(0,h-o.visibleToasts.length),P=useCallback(()=>{o.visibleToasts.forEach(p=>{o.close(p.key);});},[o]);return o.visibleToasts.length===0?null:jsxs("div",{...s,ref:d,className:i(W({position:n}),c),children:[o.visibleToasts.map(p=>jsx(X,{toast:p,state:o},p.key)),jsx(te,{queuedCount:x}),o.visibleToasts.length>=2&&jsx(S,{variant:"secondary",visualSize:"sm",onPress:P,"aria-label":"Clear all notifications",className:"mt-2 pointer-events-auto",children:"Clear All"})]})}));ht.displayName="Toaster";export{Qt as DismissReasonSchema,Mt as ToastOptionsSchema,Tt as ToastProvider,F as ToastProviderPropsSchema,vt as ToastVariantSchema,ht as Toaster,bt as ToasterPositionSchema,U as ToasterPropsSchema,Ht as toast,ft as toastQueue,dt as toastVariants,W as toasterVariants};//# sourceMappingURL=index.mjs.map
|
|
3
3
|
//# sourceMappingURL=index.mjs.map
|