lucent-ui 0.4.0 → 0.4.2

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/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),h=require("react"),ge={primary:{background:"var(--lucent-accent-default)",color:"var(--lucent-text-on-accent)",border:"1px solid var(--lucent-accent-default)"},secondary:{background:"var(--lucent-surface-default)",color:"var(--lucent-text-primary)",border:"1px solid var(--lucent-border-default)"},ghost:{background:"transparent",color:"var(--lucent-text-primary)",border:"1px solid transparent"},danger:{background:"var(--lucent-danger-default)",color:"#ffffff",border:"1px solid var(--lucent-danger-default)"}},ve={sm:{height:"32px",padding:"0 var(--lucent-space-3)",fontSize:"var(--lucent-font-size-sm)"},md:{height:"38px",padding:"0 var(--lucent-space-4)",fontSize:"var(--lucent-font-size-md)"},lg:{height:"46px",padding:"0 var(--lucent-space-5)",fontSize:"var(--lucent-font-size-lg)"}},ee=h.forwardRef(({variant:t="primary",size:n="md",loading:a=!1,fullWidth:o=!1,spread:r=!1,leftIcon:i,rightIcon:s,chevron:c=!1,disableHoverStyles:f=!1,children:g,disabled:y,style:d,...l},p)=>{const v=y??a;return e.jsxs("button",{ref:p,disabled:v,"aria-busy":a,style:{display:"inline-flex",alignItems:"center",justifyContent:r?"space-between":"center",gap:"var(--lucent-space-2)",fontFamily:"var(--lucent-font-family-base)",fontWeight:"var(--lucent-font-weight-medium)",lineHeight:1,letterSpacing:"0.01em",borderRadius:"var(--lucent-radius-lg)",cursor:v?"not-allowed":"pointer",width:o?"100%":void 0,transition:"background var(--lucent-duration-fast) var(--lucent-easing-default), border-color var(--lucent-duration-fast) var(--lucent-easing-default), box-shadow var(--lucent-duration-fast) var(--lucent-easing-default), transform 80ms var(--lucent-easing-default)",whiteSpace:"nowrap",boxSizing:"border-box",outline:"none",margin:0,...ve[n],...ge[t],...d,...v&&{background:"var(--lucent-bg-muted)",color:"var(--lucent-text-disabled)",borderColor:"transparent"}},onMouseEnter:m=>{var T;!v&&!f&&be(m.currentTarget,t),(T=l.onMouseEnter)==null||T.call(l,m)},onMouseLeave:m=>{var T;!v&&!f&&ye(m.currentTarget,t),(T=l.onMouseLeave)==null||T.call(l,m)},onMouseDown:m=>{var T;v||(m.currentTarget.style.transform="scale(0.95)"),(T=l.onMouseDown)==null||T.call(l,m)},onMouseUp:m=>{var T;m.currentTarget.style.transform="",(T=l.onMouseUp)==null||T.call(l,m)},onFocus:m=>{var T;m.currentTarget.style.boxShadow="0 0 0 3px var(--lucent-accent-subtle)",(T=l.onFocus)==null||T.call(l,m)},onBlur:m=>{var T;m.currentTarget.style.boxShadow="",(T=l.onBlur)==null||T.call(l,m)},...l,children:[i,a?e.jsx(ke,{}):g,!a&&s,!a&&c&&e.jsx(we,{size:n})]})});ee.displayName="Button";function be(t,n){n==="primary"?(t.style.background="var(--lucent-accent-hover)",t.style.borderColor="var(--lucent-accent-hover)"):n==="secondary"?t.style.background="var(--lucent-bg-subtle)":n==="ghost"?t.style.background="var(--lucent-bg-muted)":n==="danger"&&(t.style.background="var(--lucent-danger-hover)",t.style.borderColor="var(--lucent-danger-hover)")}function ye(t,n){n==="primary"?(t.style.background="var(--lucent-accent-default)",t.style.borderColor="var(--lucent-accent-default)"):n==="secondary"?t.style.background="var(--lucent-surface-default)":n==="ghost"?t.style.background="transparent":n==="danger"&&(t.style.background="var(--lucent-danger-default)",t.style.borderColor="var(--lucent-danger-default)")}const xe={sm:12,md:14,lg:16};function we({size:t}){const n=xe[t];return e.jsx("svg",{width:n,height:n,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2.5,strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":!0,style:{flexShrink:0,marginLeft:-2},children:e.jsx("polyline",{points:"6 9 12 15 18 9"})})}function ke(){return e.jsxs("svg",{width:14,height:14,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2.5,strokeLinecap:"round","aria-hidden":!0,style:{animation:"lucent-spin 0.7s linear infinite",flexShrink:0},children:[e.jsx("style",{children:"@keyframes lucent-spin { to { transform: rotate(360deg); } }"}),e.jsx("path",{d:"M12 2a10 10 0 0 1 10 10"})]})}const Se={id:"button",name:"Button",tier:"atom",domain:"neutral",specVersion:"1.0",description:"A clickable control that triggers an action. The primary interactive primitive in Lucent UI.",designIntent:'Buttons communicate available actions. Variant conveys hierarchy: use "primary" for the single most important action in a view, "secondary" for supporting actions, "ghost" for low-emphasis actions in dense UIs, and "danger" exclusively for destructive or irreversible operations. Size should match surrounding content density — prefer "md" as the default and reserve "sm" for toolbars or tables.',props:[{name:"variant",type:"enum",required:!1,default:"primary",description:"Visual style conveying action hierarchy.",enumValues:["primary","secondary","ghost","danger"]},{name:"size",type:"enum",required:!1,default:"md",description:"Controls height and padding.",enumValues:["sm","md","lg"]},{name:"children",type:"ReactNode",required:!0,description:"Button label or content."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Prevents interaction and applies disabled styling."},{name:"loading",type:"boolean",required:!1,default:"false",description:"Shows a spinner and prevents interaction while an async action is in progress."},{name:"fullWidth",type:"boolean",required:!1,default:"false",description:"Stretches the button to fill its container width."},{name:"leftIcon",type:"ReactNode",required:!1,description:"Icon element rendered before the label."},{name:"rightIcon",type:"ReactNode",required:!1,description:"Icon element rendered after the label."},{name:"onClick",type:"function",required:!1,description:"Called when the button is clicked and not disabled or loading."},{name:"type",type:"enum",required:!1,default:"button",description:"Native button type attribute.",enumValues:["button","submit","reset"]}],usageExamples:[{title:"Primary action",code:'<Button variant="primary" onClick={handleSave}>Save changes</Button>'},{title:"Destructive action",code:'<Button variant="danger" onClick={handleDelete}>Delete account</Button>'},{title:"Loading state",code:'<Button variant="primary" loading={isSaving}>Save changes</Button>'},{title:"With icon",code:'<Button variant="secondary" leftIcon={<PlusIcon />}>Add member</Button>'},{title:"Ghost in toolbar",code:'<Button variant="ghost" size="sm">Edit</Button>'},{title:"Full-width submit",code:'<Button variant="primary" type="submit" fullWidth>Sign in</Button>'}],compositionGraph:[],accessibility:{role:"button",ariaAttributes:["aria-disabled","aria-busy"],keyboardInteractions:["Enter — activates the button","Space — activates the button"]}},G=h.forwardRef(({label:t,helperText:n,errorText:a,leftElement:o,rightElement:r,id:i,style:s,...c},f)=>{const g=i??`lucent-input-${Math.random().toString(36).slice(2,7)}`,y=!!a,d=!!c.disabled;return e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"var(--lucent-space-1)",width:"100%"},children:[t&&e.jsx("label",{htmlFor:g,style:{fontSize:"var(--lucent-font-size-sm)",fontWeight:"var(--lucent-font-weight-medium)",color:"var(--lucent-text-primary)",fontFamily:"var(--lucent-font-family-base)"},children:t}),e.jsxs("div",{style:{position:"relative",display:"flex",alignItems:"center"},children:[o&&e.jsx("span",{style:{position:"absolute",left:"var(--lucent-space-3)",color:d?"var(--lucent-text-disabled)":"var(--lucent-text-secondary)",display:"flex",alignItems:"center",pointerEvents:"none"},children:o}),e.jsx("input",{ref:f,id:g,"aria-invalid":y,"aria-describedby":y?`${g}-error`:n?`${g}-helper`:void 0,style:{width:"100%",height:"40px",padding:`0 ${r?"var(--lucent-space-10)":"var(--lucent-space-3)"} 0 ${o?"var(--lucent-space-10)":"var(--lucent-space-3)"}`,fontSize:"var(--lucent-font-size-md)",fontFamily:"var(--lucent-font-family-base)",color:d?"var(--lucent-text-disabled)":"var(--lucent-text-primary)",background:d?"var(--lucent-bg-muted)":"var(--lucent-surface-default)",border:`1px solid ${d?"transparent":y?"var(--lucent-danger-default)":"var(--lucent-border-default)"}`,cursor:d?"not-allowed":void 0,borderRadius:"var(--lucent-radius-lg)",outline:"none",boxSizing:"border-box",transition:"border-color var(--lucent-duration-fast) var(--lucent-easing-default)",...s},onMouseEnter:l=>{var p;!c.disabled&&l.currentTarget!==document.activeElement&&(l.currentTarget.style.borderColor=y?"var(--lucent-danger-default)":"var(--lucent-border-strong)"),(p=c.onMouseEnter)==null||p.call(c,l)},onMouseLeave:l=>{var p;!c.disabled&&l.currentTarget!==document.activeElement&&(l.currentTarget.style.borderColor=y?"var(--lucent-danger-default)":"var(--lucent-border-default)"),(p=c.onMouseLeave)==null||p.call(c,l)},onFocus:l=>{var p;l.currentTarget.style.borderColor=y?"var(--lucent-danger-default)":"var(--lucent-focus-ring)",l.currentTarget.style.boxShadow=`0 0 0 3px ${y?"var(--lucent-danger-subtle)":"var(--lucent-accent-subtle)"}`,(p=c.onFocus)==null||p.call(c,l)},onBlur:l=>{var p;l.currentTarget.style.borderColor=y?"var(--lucent-danger-default)":"var(--lucent-border-default)",l.currentTarget.style.boxShadow="none",(p=c.onBlur)==null||p.call(c,l)},...c}),r&&e.jsx("span",{style:{position:"absolute",right:"var(--lucent-space-3)",color:d?"var(--lucent-text-disabled)":"var(--lucent-text-secondary)",display:"flex",alignItems:"center"},children:r})]}),y&&e.jsx("span",{id:`${g}-error`,role:"alert",style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-danger-text)",fontFamily:"var(--lucent-font-family-base)"},children:a}),!y&&n&&e.jsx("span",{id:`${g}-helper`,style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-text-secondary)",fontFamily:"var(--lucent-font-family-base)"},children:n})]})});G.displayName="Input";const je={id:"input",name:"Input",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A single-line text field with optional label, helper text, and error state.",designIntent:"Always pair with a visible label — never rely on placeholder text alone as it disappears on input and is inaccessible. Use errorText (not helperText) to surface validation failures; the component applies danger styling automatically. leftElement and rightElement accept icons or small controls (e.g. currency symbol, clear button).",props:[{name:"type",type:"enum",required:!1,default:"text",description:"HTML input type.",enumValues:["text","number","password","email","tel","url","search"]},{name:"label",type:"string",required:!1,description:"Visible label rendered above the input."},{name:"helperText",type:"string",required:!1,description:"Supplementary hint shown below the input."},{name:"errorText",type:"string",required:!1,description:"Validation error message. When set, input renders in error state."},{name:"leftElement",type:"ReactNode",required:!1,description:"Icon or adornment rendered inside the left edge."},{name:"rightElement",type:"ReactNode",required:!1,description:"Icon or adornment rendered inside the right edge."},{name:"placeholder",type:"string",required:!1,description:"Placeholder text. Use as a hint, not a label."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Disables the input."},{name:"value",type:"string",required:!1,description:"Controlled value."},{name:"onChange",type:"function",required:!1,description:"Change handler."}],usageExamples:[{title:"Basic",code:'<Input label="Email" type="email" placeholder="you@example.com" />'},{title:"With helper text",code:'<Input label="Username" helperText="3–20 characters, letters and numbers only" />'},{title:"Error state",code:'<Input label="Password" type="password" value={value} errorText="Must be at least 8 characters" />'},{title:"With icon",code:'<Input label="Search" leftElement={<SearchIcon />} placeholder="Search…" />'}],compositionGraph:[],accessibility:{role:"textbox",ariaAttributes:["aria-invalid","aria-describedby","aria-label"],keyboardInteractions:["Tab — focuses the input"]}},te=h.forwardRef(({label:t,helperText:n,errorText:a,autoResize:o=!1,maxLength:r,showCount:i=!1,id:s,value:c,onChange:f,style:g,...y},d)=>{const l=h.useRef(null),p=d??l,v=s??`lucent-textarea-${Math.random().toString(36).slice(2,7)}`,m=!!a,T=typeof c=="string"?c.length:0;return h.useEffect(()=>{if(!o)return;const b=p.current;b&&(b.style.height="auto",b.style.height=`${b.scrollHeight}px`)},[c,o,p]),e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"var(--lucent-space-1)",width:"100%"},children:[t&&e.jsx("label",{htmlFor:v,style:{fontSize:"var(--lucent-font-size-sm)",fontWeight:"var(--lucent-font-weight-medium)",color:"var(--lucent-text-primary)",fontFamily:"var(--lucent-font-family-base)"},children:t}),e.jsx("textarea",{ref:p,id:v,maxLength:r,value:c,onChange:f,"aria-invalid":m,"aria-describedby":m?`${v}-error`:n?`${v}-helper`:void 0,style:{width:"100%",minHeight:"100px",padding:"var(--lucent-space-3)",fontSize:"var(--lucent-font-size-md)",fontFamily:"var(--lucent-font-family-base)",color:"var(--lucent-text-primary)",background:"var(--lucent-surface-default)",border:`1px solid ${m?"var(--lucent-danger-default)":"var(--lucent-border-default)"}`,borderRadius:"var(--lucent-radius-md)",outline:"none",resize:o?"none":"vertical",boxSizing:"border-box",lineHeight:"var(--lucent-line-height-base)",transition:"border-color var(--lucent-duration-fast) var(--lucent-easing-default)",...g},onFocus:b=>{var x;b.currentTarget.style.borderColor=m?"var(--lucent-danger-default)":"var(--lucent-focus-ring)",b.currentTarget.style.boxShadow=`0 0 0 3px ${m?"var(--lucent-danger-subtle)":"var(--lucent-accent-subtle)"}`,(x=y.onFocus)==null||x.call(y,b)},onBlur:b=>{var x;b.currentTarget.style.borderColor=m?"var(--lucent-danger-default)":"var(--lucent-border-default)",b.currentTarget.style.boxShadow="none",(x=y.onBlur)==null||x.call(y,b)},...y}),e.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"flex-start"},children:[e.jsxs("div",{children:[m&&e.jsx("span",{id:`${v}-error`,role:"alert",style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-danger-text)",fontFamily:"var(--lucent-font-family-base)"},children:a}),!m&&n&&e.jsx("span",{id:`${v}-helper`,style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-text-secondary)",fontFamily:"var(--lucent-font-family-base)"},children:n})]}),(i||r)&&e.jsxs("span",{style:{fontSize:"var(--lucent-font-size-xs)",color:r&&T>=r?"var(--lucent-danger-text)":"var(--lucent-text-secondary)",fontFamily:"var(--lucent-font-family-mono)",flexShrink:0,marginLeft:"var(--lucent-space-2)"},children:[T,r?`/${r}`:""]})]})]})});te.displayName="Textarea";const Te={id:"textarea",name:"Textarea",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A multi-line text input with optional auto-resize and character count.",designIntent:"Use autoResize for open-ended fields (bio, description) where content length is unpredictable. Use maxLength + showCount for fields with hard limits (tweet-style). Behaves identically to Input for label/helper/error patterns.",props:[{name:"label",type:"string",required:!1,description:"Visible label above the textarea."},{name:"helperText",type:"string",required:!1,description:"Hint text shown below."},{name:"errorText",type:"string",required:!1,description:"Validation error. Triggers error styling."},{name:"autoResize",type:"boolean",required:!1,default:"false",description:"Grows with content, disables manual resize handle."},{name:"maxLength",type:"number",required:!1,description:"Character limit. Displays counter when set."},{name:"showCount",type:"boolean",required:!1,default:"false",description:"Always show character counter even without maxLength."},{name:"value",type:"string",required:!1,description:"Controlled value."},{name:"onChange",type:"function",required:!1,description:"Change handler."},{name:"placeholder",type:"string",required:!1,description:"Placeholder text."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Disables the textarea."}],usageExamples:[{title:"Basic",code:'<Textarea label="Bio" placeholder="Tell us about yourself…" />'},{title:"Auto-resize",code:'<Textarea label="Description" autoResize value={value} onChange={e => setValue(e.target.value)} />'},{title:"With character count",code:'<Textarea label="Tweet" maxLength={280} showCount value={value} onChange={e => setValue(e.target.value)} />'},{title:"Error state",code:'<Textarea label="Notes" errorText="Required" value="" />'}],compositionGraph:[],accessibility:{role:"textbox",ariaAttributes:["aria-multiline","aria-invalid","aria-describedby"],keyboardInteractions:["Tab — focuses the textarea"]}},Ce={neutral:{bg:"var(--lucent-bg-muted)",color:"var(--lucent-text-secondary)",border:"var(--lucent-border-default)"},accent:{bg:"var(--lucent-accent-default)",color:"var(--lucent-text-on-accent)",border:"var(--lucent-accent-default)"},success:{bg:"var(--lucent-success-subtle)",color:"var(--lucent-success-text)",border:"var(--lucent-success-subtle)"},warning:{bg:"var(--lucent-warning-subtle)",color:"var(--lucent-warning-text)",border:"var(--lucent-warning-subtle)"},danger:{bg:"var(--lucent-danger-subtle)",color:"var(--lucent-danger-text)",border:"var(--lucent-danger-subtle)"},info:{bg:"var(--lucent-info-subtle)",color:"var(--lucent-info-text)",border:"var(--lucent-info-subtle)"}},Ie={sm:{fontSize:"var(--lucent-font-size-xs)",padding:"0 var(--lucent-space-2)",height:"18px"},md:{fontSize:"var(--lucent-font-size-sm)",padding:"0 var(--lucent-space-2)",height:"22px"}};function Me({variant:t="neutral",size:n="md",dot:a=!1,children:o,style:r}){const i=Ce[t],s=Ie[n];return e.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:"var(--lucent-space-1)",height:s.height,padding:s.padding,fontSize:s.fontSize,fontFamily:"var(--lucent-font-family-base)",fontWeight:"var(--lucent-font-weight-medium)",lineHeight:1,borderRadius:"var(--lucent-radius-full)",background:i.bg,color:i.color,border:`1px solid ${i.border}`,whiteSpace:"nowrap",boxSizing:"border-box",...r},children:[a&&e.jsx("span",{style:{width:6,height:6,borderRadius:"var(--lucent-radius-full)",background:"currentColor",flexShrink:0}}),o]})}const ze={id:"badge",name:"Badge",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A small inline label for status, count, or category.",designIntent:'Badges communicate status or category at a glance. Match variant to semantic meaning — never use "danger" for non-critical states or "success" for neutral counts. Use dot=true when a single colour indicator is enough context (e.g. online status). Keep badge text short: 1–3 words maximum.',props:[{name:"variant",type:"enum",required:!1,default:"neutral",description:"Colour scheme conveying semantic meaning.",enumValues:["neutral","success","warning","danger","info","accent"]},{name:"size",type:"enum",required:!1,default:"md",description:"Controls height and font size.",enumValues:["sm","md"]},{name:"dot",type:"boolean",required:!1,default:"false",description:"Prepends a coloured dot indicator."},{name:"children",type:"ReactNode",required:!0,description:"Badge label."}],usageExamples:[{title:"Status",code:'<Badge variant="success" dot>Active</Badge>'},{title:"Count",code:'<Badge variant="danger">12</Badge>'},{title:"Category",code:'<Badge variant="info">Beta</Badge>'},{title:"Neutral tag",code:"<Badge>Draft</Badge>"}],compositionGraph:[],accessibility:{role:"status",notes:"Use aria-label on the parent element when badge meaning depends on context."}},Ee={xs:24,sm:32,md:40,lg:56,xl:80},qe={xs:"var(--lucent-font-size-xs)",sm:"var(--lucent-font-size-xs)",md:"var(--lucent-font-size-sm)",lg:"var(--lucent-font-size-lg)",xl:"var(--lucent-font-size-xl)"};function De(t,n){var o,r,i;if(n)return n.slice(0,2).toUpperCase();const a=t.trim().split(/\s+/);return a.length===1?(((o=a[0])==null?void 0:o[0])??"").toUpperCase():((((r=a[0])==null?void 0:r[0])??"")+(((i=a[a.length-1])==null?void 0:i[0])??"")).toUpperCase()}function Ae({src:t,alt:n,size:a="md",initials:o,style:r,...i}){const s=Ee[a],c=De(n,o),f={width:s,height:s,borderRadius:"var(--lucent-radius-full)",flexShrink:0,display:"inline-flex",alignItems:"center",justifyContent:"center",overflow:"hidden",boxSizing:"border-box",userSelect:"none",...r};return t?e.jsx("img",{src:t,alt:n,width:s,height:s,style:{...f,objectFit:"cover"},...i}):e.jsx("span",{role:"img","aria-label":n,style:{...f,background:"var(--lucent-accent-default)",color:"var(--lucent-text-on-accent)",fontSize:qe[a],fontWeight:"var(--lucent-font-weight-semibold)",fontFamily:"var(--lucent-font-family-base)"},children:c})}const Fe={id:"avatar",name:"Avatar",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A circular user image with initials fallback.",designIntent:"Always provide alt for accessibility — it is used to derive initials automatically when src is absent or fails. Use initials prop to override auto-derived initials (e.g. for non-Latin names). Size xs/sm suit table rows and compact lists; md is the default for comment threads; lg/xl for profile headers.",props:[{name:"src",type:"string",required:!1,description:"Image URL. Falls back to initials if omitted or fails to load."},{name:"alt",type:"string",required:!0,description:"Alt text and source for auto-derived initials."},{name:"size",type:"enum",required:!1,default:"md",description:"Diameter of the avatar.",enumValues:["xs","sm","md","lg","xl"]},{name:"initials",type:"string",required:!1,description:"Override auto-derived initials (max 2 characters)."}],usageExamples:[{title:"With image",code:'<Avatar src="/avatars/jane.jpg" alt="Jane Doe" />'},{title:"Initials fallback",code:'<Avatar alt="Jane Doe" />'},{title:"Large profile",code:'<Avatar src={user.avatar} alt={user.name} size="lg" />'},{title:"Custom initials",code:'<Avatar alt="张伟" initials="张" size="md" />'}],compositionGraph:[],accessibility:{role:"img",ariaAttributes:["aria-label"],notes:'When src is present, renders as <img> with alt. When showing initials, renders as <span role="img" aria-label>.'}},Re={xs:12,sm:16,md:24,lg:36},Ne={xs:2.5,sm:2.5,md:2,lg:2};function ne({size:t="md",label:n="Loading…",color:a}){const o=Re[t],r=Ne[t];return e.jsxs("span",{role:"status","aria-label":n,style:{display:"inline-flex",alignItems:"center",justifyContent:"center"},children:[e.jsxs("svg",{width:o,height:o,viewBox:"0 0 24 24",fill:"none","aria-hidden":!0,style:{animation:"lucent-spin 0.7s linear infinite",color:a??"currentColor"},children:[e.jsx("style",{children:"@keyframes lucent-spin { to { transform: rotate(360deg); } }"}),e.jsx("circle",{cx:12,cy:12,r:10,stroke:"currentColor",strokeWidth:r,strokeOpacity:.2}),e.jsx("path",{d:"M12 2a10 10 0 0 1 10 10",stroke:"currentColor",strokeWidth:r,strokeLinecap:"round"})]}),e.jsx("span",{style:{position:"absolute",width:1,height:1,overflow:"hidden",clip:"rect(0,0,0,0)",whiteSpace:"nowrap"},children:n})]})}const Le={id:"spinner",name:"Spinner",tier:"atom",domain:"neutral",specVersion:"0.1",description:"An animated loading indicator for async operations.",designIntent:"Use Spinner for indeterminate loading states of short duration (< 3s). For full-page or skeleton-level loading, prefer Skeleton instead. The label prop is visually hidden but read by screen readers — always set it to a meaningful description of what is loading.",props:[{name:"size",type:"enum",required:!1,default:"md",description:"Spinner diameter.",enumValues:["xs","sm","md","lg"]},{name:"label",type:"string",required:!1,default:"Loading…",description:"Visually hidden accessible label."},{name:"color",type:"string",required:!1,description:"Override colour (CSS value). Defaults to currentColor."}],usageExamples:[{title:"Default",code:"<Spinner />"},{title:"Inside button",code:'<Button loading><Spinner size="sm" label="Saving…" /></Button>'},{title:"Full-page overlay",code:`<div style={{ display: 'grid', placeItems: 'center', minHeight: '100vh' }}><Spinner size="lg" label="Loading dashboard…" /></div>`}],compositionGraph:[],accessibility:{role:"status",ariaAttributes:["aria-label"],notes:'The visible SVG is aria-hidden. The label is conveyed via a visually-hidden span inside role="status".'}};function Be({orientation:t="horizontal",label:n,spacing:a="var(--lucent-space-4)",style:o}){return t==="vertical"?e.jsx("span",{role:"separator","aria-orientation":"vertical",style:{display:"inline-block",width:"1px",alignSelf:"stretch",background:"var(--lucent-border-default)",margin:`0 ${a}`,flexShrink:0,...o}}):n?e.jsxs("div",{role:"separator","aria-label":n,style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-3)",margin:`${a} 0`,...o},children:[e.jsx("span",{style:{flex:1,height:"1px",background:"var(--lucent-border-default)"}}),e.jsx("span",{style:{fontSize:"var(--lucent-font-size-xs)",fontFamily:"var(--lucent-font-family-base)",color:"var(--lucent-text-secondary)",whiteSpace:"nowrap",letterSpacing:"var(--lucent-letter-spacing-wide)",textTransform:"uppercase"},children:n}),e.jsx("span",{style:{flex:1,height:"1px",background:"var(--lucent-border-default)"}})]}):e.jsx("hr",{role:"separator",style:{border:"none",borderTop:"1px solid var(--lucent-border-default)",margin:`${a} 0`,width:"100%",...o}})}const $e={id:"divider",name:"Divider",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A visual separator between content sections, horizontal or vertical.",designIntent:'Use horizontal Divider to separate sections in a layout. Use vertical Divider inline between sibling elements (e.g. nav links, toolbar buttons). Use the label prop for "OR" separators in auth flows or form sections — never use a plain text node next to a divider for this.',props:[{name:"orientation",type:"enum",required:!1,default:"horizontal",description:"Direction of the divider line.",enumValues:["horizontal","vertical"]},{name:"label",type:"string",required:!1,description:'Optional centered label (horizontal only). Common use: "OR", "AND", section titles.'},{name:"spacing",type:"string",required:!1,default:"var(--lucent-space-4)",description:"Margin on the axis perpendicular to the line."}],usageExamples:[{title:"Section separator",code:"<Divider />"},{title:"With label",code:'<Divider label="OR" />'},{title:"Vertical in nav",code:`<nav style={{ display: 'flex', alignItems: 'center' }}><a>Home</a><Divider orientation="vertical" /><a>About</a></nav>`}],compositionGraph:[],accessibility:{role:"separator",ariaAttributes:["aria-orientation","aria-label"]}},Pe={sm:14,md:16},We=`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),h=require("react"),ge={primary:{background:"var(--lucent-accent-default)",color:"var(--lucent-text-on-accent)",border:"1px solid var(--lucent-accent-default)"},secondary:{background:"var(--lucent-surface-default)",color:"var(--lucent-text-primary)",border:"1px solid var(--lucent-border-default)"},ghost:{background:"transparent",color:"var(--lucent-text-primary)",border:"1px solid transparent"},danger:{background:"var(--lucent-danger-default)",color:"#ffffff",border:"1px solid var(--lucent-danger-default)"}},ve={sm:{height:"32px",padding:"0 var(--lucent-space-3)",fontSize:"var(--lucent-font-size-sm)"},md:{height:"38px",padding:"0 var(--lucent-space-4)",fontSize:"var(--lucent-font-size-md)"},lg:{height:"46px",padding:"0 var(--lucent-space-5)",fontSize:"var(--lucent-font-size-lg)"}},ee=h.forwardRef(({variant:t="primary",size:n="md",loading:a=!1,fullWidth:o=!1,spread:r=!1,leftIcon:i,rightIcon:s,chevron:c=!1,disableHoverStyles:f=!1,children:g,disabled:y,style:d,...l},u)=>{const v=y??a;return e.jsxs("button",{ref:u,disabled:v,"aria-busy":a,style:{display:"inline-flex",alignItems:"center",justifyContent:r?"space-between":"center",gap:"var(--lucent-space-2)",fontFamily:"var(--lucent-font-family-base)",fontWeight:"var(--lucent-font-weight-medium)",lineHeight:1,letterSpacing:"0.01em",borderRadius:"var(--lucent-radius-lg)",cursor:v?"not-allowed":"pointer",width:o?"100%":void 0,transition:"background var(--lucent-duration-fast) var(--lucent-easing-default), border-color var(--lucent-duration-fast) var(--lucent-easing-default), box-shadow var(--lucent-duration-fast) var(--lucent-easing-default), transform 80ms var(--lucent-easing-default)",whiteSpace:"nowrap",boxSizing:"border-box",outline:"none",margin:0,...ve[n],...ge[t],...d,...v&&{background:"var(--lucent-bg-muted)",color:"var(--lucent-text-disabled)",borderColor:"transparent"}},onMouseEnter:m=>{var k;!v&&!f&&be(m.currentTarget,t),(k=l.onMouseEnter)==null||k.call(l,m)},onMouseLeave:m=>{var k;!v&&!f&&ye(m.currentTarget,t),(k=l.onMouseLeave)==null||k.call(l,m)},onMouseDown:m=>{var k;v||(m.currentTarget.style.transform="scale(0.95)"),(k=l.onMouseDown)==null||k.call(l,m)},onMouseUp:m=>{var k;m.currentTarget.style.transform="",(k=l.onMouseUp)==null||k.call(l,m)},onFocus:m=>{var k;m.currentTarget.style.boxShadow="0 0 0 3px var(--lucent-accent-subtle)",(k=l.onFocus)==null||k.call(l,m)},onBlur:m=>{var k;m.currentTarget.style.boxShadow="",(k=l.onBlur)==null||k.call(l,m)},...l,children:[i,a?e.jsx(ke,{}):g,!a&&s,!a&&c&&e.jsx(we,{size:n})]})});ee.displayName="Button";function be(t,n){n==="primary"?(t.style.background="var(--lucent-accent-hover)",t.style.borderColor="var(--lucent-accent-hover)"):n==="secondary"?t.style.background="var(--lucent-bg-subtle)":n==="ghost"?t.style.background="var(--lucent-bg-muted)":n==="danger"&&(t.style.background="var(--lucent-danger-hover)",t.style.borderColor="var(--lucent-danger-hover)")}function ye(t,n){n==="primary"?(t.style.background="var(--lucent-accent-default)",t.style.borderColor="var(--lucent-accent-default)"):n==="secondary"?t.style.background="var(--lucent-surface-default)":n==="ghost"?t.style.background="transparent":n==="danger"&&(t.style.background="var(--lucent-danger-default)",t.style.borderColor="var(--lucent-danger-default)")}const xe={sm:12,md:14,lg:16};function we({size:t}){const n=xe[t];return e.jsx("svg",{width:n,height:n,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2.5,strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":!0,style:{flexShrink:0,marginLeft:-2},children:e.jsx("polyline",{points:"6 9 12 15 18 9"})})}function ke(){return e.jsxs("svg",{width:14,height:14,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2.5,strokeLinecap:"round","aria-hidden":!0,style:{animation:"lucent-spin 0.7s linear infinite",flexShrink:0},children:[e.jsx("style",{children:"@keyframes lucent-spin { to { transform: rotate(360deg); } }"}),e.jsx("path",{d:"M12 2a10 10 0 0 1 10 10"})]})}const Se={id:"button",name:"Button",tier:"atom",domain:"neutral",specVersion:"1.0",description:"A clickable control that triggers an action. The primary interactive primitive in Lucent UI.",designIntent:'Buttons communicate available actions. Variant conveys hierarchy: use "primary" for the single most important action in a view, "secondary" for supporting actions, "ghost" for low-emphasis actions in dense UIs, and "danger" exclusively for destructive or irreversible operations. Size should match surrounding content density — prefer "md" as the default and reserve "sm" for toolbars or tables.',props:[{name:"variant",type:"enum",required:!1,default:"primary",description:"Visual style conveying action hierarchy.",enumValues:["primary","secondary","ghost","danger"]},{name:"size",type:"enum",required:!1,default:"md",description:"Controls height and padding.",enumValues:["sm","md","lg"]},{name:"children",type:"ReactNode",required:!0,description:"Button label or content."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Prevents interaction and applies disabled styling."},{name:"loading",type:"boolean",required:!1,default:"false",description:"Shows a spinner and prevents interaction while an async action is in progress."},{name:"fullWidth",type:"boolean",required:!1,default:"false",description:"Stretches the button to fill its container width."},{name:"leftIcon",type:"ReactNode",required:!1,description:"Icon element rendered before the label."},{name:"rightIcon",type:"ReactNode",required:!1,description:"Icon element rendered after the label."},{name:"onClick",type:"function",required:!1,description:"Called when the button is clicked and not disabled or loading."},{name:"type",type:"enum",required:!1,default:"button",description:"Native button type attribute.",enumValues:["button","submit","reset"]}],usageExamples:[{title:"Primary action",code:'<Button variant="primary" onClick={handleSave}>Save changes</Button>'},{title:"Destructive action",code:'<Button variant="danger" onClick={handleDelete}>Delete account</Button>'},{title:"Loading state",code:'<Button variant="primary" loading={isSaving}>Save changes</Button>'},{title:"With icon",code:'<Button variant="secondary" leftIcon={<PlusIcon />}>Add member</Button>'},{title:"Ghost in toolbar",code:'<Button variant="ghost" size="sm">Edit</Button>'},{title:"Full-width submit",code:'<Button variant="primary" type="submit" fullWidth>Sign in</Button>'}],compositionGraph:[],accessibility:{role:"button",ariaAttributes:["aria-disabled","aria-busy"],keyboardInteractions:["Enter — activates the button","Space — activates the button"]}},_=h.forwardRef(({label:t,helperText:n,errorText:a,leftElement:o,rightElement:r,id:i,style:s,...c},f)=>{const g=i??`lucent-input-${Math.random().toString(36).slice(2,7)}`,y=!!a,d=!!c.disabled;return e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"var(--lucent-space-1)",width:"100%"},children:[t&&e.jsx("label",{htmlFor:g,style:{fontSize:"var(--lucent-font-size-sm)",fontWeight:"var(--lucent-font-weight-medium)",color:"var(--lucent-text-primary)",fontFamily:"var(--lucent-font-family-base)"},children:t}),e.jsxs("div",{style:{position:"relative",display:"flex",alignItems:"center"},children:[o&&e.jsx("span",{style:{position:"absolute",left:"var(--lucent-space-3)",color:d?"var(--lucent-text-disabled)":"var(--lucent-text-secondary)",display:"flex",alignItems:"center",pointerEvents:"none"},children:o}),e.jsx("input",{ref:f,id:g,"aria-invalid":y,"aria-describedby":y?`${g}-error`:n?`${g}-helper`:void 0,style:{width:"100%",height:"40px",padding:`0 ${r?"var(--lucent-space-10)":"var(--lucent-space-3)"} 0 ${o?"var(--lucent-space-10)":"var(--lucent-space-3)"}`,fontSize:"var(--lucent-font-size-md)",fontFamily:"var(--lucent-font-family-base)",color:d?"var(--lucent-text-disabled)":"var(--lucent-text-primary)",background:d?"var(--lucent-bg-muted)":"var(--lucent-surface-default)",border:`1px solid ${d?"transparent":y?"var(--lucent-danger-default)":"var(--lucent-border-default)"}`,cursor:d?"not-allowed":void 0,borderRadius:"var(--lucent-radius-lg)",outline:"none",boxSizing:"border-box",transition:"border-color var(--lucent-duration-fast) var(--lucent-easing-default)",...s},onMouseEnter:l=>{var u;!c.disabled&&l.currentTarget!==document.activeElement&&(l.currentTarget.style.borderColor=y?"var(--lucent-danger-default)":"var(--lucent-border-strong)"),(u=c.onMouseEnter)==null||u.call(c,l)},onMouseLeave:l=>{var u;!c.disabled&&l.currentTarget!==document.activeElement&&(l.currentTarget.style.borderColor=y?"var(--lucent-danger-default)":"var(--lucent-border-default)"),(u=c.onMouseLeave)==null||u.call(c,l)},onFocus:l=>{var u;l.currentTarget.style.borderColor=y?"var(--lucent-danger-default)":"var(--lucent-focus-ring)",l.currentTarget.style.boxShadow=`0 0 0 3px ${y?"var(--lucent-danger-subtle)":"var(--lucent-accent-subtle)"}`,(u=c.onFocus)==null||u.call(c,l)},onBlur:l=>{var u;l.currentTarget.style.borderColor=y?"var(--lucent-danger-default)":"var(--lucent-border-default)",l.currentTarget.style.boxShadow="none",(u=c.onBlur)==null||u.call(c,l)},...c}),r&&e.jsx("span",{style:{position:"absolute",right:"var(--lucent-space-3)",color:d?"var(--lucent-text-disabled)":"var(--lucent-text-secondary)",display:"flex",alignItems:"center"},children:r})]}),y&&e.jsx("span",{id:`${g}-error`,role:"alert",style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-danger-text)",fontFamily:"var(--lucent-font-family-base)"},children:a}),!y&&n&&e.jsx("span",{id:`${g}-helper`,style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-text-secondary)",fontFamily:"var(--lucent-font-family-base)"},children:n})]})});_.displayName="Input";const je={id:"input",name:"Input",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A single-line text field with optional label, helper text, and error state.",designIntent:"Always pair with a visible label — never rely on placeholder text alone as it disappears on input and is inaccessible. Use errorText (not helperText) to surface validation failures; the component applies danger styling automatically. leftElement and rightElement accept icons or small controls (e.g. currency symbol, clear button).",props:[{name:"type",type:"enum",required:!1,default:"text",description:"HTML input type.",enumValues:["text","number","password","email","tel","url","search"]},{name:"label",type:"string",required:!1,description:"Visible label rendered above the input."},{name:"helperText",type:"string",required:!1,description:"Supplementary hint shown below the input."},{name:"errorText",type:"string",required:!1,description:"Validation error message. When set, input renders in error state."},{name:"leftElement",type:"ReactNode",required:!1,description:"Icon or adornment rendered inside the left edge."},{name:"rightElement",type:"ReactNode",required:!1,description:"Icon or adornment rendered inside the right edge."},{name:"placeholder",type:"string",required:!1,description:"Placeholder text. Use as a hint, not a label."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Disables the input."},{name:"value",type:"string",required:!1,description:"Controlled value."},{name:"onChange",type:"function",required:!1,description:"Change handler."}],usageExamples:[{title:"Basic",code:'<Input label="Email" type="email" placeholder="you@example.com" />'},{title:"With helper text",code:'<Input label="Username" helperText="3–20 characters, letters and numbers only" />'},{title:"Error state",code:'<Input label="Password" type="password" value={value} errorText="Must be at least 8 characters" />'},{title:"With icon",code:'<Input label="Search" leftElement={<SearchIcon />} placeholder="Search…" />'}],compositionGraph:[],accessibility:{role:"textbox",ariaAttributes:["aria-invalid","aria-describedby","aria-label"],keyboardInteractions:["Tab — focuses the input"]}},te=h.forwardRef(({label:t,helperText:n,errorText:a,autoResize:o=!1,maxLength:r,showCount:i=!1,id:s,value:c,onChange:f,style:g,...y},d)=>{const l=h.useRef(null),u=d??l,v=s??`lucent-textarea-${Math.random().toString(36).slice(2,7)}`,m=!!a,k=typeof c=="string"?c.length:0;return h.useEffect(()=>{if(!o)return;const b=u.current;b&&(b.style.height="auto",b.style.height=`${b.scrollHeight}px`)},[c,o,u]),e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"var(--lucent-space-1)",width:"100%"},children:[t&&e.jsx("label",{htmlFor:v,style:{fontSize:"var(--lucent-font-size-sm)",fontWeight:"var(--lucent-font-weight-medium)",color:"var(--lucent-text-primary)",fontFamily:"var(--lucent-font-family-base)"},children:t}),e.jsx("textarea",{ref:u,id:v,maxLength:r,value:c,onChange:f,"aria-invalid":m,"aria-describedby":m?`${v}-error`:n?`${v}-helper`:void 0,style:{width:"100%",minHeight:"100px",padding:"var(--lucent-space-3)",fontSize:"var(--lucent-font-size-md)",fontFamily:"var(--lucent-font-family-base)",color:"var(--lucent-text-primary)",background:"var(--lucent-surface-default)",border:`1px solid ${m?"var(--lucent-danger-default)":"var(--lucent-border-default)"}`,borderRadius:"var(--lucent-radius-md)",outline:"none",resize:o?"none":"vertical",boxSizing:"border-box",lineHeight:"var(--lucent-line-height-base)",transition:"border-color var(--lucent-duration-fast) var(--lucent-easing-default)",...g},onFocus:b=>{var x;b.currentTarget.style.borderColor=m?"var(--lucent-danger-default)":"var(--lucent-focus-ring)",b.currentTarget.style.boxShadow=`0 0 0 3px ${m?"var(--lucent-danger-subtle)":"var(--lucent-accent-subtle)"}`,(x=y.onFocus)==null||x.call(y,b)},onBlur:b=>{var x;b.currentTarget.style.borderColor=m?"var(--lucent-danger-default)":"var(--lucent-border-default)",b.currentTarget.style.boxShadow="none",(x=y.onBlur)==null||x.call(y,b)},...y}),e.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"flex-start"},children:[e.jsxs("div",{children:[m&&e.jsx("span",{id:`${v}-error`,role:"alert",style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-danger-text)",fontFamily:"var(--lucent-font-family-base)"},children:a}),!m&&n&&e.jsx("span",{id:`${v}-helper`,style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-text-secondary)",fontFamily:"var(--lucent-font-family-base)"},children:n})]}),(i||r)&&e.jsxs("span",{style:{fontSize:"var(--lucent-font-size-xs)",color:r&&k>=r?"var(--lucent-danger-text)":"var(--lucent-text-secondary)",fontFamily:"var(--lucent-font-family-mono)",flexShrink:0,marginLeft:"var(--lucent-space-2)"},children:[k,r?`/${r}`:""]})]})]})});te.displayName="Textarea";const Te={id:"textarea",name:"Textarea",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A multi-line text input with optional auto-resize and character count.",designIntent:"Use autoResize for open-ended fields (bio, description) where content length is unpredictable. Use maxLength + showCount for fields with hard limits (tweet-style). Behaves identically to Input for label/helper/error patterns.",props:[{name:"label",type:"string",required:!1,description:"Visible label above the textarea."},{name:"helperText",type:"string",required:!1,description:"Hint text shown below."},{name:"errorText",type:"string",required:!1,description:"Validation error. Triggers error styling."},{name:"autoResize",type:"boolean",required:!1,default:"false",description:"Grows with content, disables manual resize handle."},{name:"maxLength",type:"number",required:!1,description:"Character limit. Displays counter when set."},{name:"showCount",type:"boolean",required:!1,default:"false",description:"Always show character counter even without maxLength."},{name:"value",type:"string",required:!1,description:"Controlled value."},{name:"onChange",type:"function",required:!1,description:"Change handler."},{name:"placeholder",type:"string",required:!1,description:"Placeholder text."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Disables the textarea."}],usageExamples:[{title:"Basic",code:'<Textarea label="Bio" placeholder="Tell us about yourself…" />'},{title:"Auto-resize",code:'<Textarea label="Description" autoResize value={value} onChange={e => setValue(e.target.value)} />'},{title:"With character count",code:'<Textarea label="Tweet" maxLength={280} showCount value={value} onChange={e => setValue(e.target.value)} />'},{title:"Error state",code:'<Textarea label="Notes" errorText="Required" value="" />'}],compositionGraph:[],accessibility:{role:"textbox",ariaAttributes:["aria-multiline","aria-invalid","aria-describedby"],keyboardInteractions:["Tab — focuses the textarea"]}},Ce={neutral:{bg:"var(--lucent-bg-muted)",color:"var(--lucent-text-secondary)",border:"var(--lucent-border-default)"},accent:{bg:"var(--lucent-accent-default)",color:"var(--lucent-text-on-accent)",border:"var(--lucent-accent-default)"},success:{bg:"var(--lucent-success-subtle)",color:"var(--lucent-success-text)",border:"var(--lucent-success-subtle)"},warning:{bg:"var(--lucent-warning-subtle)",color:"var(--lucent-warning-text)",border:"var(--lucent-warning-subtle)"},danger:{bg:"var(--lucent-danger-subtle)",color:"var(--lucent-danger-text)",border:"var(--lucent-danger-subtle)"},info:{bg:"var(--lucent-info-subtle)",color:"var(--lucent-info-text)",border:"var(--lucent-info-subtle)"}},Ie={sm:{fontSize:"var(--lucent-font-size-xs)",padding:"0 var(--lucent-space-2)",height:"18px"},md:{fontSize:"var(--lucent-font-size-sm)",padding:"0 var(--lucent-space-2)",height:"22px"}};function Me({variant:t="neutral",size:n="md",dot:a=!1,children:o,style:r}){const i=Ce[t],s=Ie[n];return e.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:"var(--lucent-space-1)",height:s.height,padding:s.padding,fontSize:s.fontSize,fontFamily:"var(--lucent-font-family-base)",fontWeight:"var(--lucent-font-weight-medium)",lineHeight:1,borderRadius:"var(--lucent-radius-full)",background:i.bg,color:i.color,border:`1px solid ${i.border}`,whiteSpace:"nowrap",boxSizing:"border-box",...r},children:[a&&e.jsx("span",{style:{width:6,height:6,borderRadius:"var(--lucent-radius-full)",background:"currentColor",flexShrink:0}}),o]})}const ze={id:"badge",name:"Badge",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A small inline label for status, count, or category.",designIntent:'Badges communicate status or category at a glance. Match variant to semantic meaning — never use "danger" for non-critical states or "success" for neutral counts. Use dot=true when a single colour indicator is enough context (e.g. online status). Keep badge text short: 1–3 words maximum.',props:[{name:"variant",type:"enum",required:!1,default:"neutral",description:"Colour scheme conveying semantic meaning.",enumValues:["neutral","success","warning","danger","info","accent"]},{name:"size",type:"enum",required:!1,default:"md",description:"Controls height and font size.",enumValues:["sm","md"]},{name:"dot",type:"boolean",required:!1,default:"false",description:"Prepends a coloured dot indicator."},{name:"children",type:"ReactNode",required:!0,description:"Badge label."}],usageExamples:[{title:"Status",code:'<Badge variant="success" dot>Active</Badge>'},{title:"Count",code:'<Badge variant="danger">12</Badge>'},{title:"Category",code:'<Badge variant="info">Beta</Badge>'},{title:"Neutral tag",code:"<Badge>Draft</Badge>"}],compositionGraph:[],accessibility:{role:"status",notes:"Use aria-label on the parent element when badge meaning depends on context."}},Ee={xs:24,sm:32,md:40,lg:56,xl:80},qe={xs:"var(--lucent-font-size-xs)",sm:"var(--lucent-font-size-xs)",md:"var(--lucent-font-size-sm)",lg:"var(--lucent-font-size-lg)",xl:"var(--lucent-font-size-xl)"};function De(t,n){var o,r,i;if(n)return n.slice(0,2).toUpperCase();const a=t.trim().split(/\s+/);return a.length===1?(((o=a[0])==null?void 0:o[0])??"").toUpperCase():((((r=a[0])==null?void 0:r[0])??"")+(((i=a[a.length-1])==null?void 0:i[0])??"")).toUpperCase()}function Ae({src:t,alt:n,size:a="md",initials:o,style:r,...i}){const s=Ee[a],c=De(n,o),f={width:s,height:s,borderRadius:"var(--lucent-radius-full)",flexShrink:0,display:"inline-flex",alignItems:"center",justifyContent:"center",overflow:"hidden",boxSizing:"border-box",userSelect:"none",...r};return t?e.jsx("img",{src:t,alt:n,width:s,height:s,style:{...f,objectFit:"cover"},...i}):e.jsx("span",{role:"img","aria-label":n,style:{...f,background:"var(--lucent-accent-default)",color:"var(--lucent-text-on-accent)",fontSize:qe[a],fontWeight:"var(--lucent-font-weight-semibold)",fontFamily:"var(--lucent-font-family-base)"},children:c})}const Fe={id:"avatar",name:"Avatar",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A circular user image with initials fallback.",designIntent:"Always provide alt for accessibility — it is used to derive initials automatically when src is absent or fails. Use initials prop to override auto-derived initials (e.g. for non-Latin names). Size xs/sm suit table rows and compact lists; md is the default for comment threads; lg/xl for profile headers.",props:[{name:"src",type:"string",required:!1,description:"Image URL. Falls back to initials if omitted or fails to load."},{name:"alt",type:"string",required:!0,description:"Alt text and source for auto-derived initials."},{name:"size",type:"enum",required:!1,default:"md",description:"Diameter of the avatar.",enumValues:["xs","sm","md","lg","xl"]},{name:"initials",type:"string",required:!1,description:"Override auto-derived initials (max 2 characters)."}],usageExamples:[{title:"With image",code:'<Avatar src="/avatars/jane.jpg" alt="Jane Doe" />'},{title:"Initials fallback",code:'<Avatar alt="Jane Doe" />'},{title:"Large profile",code:'<Avatar src={user.avatar} alt={user.name} size="lg" />'},{title:"Custom initials",code:'<Avatar alt="张伟" initials="张" size="md" />'}],compositionGraph:[],accessibility:{role:"img",ariaAttributes:["aria-label"],notes:'When src is present, renders as <img> with alt. When showing initials, renders as <span role="img" aria-label>.'}},Re={xs:12,sm:16,md:24,lg:36},Ne={xs:2.5,sm:2.5,md:2,lg:2};function ne({size:t="md",label:n="Loading…",color:a}){const o=Re[t],r=Ne[t];return e.jsxs("span",{role:"status","aria-label":n,style:{display:"inline-flex",alignItems:"center",justifyContent:"center"},children:[e.jsxs("svg",{width:o,height:o,viewBox:"0 0 24 24",fill:"none","aria-hidden":!0,style:{animation:"lucent-spin 0.7s linear infinite",color:a??"currentColor"},children:[e.jsx("style",{children:"@keyframes lucent-spin { to { transform: rotate(360deg); } }"}),e.jsx("circle",{cx:12,cy:12,r:10,stroke:"currentColor",strokeWidth:r,strokeOpacity:.2}),e.jsx("path",{d:"M12 2a10 10 0 0 1 10 10",stroke:"currentColor",strokeWidth:r,strokeLinecap:"round"})]}),e.jsx("span",{style:{position:"absolute",width:1,height:1,overflow:"hidden",clip:"rect(0,0,0,0)",whiteSpace:"nowrap"},children:n})]})}const Le={id:"spinner",name:"Spinner",tier:"atom",domain:"neutral",specVersion:"0.1",description:"An animated loading indicator for async operations.",designIntent:"Use Spinner for indeterminate loading states of short duration (< 3s). For full-page or skeleton-level loading, prefer Skeleton instead. The label prop is visually hidden but read by screen readers — always set it to a meaningful description of what is loading.",props:[{name:"size",type:"enum",required:!1,default:"md",description:"Spinner diameter.",enumValues:["xs","sm","md","lg"]},{name:"label",type:"string",required:!1,default:"Loading…",description:"Visually hidden accessible label."},{name:"color",type:"string",required:!1,description:"Override colour (CSS value). Defaults to currentColor."}],usageExamples:[{title:"Default",code:"<Spinner />"},{title:"Inside button",code:'<Button loading><Spinner size="sm" label="Saving…" /></Button>'},{title:"Full-page overlay",code:`<div style={{ display: 'grid', placeItems: 'center', minHeight: '100vh' }}><Spinner size="lg" label="Loading dashboard…" /></div>`}],compositionGraph:[],accessibility:{role:"status",ariaAttributes:["aria-label"],notes:'The visible SVG is aria-hidden. The label is conveyed via a visually-hidden span inside role="status".'}};function Be({orientation:t="horizontal",label:n,spacing:a="var(--lucent-space-4)",style:o}){return t==="vertical"?e.jsx("span",{role:"separator","aria-orientation":"vertical",style:{display:"inline-block",width:"1px",alignSelf:"stretch",background:"var(--lucent-border-default)",margin:`0 ${a}`,flexShrink:0,...o}}):n?e.jsxs("div",{role:"separator","aria-label":n,style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-3)",margin:`${a} 0`,...o},children:[e.jsx("span",{style:{flex:1,height:"1px",background:"var(--lucent-border-default)"}}),e.jsx("span",{style:{fontSize:"var(--lucent-font-size-xs)",fontFamily:"var(--lucent-font-family-base)",color:"var(--lucent-text-secondary)",whiteSpace:"nowrap",letterSpacing:"var(--lucent-letter-spacing-wide)",textTransform:"uppercase"},children:n}),e.jsx("span",{style:{flex:1,height:"1px",background:"var(--lucent-border-default)"}})]}):e.jsx("hr",{role:"separator",style:{border:"none",borderTop:"1px solid var(--lucent-border-default)",margin:`${a} 0`,width:"100%",...o}})}const $e={id:"divider",name:"Divider",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A visual separator between content sections, horizontal or vertical.",designIntent:'Use horizontal Divider to separate sections in a layout. Use vertical Divider inline between sibling elements (e.g. nav links, toolbar buttons). Use the label prop for "OR" separators in auth flows or form sections — never use a plain text node next to a divider for this.',props:[{name:"orientation",type:"enum",required:!1,default:"horizontal",description:"Direction of the divider line.",enumValues:["horizontal","vertical"]},{name:"label",type:"string",required:!1,description:'Optional centered label (horizontal only). Common use: "OR", "AND", section titles.'},{name:"spacing",type:"string",required:!1,default:"var(--lucent-space-4)",description:"Margin on the axis perpendicular to the line."}],usageExamples:[{title:"Section separator",code:"<Divider />"},{title:"With label",code:'<Divider label="OR" />'},{title:"Vertical in nav",code:`<nav style={{ display: 'flex', alignItems: 'center' }}><a>Home</a><Divider orientation="vertical" /><a>About</a></nav>`}],compositionGraph:[],accessibility:{role:"separator",ariaAttributes:["aria-orientation","aria-label"]}},Pe={sm:14,md:16},We=`
2
2
  @keyframes lucent-cb-pop {
3
3
  0% { transform: scale(1); }
4
4
  35% { transform: scale(0.82); }
@@ -10,7 +10,7 @@
10
10
  60% { transform: scale(1.15) rotate(2deg); }
11
11
  100% { opacity: 1; transform: scale(1) rotate(0deg); }
12
12
  }
13
- `,ae=h.forwardRef(({label:t,size:n="md",indeterminate:a=!1,checked:o,defaultChecked:r,disabled:i,id:s,onChange:c,style:f,...g},y)=>{const d=h.useRef(null),l=s??`lucent-checkbox-${Math.random().toString(36).slice(2,7)}`,p=Pe[n],v=o!==void 0,[m,T]=h.useState(r??!1),b=v?!!o:m,x=h.useRef(b),[M,S]=h.useState(0);h.useEffect(()=>{!i&&x.current!==b&&(x.current=b,S(k=>k+1))},[b,i]);const u=h.useCallback(k=>{d.current=k,typeof y=="function"?y(k):y&&(y.current=k)},[y]);h.useEffect(()=>{d.current&&(d.current.indeterminate=a)},[a]);const j=k=>{v||T(k.target.checked),c==null||c(k)},E=i?"var(--lucent-text-disabled)":"var(--lucent-text-on-accent)",w={width:p,height:p,borderRadius:"var(--lucent-radius-sm)",border:`1.5px solid ${i?"transparent":b||a?"var(--lucent-accent-default)":"var(--lucent-border-strong)"}`,background:i?"var(--lucent-bg-muted)":b||a?"var(--lucent-accent-default)":"var(--lucent-surface-default)",display:"inline-flex",alignItems:"center",justifyContent:"center",flexShrink:0,transition:"background var(--lucent-duration-fast) var(--lucent-easing-default), border-color var(--lucent-duration-fast) var(--lucent-easing-default)",animation:M>0?"lucent-cb-pop 220ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards":void 0};return e.jsxs(e.Fragment,{children:[e.jsx("style",{children:We}),e.jsxs("label",{style:{display:"inline-flex",alignItems:"center",gap:"var(--lucent-space-2)",cursor:i?"not-allowed":"pointer",fontFamily:"var(--lucent-font-family-base)",fontSize:n==="sm"?"var(--lucent-font-size-sm)":"var(--lucent-font-size-md)",color:i?"var(--lucent-text-disabled)":"var(--lucent-text-primary)",userSelect:"none",...f},children:[e.jsx("input",{ref:u,type:"checkbox",id:l,checked:v?o:m,disabled:i,onChange:j,style:{position:"absolute",opacity:0,width:0,height:0,margin:0,pointerEvents:"none"},...g}),e.jsxs("span",{"aria-hidden":!0,style:w,children:[b&&!a&&e.jsx("svg",{width:p-4,height:p-4,viewBox:"0 0 10 10",fill:"none",style:{animation:"lucent-cb-mark 200ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards"},children:e.jsx("path",{d:"M1.5 5L4 7.5L8.5 2.5",stroke:E,strokeWidth:1.5,strokeLinecap:"round",strokeLinejoin:"round"})}),a&&e.jsx("svg",{width:p-4,height:p-4,viewBox:"0 0 10 10",fill:"none",style:{animation:"lucent-cb-mark 200ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards"},children:e.jsx("path",{d:"M2 5H8",stroke:E,strokeWidth:1.5,strokeLinecap:"round"})})]},M),t]})]})});ae.displayName="Checkbox";const Ve={id:"checkbox",name:"Checkbox",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A binary selection control for boolean values or multi-select lists.",designIntent:'Checkboxes represent independent boolean choices — they do not affect each other. Use a Checkbox for settings that take effect immediately (e.g. "Remember me") or for selecting multiple items from a list. When only one option may be active at a time, use Radio instead. The indeterminate state communicates a "select all" parent whose children are partially checked — never use it for a third logical state.',props:[{name:"checked",type:"boolean",required:!1,description:"Controlled checked state. Pair with onChange for controlled usage."},{name:"defaultChecked",type:"boolean",required:!1,default:"false",description:"Initial checked state for uncontrolled usage."},{name:"onChange",type:"function",required:!1,description:"Called when the checked state changes."},{name:"label",type:"string",required:!1,description:"Visible label rendered beside the checkbox."},{name:"indeterminate",type:"boolean",required:!1,default:"false",description:"Displays a dash to indicate a partially-checked parent state."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Prevents interaction and dims the control."},{name:"size",type:"enum",required:!1,default:"md",description:"Size of the checkbox box.",enumValues:["sm","md"]}],usageExamples:[{title:"Controlled",code:'<Checkbox checked={agreed} onChange={e => setAgreed(e.target.checked)} label="I agree to the terms" />'},{title:"Uncontrolled",code:'<Checkbox defaultChecked label="Send me updates" />'},{title:"Indeterminate",code:'<Checkbox indeterminate label="Select all" />'},{title:"Disabled",code:'<Checkbox disabled label="Unavailable option" />'}],compositionGraph:[],accessibility:{role:"checkbox",ariaAttributes:["aria-checked","aria-disabled"],keyboardInteractions:["Space — toggles checked state"]}},Oe=`
13
+ `,ae=h.forwardRef(({label:t,size:n="md",indeterminate:a=!1,checked:o,defaultChecked:r,disabled:i,id:s,onChange:c,style:f,...g},y)=>{const d=h.useRef(null),l=s??`lucent-checkbox-${Math.random().toString(36).slice(2,7)}`,u=Pe[n],v=o!==void 0,[m,k]=h.useState(r??!1),b=v?!!o:m,x=h.useRef(b),[M,j]=h.useState(0);h.useEffect(()=>{!i&&x.current!==b&&(x.current=b,j(S=>S+1))},[b,i]);const p=h.useCallback(S=>{d.current=S,typeof y=="function"?y(S):y&&(y.current=S)},[y]);h.useEffect(()=>{d.current&&(d.current.indeterminate=a)},[a]);const T=S=>{v||k(S.target.checked),c==null||c(S)},E=i?"var(--lucent-text-disabled)":"var(--lucent-text-on-accent)",w={width:u,height:u,borderRadius:"var(--lucent-radius-sm)",border:`1.5px solid ${i?"transparent":b||a?"var(--lucent-accent-default)":"var(--lucent-border-strong)"}`,background:i?"var(--lucent-bg-muted)":b||a?"var(--lucent-accent-default)":"var(--lucent-surface-default)",display:"inline-flex",alignItems:"center",justifyContent:"center",flexShrink:0,transition:"background var(--lucent-duration-fast) var(--lucent-easing-default), border-color var(--lucent-duration-fast) var(--lucent-easing-default)",animation:M>0?"lucent-cb-pop 220ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards":void 0};return e.jsxs(e.Fragment,{children:[e.jsx("style",{children:We}),e.jsxs("label",{style:{display:"inline-flex",alignItems:"center",gap:"var(--lucent-space-2)",cursor:i?"not-allowed":"pointer",fontFamily:"var(--lucent-font-family-base)",fontSize:n==="sm"?"var(--lucent-font-size-sm)":"var(--lucent-font-size-md)",color:i?"var(--lucent-text-disabled)":"var(--lucent-text-primary)",userSelect:"none",...f},children:[e.jsx("input",{ref:p,type:"checkbox",id:l,checked:v?o:m,disabled:i,onChange:T,style:{position:"absolute",opacity:0,width:0,height:0,margin:0,pointerEvents:"none"},...g}),e.jsxs("span",{"aria-hidden":!0,style:w,children:[b&&!a&&e.jsx("svg",{width:u-4,height:u-4,viewBox:"0 0 10 10",fill:"none",style:{animation:"lucent-cb-mark 200ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards"},children:e.jsx("path",{d:"M1.5 5L4 7.5L8.5 2.5",stroke:E,strokeWidth:1.5,strokeLinecap:"round",strokeLinejoin:"round"})}),a&&e.jsx("svg",{width:u-4,height:u-4,viewBox:"0 0 10 10",fill:"none",style:{animation:"lucent-cb-mark 200ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards"},children:e.jsx("path",{d:"M2 5H8",stroke:E,strokeWidth:1.5,strokeLinecap:"round"})})]},M),t]})]})});ae.displayName="Checkbox";const Ve={id:"checkbox",name:"Checkbox",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A binary selection control for boolean values or multi-select lists.",designIntent:'Checkboxes represent independent boolean choices — they do not affect each other. Use a Checkbox for settings that take effect immediately (e.g. "Remember me") or for selecting multiple items from a list. When only one option may be active at a time, use Radio instead. The indeterminate state communicates a "select all" parent whose children are partially checked — never use it for a third logical state.',props:[{name:"checked",type:"boolean",required:!1,description:"Controlled checked state. Pair with onChange for controlled usage."},{name:"defaultChecked",type:"boolean",required:!1,default:"false",description:"Initial checked state for uncontrolled usage."},{name:"onChange",type:"function",required:!1,description:"Called when the checked state changes."},{name:"label",type:"string",required:!1,description:"Visible label rendered beside the checkbox."},{name:"indeterminate",type:"boolean",required:!1,default:"false",description:"Displays a dash to indicate a partially-checked parent state."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Prevents interaction and dims the control."},{name:"size",type:"enum",required:!1,default:"md",description:"Size of the checkbox box.",enumValues:["sm","md"]}],usageExamples:[{title:"Controlled",code:'<Checkbox checked={agreed} onChange={e => setAgreed(e.target.checked)} label="I agree to the terms" />'},{title:"Uncontrolled",code:'<Checkbox defaultChecked label="Send me updates" />'},{title:"Indeterminate",code:'<Checkbox indeterminate label="Select all" />'},{title:"Disabled",code:'<Checkbox disabled label="Unavailable option" />'}],compositionGraph:[],accessibility:{role:"checkbox",ariaAttributes:["aria-checked","aria-disabled"],keyboardInteractions:["Space — toggles checked state"]}},Oe=`
14
14
  @keyframes lucent-radio-pop {
15
15
  0% { transform: scale(1); }
16
16
  35% { transform: scale(0.82); }
@@ -22,7 +22,7 @@
22
22
  60% { transform: scale(1.2); }
23
23
  100% { opacity: 1; transform: scale(1); }
24
24
  }
25
- `,re=h.createContext(null);function oe({name:t,value:n,onChange:a,disabled:o,orientation:r="vertical",label:i,children:s}){return e.jsx(re.Provider,{value:{name:t,value:n,onChange:a,disabled:o??!1},children:e.jsx("div",{role:"radiogroup","aria-label":i,style:{display:"flex",flexDirection:r==="vertical"?"column":"row",gap:r==="vertical"?"var(--lucent-space-3)":"var(--lucent-space-4)",flexWrap:"wrap"},children:s})})}const He={sm:14,md:16};function Ue({value:t,label:n,size:a="md",disabled:o,id:r,onChange:i,checked:s,...c}){const f=h.useContext(re),g=r??`lucent-radio-${Math.random().toString(36).slice(2,7)}`,y=He[a],d=o??(f==null?void 0:f.disabled)??!1,l=f?f.value===t:!!s,p=h.useRef(l),[v,m]=h.useState(0);h.useEffect(()=>{!d&&p.current!==l&&(p.current=l,m(M=>M+1))},[l,d]);const T=M=>{f==null||f.onChange(t),i==null||i(M)},b={width:y/2,height:y/2,borderRadius:"50%",background:d?"var(--lucent-text-disabled)":"var(--lucent-text-on-accent)",animation:l?"lucent-radio-dot 200ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards":void 0,opacity:l?1:0},x={width:y,height:y,borderRadius:"50%",border:`1.5px solid ${d?"transparent":l?"var(--lucent-accent-default)":"var(--lucent-border-strong)"}`,background:d?"var(--lucent-bg-muted)":l?"var(--lucent-accent-default)":"var(--lucent-surface-default)",display:"inline-flex",alignItems:"center",justifyContent:"center",flexShrink:0,transition:"background var(--lucent-duration-fast) var(--lucent-easing-default), border-color var(--lucent-duration-fast) var(--lucent-easing-default)",animation:v>0?"lucent-radio-pop 220ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards":void 0};return e.jsxs(e.Fragment,{children:[e.jsx("style",{children:Oe}),e.jsxs("label",{style:{display:"inline-flex",alignItems:"center",gap:"var(--lucent-space-2)",cursor:d?"not-allowed":"pointer",fontFamily:"var(--lucent-font-family-base)",fontSize:a==="sm"?"var(--lucent-font-size-sm)":"var(--lucent-font-size-md)",color:d?"var(--lucent-text-disabled)":"var(--lucent-text-primary)",userSelect:"none"},children:[e.jsx("input",{type:"radio",id:g,value:t,name:(f==null?void 0:f.name)??c.name,checked:l,disabled:d,onChange:T,style:{position:"absolute",opacity:0,width:0,height:0,margin:0,pointerEvents:"none"},...c}),e.jsx("span",{"aria-hidden":!0,style:x,children:e.jsx("span",{style:b})},v),n]})]})}function Ge({defaultValue:t="",onChange:n,...a}){const[o,r]=h.useState(t);return e.jsx(oe,{...a,value:o,onChange:i=>{r(i),n==null||n(i)}})}const _e={id:"radio",name:"Radio",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A mutually exclusive selection control. Use RadioGroup to manage a set of options.",designIntent:"Radio buttons enforce a single selection from a small set of options (typically 2–6). Always wrap Radio in a RadioGroup so name and selection state are shared automatically. For larger option sets (7+) prefer a Select. For independent true/false choices, use a Checkbox. RadioGroup orientation should match how the options relate — vertical for distinct choices, horizontal for brief inline options (e.g. Yes / No).",props:[{name:"value",type:"string",required:!0,description:"The value submitted when this radio is selected."},{name:"label",type:"string",required:!1,description:"Visible label rendered beside the radio button."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Prevents interaction. Inherits group-level disabled when inside RadioGroup."},{name:"size",type:"enum",required:!1,default:"md",description:"Size of the radio button circle.",enumValues:["sm","md"]}],usageExamples:[{title:"Controlled RadioGroup",code:`
25
+ `,re=h.createContext(null);function oe({name:t,value:n,onChange:a,disabled:o,orientation:r="vertical",label:i,children:s}){return e.jsx(re.Provider,{value:{name:t,value:n,onChange:a,disabled:o??!1},children:e.jsx("div",{role:"radiogroup","aria-label":i,style:{display:"flex",flexDirection:r==="vertical"?"column":"row",gap:r==="vertical"?"var(--lucent-space-3)":"var(--lucent-space-4)",flexWrap:"wrap"},children:s})})}const He={sm:14,md:16};function Ue({value:t,label:n,size:a="md",disabled:o,id:r,onChange:i,checked:s,...c}){const f=h.useContext(re),g=r??`lucent-radio-${Math.random().toString(36).slice(2,7)}`,y=He[a],d=o??(f==null?void 0:f.disabled)??!1,l=f?f.value===t:!!s,u=h.useRef(l),[v,m]=h.useState(0);h.useEffect(()=>{!d&&u.current!==l&&(u.current=l,m(M=>M+1))},[l,d]);const k=M=>{f==null||f.onChange(t),i==null||i(M)},b={width:y/2,height:y/2,borderRadius:"50%",background:d?"var(--lucent-text-disabled)":"var(--lucent-text-on-accent)",animation:l?"lucent-radio-dot 200ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards":void 0,opacity:l?1:0},x={width:y,height:y,borderRadius:"50%",border:`1.5px solid ${d?"transparent":l?"var(--lucent-accent-default)":"var(--lucent-border-strong)"}`,background:d?"var(--lucent-bg-muted)":l?"var(--lucent-accent-default)":"var(--lucent-surface-default)",display:"inline-flex",alignItems:"center",justifyContent:"center",flexShrink:0,transition:"background var(--lucent-duration-fast) var(--lucent-easing-default), border-color var(--lucent-duration-fast) var(--lucent-easing-default)",animation:v>0?"lucent-radio-pop 220ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards":void 0};return e.jsxs(e.Fragment,{children:[e.jsx("style",{children:Oe}),e.jsxs("label",{style:{display:"inline-flex",alignItems:"center",gap:"var(--lucent-space-2)",cursor:d?"not-allowed":"pointer",fontFamily:"var(--lucent-font-family-base)",fontSize:a==="sm"?"var(--lucent-font-size-sm)":"var(--lucent-font-size-md)",color:d?"var(--lucent-text-disabled)":"var(--lucent-text-primary)",userSelect:"none"},children:[e.jsx("input",{type:"radio",id:g,value:t,name:(f==null?void 0:f.name)??c.name,checked:l,disabled:d,onChange:k,style:{position:"absolute",opacity:0,width:0,height:0,margin:0,pointerEvents:"none"},...c}),e.jsx("span",{"aria-hidden":!0,style:x,children:e.jsx("span",{style:b})},v),n]})]})}function Ge({defaultValue:t="",onChange:n,...a}){const[o,r]=h.useState(t);return e.jsx(oe,{...a,value:o,onChange:i=>{r(i),n==null||n(i)}})}const _e={id:"radio",name:"Radio",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A mutually exclusive selection control. Use RadioGroup to manage a set of options.",designIntent:"Radio buttons enforce a single selection from a small set of options (typically 2–6). Always wrap Radio in a RadioGroup so name and selection state are shared automatically. For larger option sets (7+) prefer a Select. For independent true/false choices, use a Checkbox. RadioGroup orientation should match how the options relate — vertical for distinct choices, horizontal for brief inline options (e.g. Yes / No).",props:[{name:"value",type:"string",required:!0,description:"The value submitted when this radio is selected."},{name:"label",type:"string",required:!1,description:"Visible label rendered beside the radio button."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Prevents interaction. Inherits group-level disabled when inside RadioGroup."},{name:"size",type:"enum",required:!1,default:"md",description:"Size of the radio button circle.",enumValues:["sm","md"]}],usageExamples:[{title:"Controlled RadioGroup",code:`
26
26
  <RadioGroup name="plan" value={plan} onChange={setPlan}>
27
27
  <Radio value="free" label="Free" />
28
28
  <Radio value="pro" label="Pro" />
@@ -36,16 +36,16 @@
36
36
  <RadioGroup name="tier" value="basic" onChange={() => {}} disabled>
37
37
  <Radio value="basic" label="Basic" />
38
38
  <Radio value="advanced" label="Advanced" />
39
- </RadioGroup>`.trim()}],compositionGraph:[{componentId:"radio-group",componentName:"RadioGroup",role:"container",required:!0},{componentId:"radio",componentName:"Radio",role:"item",required:!0}],accessibility:{role:"radio",ariaAttributes:["aria-checked","aria-disabled"],keyboardInteractions:["Arrow keys — move selection within the group","Space — selects the focused radio"]}},Ye={sm:{track:[28,16],thumb:12},md:{track:[36,20],thumb:16},lg:{track:[44,24],thumb:20}},Y="cubic-bezier(0.34, 1.56, 0.64, 1)",Ke=`
39
+ </RadioGroup>`.trim()}],compositionGraph:[{componentId:"radio-group",componentName:"RadioGroup",role:"container",required:!0},{componentId:"radio",componentName:"Radio",role:"item",required:!0}],accessibility:{role:"radio",ariaAttributes:["aria-checked","aria-disabled"],keyboardInteractions:["Arrow keys — move selection within the group","Space — selects the focused radio"]}},Ye={sm:{track:[28,16],thumb:12},md:{track:[36,20],thumb:16},lg:{track:[44,24],thumb:20}},K="cubic-bezier(0.34, 1.56, 0.64, 1)",Ke=`
40
40
  @keyframes lucent-toggle-pop {
41
41
  0% { transform: scale(1); }
42
42
  35% { transform: scale(0.94); }
43
43
  70% { transform: scale(1.06); }
44
44
  100% { transform: scale(1); }
45
45
  }
46
- `;function Xe({label:t,size:n="md",checked:a,defaultChecked:o,disabled:r,id:i,onChange:s,style:c,...f}){const g=i??`lucent-toggle-${Math.random().toString(36).slice(2,7)}`,y=a!==void 0,[d,l]=h.useState(o??!1),p=y?!!a:d,v=h.useRef(p),[m,T]=h.useState(0);h.useEffect(()=>{!r&&v.current!==p&&(v.current=p,T(j=>j+1))},[p,r]);const{track:[b,x],thumb:M}=Ye[n],S=p?b-M-2:2,u=j=>{y||l(j.target.checked),s==null||s(j)};return e.jsxs(e.Fragment,{children:[e.jsx("style",{children:Ke}),e.jsxs("label",{style:{display:"inline-flex",alignItems:"center",gap:"var(--lucent-space-2)",cursor:r?"not-allowed":"pointer",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-md)",color:r?"var(--lucent-text-disabled)":"var(--lucent-text-primary)",userSelect:"none",...c},children:[e.jsx("input",{type:"checkbox",role:"switch",id:g,checked:y?a:d,disabled:r,onChange:u,"aria-checked":p,style:{position:"absolute",opacity:0,width:0,height:0,margin:0,pointerEvents:"none"},...f}),e.jsx("span",{"aria-hidden":!0,style:{position:"relative",width:b,height:x,borderRadius:x/2,background:r?"var(--lucent-bg-muted)":p?"var(--lucent-accent-default)":"var(--lucent-border-strong)",flexShrink:0,transition:"background var(--lucent-duration-fast) var(--lucent-easing-default)",animation:m>0?`lucent-toggle-pop 240ms ${Y} forwards`:void 0},children:e.jsx("span",{style:{position:"absolute",top:2,left:S,width:M,height:M,borderRadius:"50%",background:"#ffffff",boxShadow:"0 1px 3px rgb(0 0 0 / 0.2)",transition:`left 260ms ${Y}`}})},m),t]})]})}const Je={id:"toggle",name:"Toggle",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A sliding switch for immediately-applied binary settings.",designIntent:'Toggles are for settings that take effect the moment they are flipped — no Save button needed. If the action requires a confirmation step or only applies on form submit, use a Checkbox instead. The "on" state is visually represented by the accent colour so the meaning is clear without relying on text alone. Keep the label short (2–4 words) and phrase it as the enabled state (e.g. "Dark mode", not "Enable dark mode").',props:[{name:"checked",type:"boolean",required:!1,description:"Controlled on/off state. Pair with onChange for controlled usage."},{name:"defaultChecked",type:"boolean",required:!1,default:"false",description:"Initial state for uncontrolled usage."},{name:"onChange",type:"function",required:!1,description:"Called when the toggle is flipped."},{name:"label",type:"string",required:!1,description:"Visible label rendered beside the toggle."},{name:"size",type:"enum",required:!1,default:"md",description:"Controls the track and thumb size.",enumValues:["sm","md","lg"]},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Prevents interaction and dims the control."}],usageExamples:[{title:"Controlled",code:'<Toggle checked={darkMode} onChange={e => setDarkMode(e.target.checked)} label="Dark mode" />'},{title:"Uncontrolled",code:'<Toggle defaultChecked label="Email notifications" />'},{title:"Sizes",code:`<Toggle size="sm" label="Compact" />
46
+ `;function Xe({label:t,size:n="md",checked:a,defaultChecked:o,disabled:r,id:i,onChange:s,style:c,...f}){const g=i??`lucent-toggle-${Math.random().toString(36).slice(2,7)}`,y=a!==void 0,[d,l]=h.useState(o??!1),u=y?!!a:d,v=h.useRef(u),[m,k]=h.useState(0);h.useEffect(()=>{!r&&v.current!==u&&(v.current=u,k(T=>T+1))},[u,r]);const{track:[b,x],thumb:M}=Ye[n],j=u?b-M-2:2,p=T=>{y||l(T.target.checked),s==null||s(T)};return e.jsxs(e.Fragment,{children:[e.jsx("style",{children:Ke}),e.jsxs("label",{style:{display:"inline-flex",alignItems:"center",gap:"var(--lucent-space-2)",cursor:r?"not-allowed":"pointer",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-md)",color:r?"var(--lucent-text-disabled)":"var(--lucent-text-primary)",userSelect:"none",...c},children:[e.jsx("input",{type:"checkbox",role:"switch",id:g,checked:y?a:d,disabled:r,onChange:p,"aria-checked":u,style:{position:"absolute",opacity:0,width:0,height:0,margin:0,pointerEvents:"none"},...f}),e.jsx("span",{"aria-hidden":!0,style:{position:"relative",width:b,height:x,borderRadius:x/2,background:r?"var(--lucent-bg-muted)":u?"var(--lucent-accent-default)":"var(--lucent-border-strong)",flexShrink:0,transition:"background var(--lucent-duration-fast) var(--lucent-easing-default)",animation:m>0?`lucent-toggle-pop 240ms ${K} forwards`:void 0},children:e.jsx("span",{style:{position:"absolute",top:2,left:j,width:M,height:M,borderRadius:"50%",background:"#ffffff",boxShadow:"0 1px 3px rgb(0 0 0 / 0.2)",transition:`left 260ms ${K}`}})},m),t]})]})}const Je={id:"toggle",name:"Toggle",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A sliding switch for immediately-applied binary settings.",designIntent:'Toggles are for settings that take effect the moment they are flipped — no Save button needed. If the action requires a confirmation step or only applies on form submit, use a Checkbox instead. The "on" state is visually represented by the accent colour so the meaning is clear without relying on text alone. Keep the label short (2–4 words) and phrase it as the enabled state (e.g. "Dark mode", not "Enable dark mode").',props:[{name:"checked",type:"boolean",required:!1,description:"Controlled on/off state. Pair with onChange for controlled usage."},{name:"defaultChecked",type:"boolean",required:!1,default:"false",description:"Initial state for uncontrolled usage."},{name:"onChange",type:"function",required:!1,description:"Called when the toggle is flipped."},{name:"label",type:"string",required:!1,description:"Visible label rendered beside the toggle."},{name:"size",type:"enum",required:!1,default:"md",description:"Controls the track and thumb size.",enumValues:["sm","md","lg"]},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Prevents interaction and dims the control."}],usageExamples:[{title:"Controlled",code:'<Toggle checked={darkMode} onChange={e => setDarkMode(e.target.checked)} label="Dark mode" />'},{title:"Uncontrolled",code:'<Toggle defaultChecked label="Email notifications" />'},{title:"Sizes",code:`<Toggle size="sm" label="Compact" />
47
47
  <Toggle size="md" label="Default" />
48
- <Toggle size="lg" label="Large" />`},{title:"Disabled",code:'<Toggle disabled label="Unavailable setting" />'}],compositionGraph:[],accessibility:{role:"switch",ariaAttributes:["aria-checked","aria-disabled"],keyboardInteractions:["Space — toggles the switch state"]}},Qe={sm:"32px",md:"40px",lg:"46px"},Ze={sm:"var(--lucent-font-size-sm)",md:"var(--lucent-font-size-md)",lg:"var(--lucent-font-size-lg)"},ie=h.forwardRef(({options:t,size:n="md",label:a,helperText:o,errorText:r,placeholder:i,disabled:s,id:c,style:f,...g},y)=>{const d=c??`lucent-select-${Math.random().toString(36).slice(2,7)}`,l=!!r;return e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"var(--lucent-space-1)",width:"100%"},children:[a&&e.jsx("label",{htmlFor:d,style:{fontSize:"var(--lucent-font-size-sm)",fontWeight:"var(--lucent-font-weight-medium)",color:"var(--lucent-text-primary)",fontFamily:"var(--lucent-font-family-base)"},children:a}),e.jsxs("div",{style:{position:"relative",display:"flex",alignItems:"center"},children:[e.jsxs("select",{ref:y,id:d,disabled:s,"aria-invalid":l,"aria-describedby":l?`${d}-error`:o?`${d}-helper`:void 0,style:{width:"100%",height:Qe[n],padding:"0 var(--lucent-space-8) 0 var(--lucent-space-3)",fontSize:Ze[n],fontFamily:"var(--lucent-font-family-base)",color:"var(--lucent-text-primary)",background:"var(--lucent-surface-default)",border:`1px solid ${l?"var(--lucent-danger-default)":"var(--lucent-border-default)"}`,borderRadius:"var(--lucent-radius-lg)",outline:"none",boxSizing:"border-box",appearance:"none",cursor:s?"not-allowed":"pointer",transition:"border-color var(--lucent-duration-fast) var(--lucent-easing-default)",...f},onMouseEnter:p=>{var v;!s&&p.currentTarget!==document.activeElement&&(p.currentTarget.style.borderColor=l?"var(--lucent-danger-default)":"var(--lucent-border-strong)"),(v=g.onMouseEnter)==null||v.call(g,p)},onMouseLeave:p=>{var v;!s&&p.currentTarget!==document.activeElement&&(p.currentTarget.style.borderColor=l?"var(--lucent-danger-default)":"var(--lucent-border-default)"),(v=g.onMouseLeave)==null||v.call(g,p)},onFocus:p=>{var v;p.currentTarget.style.borderColor=l?"var(--lucent-danger-default)":"var(--lucent-focus-ring)",p.currentTarget.style.boxShadow=`0 0 0 3px ${l?"var(--lucent-danger-subtle)":"var(--lucent-accent-subtle)"}`,(v=g.onFocus)==null||v.call(g,p)},onBlur:p=>{var v;p.currentTarget.style.borderColor=l?"var(--lucent-danger-default)":"var(--lucent-border-default)",p.currentTarget.style.boxShadow="none",(v=g.onBlur)==null||v.call(g,p)},...g,children:[i&&e.jsx("option",{value:"",disabled:!0,children:i}),t.map(p=>e.jsx("option",{value:p.value,disabled:p.disabled,children:p.label},p.value))]}),e.jsx("span",{"aria-hidden":!0,style:{position:"absolute",right:"var(--lucent-space-3)",pointerEvents:"none",color:"var(--lucent-text-secondary)",display:"flex",alignItems:"center"},children:e.jsx("svg",{width:14,height:14,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round",children:e.jsx("polyline",{points:"6 9 12 15 18 9"})})})]}),l&&e.jsx("span",{id:`${d}-error`,role:"alert",style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-danger-text)",fontFamily:"var(--lucent-font-family-base)"},children:r}),!l&&o&&e.jsx("span",{id:`${d}-helper`,style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-text-secondary)",fontFamily:"var(--lucent-font-family-base)"},children:o})]})});ie.displayName="Select";const et={id:"select",name:"Select",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A dropdown for choosing one option from a list.",designIntent:"Select is best for 5–15 options where showing all at once would be too noisy. For fewer than 5 options, prefer RadioGroup (always-visible, faster to scan). For search-filtered or async options, a Combobox (Wave 3) is more appropriate. Always provide a placeholder when no default is sensible — this communicates the field is required and prevents silent zero-state submissions.",props:[{name:"options",type:"array",required:!0,description:"Array of { value, label, disabled? } option objects."},{name:"value",type:"string",required:!1,description:"Controlled selected value. Pair with onChange."},{name:"onChange",type:"function",required:!1,description:"Called when the selected value changes."},{name:"placeholder",type:"string",required:!1,description:"Disabled first option shown when no value is selected."},{name:"label",type:"string",required:!1,description:"Visible label rendered above the select."},{name:"helperText",type:"string",required:!1,description:"Supplementary hint shown below the select."},{name:"errorText",type:"string",required:!1,description:"Validation error message. Replaces helperText and applies error styling."},{name:"size",type:"enum",required:!1,default:"md",description:"Controls the height of the select control.",enumValues:["sm","md","lg"]},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Prevents interaction."}],usageExamples:[{title:"Controlled",code:`
48
+ <Toggle size="lg" label="Large" />`},{title:"Disabled",code:'<Toggle disabled label="Unavailable setting" />'}],compositionGraph:[],accessibility:{role:"switch",ariaAttributes:["aria-checked","aria-disabled"],keyboardInteractions:["Space — toggles the switch state"]}},Qe={sm:"32px",md:"40px",lg:"46px"},Ze={sm:"var(--lucent-font-size-sm)",md:"var(--lucent-font-size-md)",lg:"var(--lucent-font-size-lg)"},ie=h.forwardRef(({options:t,size:n="md",label:a,helperText:o,errorText:r,placeholder:i,disabled:s,id:c,style:f,...g},y)=>{const d=c??`lucent-select-${Math.random().toString(36).slice(2,7)}`,l=!!r;return e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"var(--lucent-space-1)",width:"100%"},children:[a&&e.jsx("label",{htmlFor:d,style:{fontSize:"var(--lucent-font-size-sm)",fontWeight:"var(--lucent-font-weight-medium)",color:"var(--lucent-text-primary)",fontFamily:"var(--lucent-font-family-base)"},children:a}),e.jsxs("div",{style:{position:"relative",display:"flex",alignItems:"center"},children:[e.jsxs("select",{ref:y,id:d,disabled:s,"aria-invalid":l,"aria-describedby":l?`${d}-error`:o?`${d}-helper`:void 0,style:{width:"100%",height:Qe[n],padding:"0 var(--lucent-space-8) 0 var(--lucent-space-3)",fontSize:Ze[n],fontFamily:"var(--lucent-font-family-base)",color:"var(--lucent-text-primary)",background:"var(--lucent-surface-default)",border:`1px solid ${l?"var(--lucent-danger-default)":"var(--lucent-border-default)"}`,borderRadius:"var(--lucent-radius-lg)",outline:"none",boxSizing:"border-box",appearance:"none",cursor:s?"not-allowed":"pointer",transition:"border-color var(--lucent-duration-fast) var(--lucent-easing-default)",...f},onMouseEnter:u=>{var v;!s&&u.currentTarget!==document.activeElement&&(u.currentTarget.style.borderColor=l?"var(--lucent-danger-default)":"var(--lucent-border-strong)"),(v=g.onMouseEnter)==null||v.call(g,u)},onMouseLeave:u=>{var v;!s&&u.currentTarget!==document.activeElement&&(u.currentTarget.style.borderColor=l?"var(--lucent-danger-default)":"var(--lucent-border-default)"),(v=g.onMouseLeave)==null||v.call(g,u)},onFocus:u=>{var v;u.currentTarget.style.borderColor=l?"var(--lucent-danger-default)":"var(--lucent-focus-ring)",u.currentTarget.style.boxShadow=`0 0 0 3px ${l?"var(--lucent-danger-subtle)":"var(--lucent-accent-subtle)"}`,(v=g.onFocus)==null||v.call(g,u)},onBlur:u=>{var v;u.currentTarget.style.borderColor=l?"var(--lucent-danger-default)":"var(--lucent-border-default)",u.currentTarget.style.boxShadow="none",(v=g.onBlur)==null||v.call(g,u)},...g,children:[i&&e.jsx("option",{value:"",disabled:!0,children:i}),t.map(u=>e.jsx("option",{value:u.value,disabled:u.disabled,children:u.label},u.value))]}),e.jsx("span",{"aria-hidden":!0,style:{position:"absolute",right:"var(--lucent-space-3)",pointerEvents:"none",color:"var(--lucent-text-secondary)",display:"flex",alignItems:"center"},children:e.jsx("svg",{width:14,height:14,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round",children:e.jsx("polyline",{points:"6 9 12 15 18 9"})})})]}),l&&e.jsx("span",{id:`${d}-error`,role:"alert",style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-danger-text)",fontFamily:"var(--lucent-font-family-base)"},children:r}),!l&&o&&e.jsx("span",{id:`${d}-helper`,style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-text-secondary)",fontFamily:"var(--lucent-font-family-base)"},children:o})]})});ie.displayName="Select";const et={id:"select",name:"Select",tier:"atom",domain:"neutral",specVersion:"0.1",description:"A dropdown for choosing one option from a list.",designIntent:"Select is best for 5–15 options where showing all at once would be too noisy. For fewer than 5 options, prefer RadioGroup (always-visible, faster to scan). For search-filtered or async options, a Combobox (Wave 3) is more appropriate. Always provide a placeholder when no default is sensible — this communicates the field is required and prevents silent zero-state submissions.",props:[{name:"options",type:"array",required:!0,description:"Array of { value, label, disabled? } option objects."},{name:"value",type:"string",required:!1,description:"Controlled selected value. Pair with onChange."},{name:"onChange",type:"function",required:!1,description:"Called when the selected value changes."},{name:"placeholder",type:"string",required:!1,description:"Disabled first option shown when no value is selected."},{name:"label",type:"string",required:!1,description:"Visible label rendered above the select."},{name:"helperText",type:"string",required:!1,description:"Supplementary hint shown below the select."},{name:"errorText",type:"string",required:!1,description:"Validation error message. Replaces helperText and applies error styling."},{name:"size",type:"enum",required:!1,default:"md",description:"Controls the height of the select control.",enumValues:["sm","md","lg"]},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Prevents interaction."}],usageExamples:[{title:"Controlled",code:`
49
49
  <Select
50
50
  label="Country"
51
51
  placeholder="Choose a country"
@@ -60,7 +60,7 @@
60
60
  <Input id="pw" type="password" />
61
61
  </FormField>`},{title:"Wrapping a Select",code:`<FormField label="Country" htmlFor="country">
62
62
  <Select id="country" options={countryOptions} />
63
- </FormField>`}],compositionGraph:[{componentId:"text",componentName:"Text",role:"Label, helper text, and error message",required:!1}],accessibility:{ariaAttributes:["aria-required","aria-describedby"],notes:'Link the wrapped control to an error message using aria-describedby on the control and a matching id on the error element for full screen reader support. The required asterisk is aria-hidden; set aria-required="true" on the control itself.'}},wt=()=>e.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:[e.jsx("circle",{cx:"6.5",cy:"6.5",r:"4",stroke:"currentColor",strokeWidth:"1.5"}),e.jsx("path",{d:"M9.5 9.5L13 13",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})]}),kt=()=>e.jsx("svg",{width:"14",height:"14",viewBox:"0 0 14 14",fill:"none","aria-hidden":"true",children:e.jsx("path",{d:"M3 3L11 11M11 3L3 11",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})});function St({value:t,onChange:n,placeholder:a="Search…",results:o=[],onResultSelect:r,isLoading:i=!1,disabled:s=!1,id:c,style:f}){const[g,y]=h.useState(!1),[d,l]=h.useState(null),p=h.useRef(null),v=g&&o.length>0,m=()=>{n("")},T=S=>{r==null||r(S),y(!1)},b=()=>{p.current=setTimeout(()=>y(!1),150)},x=()=>{p.current&&clearTimeout(p.current),y(!0)},M=i?e.jsx(ne,{size:"sm"}):t?e.jsx("button",{type:"button","aria-label":"Clear search",onClick:m,style:{display:"flex",alignItems:"center",background:"none",border:"none",cursor:"pointer",padding:2,borderRadius:"var(--lucent-radius-sm)",color:"var(--lucent-text-secondary)"},onMouseEnter:S=>{S.currentTarget.style.color="var(--lucent-text-primary)"},onMouseLeave:S=>{S.currentTarget.style.color="var(--lucent-text-secondary)"},children:e.jsx(kt,{})}):null;return e.jsxs("div",{style:{position:"relative",...f},children:[e.jsx(G,{id:c,type:"search",value:t,onChange:S=>n(S.target.value),placeholder:a,disabled:s,leftElement:e.jsx(wt,{}),rightElement:M??void 0,onFocus:x,onBlur:b}),v&&e.jsx("div",{role:"listbox",style:{position:"absolute",top:"calc(100% + var(--lucent-space-1))",left:0,right:0,zIndex:50,background:"var(--lucent-surface-overlay)",border:"1px solid var(--lucent-border-default)",borderRadius:"var(--lucent-radius-md)",boxShadow:"var(--lucent-shadow-md)",overflow:"hidden"},children:o.map((S,u)=>e.jsx("div",{role:"option","aria-selected":!1,onMouseDown:()=>T(S),onMouseEnter:()=>l(u),onMouseLeave:()=>l(null),style:{padding:"var(--lucent-space-2) var(--lucent-space-3)",cursor:"pointer",background:d===u?"var(--lucent-bg-subtle)":"transparent",transition:"background var(--lucent-duration-fast) var(--lucent-easing-default)"},children:e.jsx(z,{as:"span",size:"md",children:S.label})},S.id))})]})}const jt={id:"search-input",name:"SearchInput",tier:"molecule",domain:"neutral",specVersion:"0.1",description:"A search field with a built-in magnifier icon, clear button, and an optional results dropdown.",designIntent:"SearchInput is intentionally dumb about filtering — the consumer passes already-filtered results so the component stays stateless and flexible. The clear button appears only when the input has a value, keeping the right side clean at rest. The results dropdown is rendered absolutely below the input and closes after a 150ms delay on blur to allow result clicks to register before focus is lost. Spinner replaces the clear button during loading to communicate async state without layout shift.",props:[{name:"value",type:"string",required:!0,description:"Controlled input value."},{name:"onChange",type:"function",required:!0,description:"Called with the new string value whenever the input changes."},{name:"placeholder",type:"string",required:!1,default:'"Search…"',description:"Placeholder text for the input."},{name:"results",type:"array",required:!1,default:"[]",description:"Pre-filtered list of SearchResult objects ({ id, label }) to display in the dropdown."},{name:"onResultSelect",type:"function",required:!1,description:"Called with the selected SearchResult when a dropdown item is clicked."},{name:"isLoading",type:"boolean",required:!1,default:"false",description:"Shows a spinner in the right slot to indicate async search in progress."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Disables the input."},{name:"id",type:"string",required:!1,description:"HTML id forwarded to the underlying input element."},{name:"style",type:"object",required:!1,description:"Inline style overrides for the outer wrapper."}],usageExamples:[{title:"Basic controlled search",code:`const [query, setQuery] = useState('');
63
+ </FormField>`}],compositionGraph:[{componentId:"text",componentName:"Text",role:"Label, helper text, and error message",required:!1}],accessibility:{ariaAttributes:["aria-required","aria-describedby"],notes:'Link the wrapped control to an error message using aria-describedby on the control and a matching id on the error element for full screen reader support. The required asterisk is aria-hidden; set aria-required="true" on the control itself.'}},wt=()=>e.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:[e.jsx("circle",{cx:"6.5",cy:"6.5",r:"4",stroke:"currentColor",strokeWidth:"1.5"}),e.jsx("path",{d:"M9.5 9.5L13 13",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})]}),kt=()=>e.jsx("svg",{width:"14",height:"14",viewBox:"0 0 14 14",fill:"none","aria-hidden":"true",children:e.jsx("path",{d:"M3 3L11 11M11 3L3 11",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})});function St({value:t,onChange:n,placeholder:a="Search…",results:o=[],onResultSelect:r,isLoading:i=!1,disabled:s=!1,id:c,style:f}){const[g,y]=h.useState(!1),[d,l]=h.useState(null),u=h.useRef(null),v=g&&o.length>0,m=()=>{n("")},k=j=>{r==null||r(j),y(!1)},b=()=>{u.current=setTimeout(()=>y(!1),150)},x=()=>{u.current&&clearTimeout(u.current),y(!0)},M=i?e.jsx(ne,{size:"sm"}):t?e.jsx("button",{type:"button","aria-label":"Clear search",onClick:m,style:{display:"flex",alignItems:"center",background:"none",border:"none",cursor:"pointer",padding:2,borderRadius:"var(--lucent-radius-sm)",color:"var(--lucent-text-secondary)"},onMouseEnter:j=>{j.currentTarget.style.color="var(--lucent-text-primary)"},onMouseLeave:j=>{j.currentTarget.style.color="var(--lucent-text-secondary)"},children:e.jsx(kt,{})}):null;return e.jsxs("div",{style:{position:"relative",...f},children:[e.jsx(_,{id:c,type:"search",value:t,onChange:j=>n(j.target.value),placeholder:a,disabled:s,leftElement:e.jsx(wt,{}),rightElement:M??void 0,onFocus:x,onBlur:b}),v&&e.jsx("div",{role:"listbox",style:{position:"absolute",top:"calc(100% + var(--lucent-space-1))",left:0,right:0,zIndex:50,background:"var(--lucent-surface-overlay)",border:"1px solid var(--lucent-border-default)",borderRadius:"var(--lucent-radius-md)",boxShadow:"var(--lucent-shadow-md)",overflow:"hidden"},children:o.map((j,p)=>e.jsx("div",{role:"option","aria-selected":!1,onMouseDown:()=>k(j),onMouseEnter:()=>l(p),onMouseLeave:()=>l(null),style:{padding:"var(--lucent-space-2) var(--lucent-space-3)",cursor:"pointer",background:d===p?"var(--lucent-bg-subtle)":"transparent",transition:"background var(--lucent-duration-fast) var(--lucent-easing-default)"},children:e.jsx(z,{as:"span",size:"md",children:j.label})},j.id))})]})}const jt={id:"search-input",name:"SearchInput",tier:"molecule",domain:"neutral",specVersion:"0.1",description:"A search field with a built-in magnifier icon, clear button, and an optional results dropdown.",designIntent:"SearchInput is intentionally dumb about filtering — the consumer passes already-filtered results so the component stays stateless and flexible. The clear button appears only when the input has a value, keeping the right side clean at rest. The results dropdown is rendered absolutely below the input and closes after a 150ms delay on blur to allow result clicks to register before focus is lost. Spinner replaces the clear button during loading to communicate async state without layout shift.",props:[{name:"value",type:"string",required:!0,description:"Controlled input value."},{name:"onChange",type:"function",required:!0,description:"Called with the new string value whenever the input changes."},{name:"placeholder",type:"string",required:!1,default:'"Search…"',description:"Placeholder text for the input."},{name:"results",type:"array",required:!1,default:"[]",description:"Pre-filtered list of SearchResult objects ({ id, label }) to display in the dropdown."},{name:"onResultSelect",type:"function",required:!1,description:"Called with the selected SearchResult when a dropdown item is clicked."},{name:"isLoading",type:"boolean",required:!1,default:"false",description:"Shows a spinner in the right slot to indicate async search in progress."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Disables the input."},{name:"id",type:"string",required:!1,description:"HTML id forwarded to the underlying input element."},{name:"style",type:"object",required:!1,description:"Inline style overrides for the outer wrapper."}],usageExamples:[{title:"Basic controlled search",code:`const [query, setQuery] = useState('');
64
64
  const [results, setResults] = useState([]);
65
65
 
66
66
  <SearchInput
@@ -92,23 +92,23 @@ const [results, setResults] = useState([]);
92
92
  action={<Button variant="primary">New project</Button>}
93
93
  />`},{title:"Inside a Card",code:`<Card>
94
94
  <EmptyState title="Nothing here" description="Add items to see them listed." />
95
- </Card>`}],compositionGraph:[{componentId:"text",componentName:"Text",role:"Title and description",required:!0}],accessibility:{notes:"The title renders as an h3 by default. If EmptyState appears inside a section with its own heading hierarchy, override by passing a Text component as part of a custom layout. Ensure the action element has a descriptive label for screen readers."}},Wt={text:"1em",circle:40,rectangle:40},Vt={text:"var(--lucent-radius-sm)",circle:"var(--lucent-radius-full)",rectangle:"var(--lucent-radius-md)"};function K({width:t,height:n,radius:a,animate:o,style:r}){return e.jsx("span",{style:{display:"block",width:typeof t=="number"?`${t}px`:t,height:typeof n=="number"?`${n}px`:n,borderRadius:a,background:o?"linear-gradient(90deg, var(--lucent-bg-muted) 25%, var(--lucent-bg-subtle) 50%, var(--lucent-bg-muted) 75%)":"var(--lucent-bg-muted)",backgroundSize:o?"200% 100%":void 0,animation:o?"lucent-skeleton-shimmer 1.6s ease-in-out infinite":void 0,flexShrink:0,...r}})}function Ot({variant:t="rectangle",width:n="100%",height:a,lines:o=1,animate:r=!0,radius:i,style:s}){const c=a??Wt[t],f=i??Vt[t],g=r?e.jsx("style",{children:`
95
+ </Card>`}],compositionGraph:[{componentId:"text",componentName:"Text",role:"Title and description",required:!0}],accessibility:{notes:"The title renders as an h3 by default. If EmptyState appears inside a section with its own heading hierarchy, override by passing a Text component as part of a custom layout. Ensure the action element has a descriptive label for screen readers."}},Wt={text:"1em",circle:40,rectangle:40},Vt={text:"var(--lucent-radius-sm)",circle:"var(--lucent-radius-full)",rectangle:"var(--lucent-radius-md)"};function X({width:t,height:n,radius:a,animate:o,style:r}){return e.jsx("span",{style:{display:"block",width:typeof t=="number"?`${t}px`:t,height:typeof n=="number"?`${n}px`:n,borderRadius:a,background:o?"linear-gradient(90deg, var(--lucent-bg-muted) 25%, var(--lucent-bg-subtle) 50%, var(--lucent-bg-muted) 75%)":"var(--lucent-bg-muted)",backgroundSize:o?"200% 100%":void 0,animation:o?"lucent-skeleton-shimmer 1.6s ease-in-out infinite":void 0,flexShrink:0,...r}})}function Ot({variant:t="rectangle",width:n="100%",height:a,lines:o=1,animate:r=!0,radius:i,style:s}){const c=a??Wt[t],f=i??Vt[t],g=r?e.jsx("style",{children:`
96
96
  @keyframes lucent-skeleton-shimmer {
97
97
  0% { background-position: 200% 0; }
98
98
  100% { background-position: -200% 0; }
99
99
  }
100
- `}):null;return t==="text"&&o>1?e.jsxs(e.Fragment,{children:[g,e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"var(--lucent-space-2)",...s},children:Array.from({length:o}).map((y,d)=>e.jsx(K,{width:d===o-1?"70%":n,height:c,radius:f,animate:r},d))})]}):e.jsxs(e.Fragment,{children:[g,e.jsx(K,{width:t==="circle"?a??40:n,height:c,radius:f,animate:r,...s!==void 0&&{style:s}})]})}const Ht={id:"skeleton",name:"Skeleton",tier:"molecule",domain:"neutral",specVersion:"0.1",description:"Animated placeholder that matches the shape of content while it loads.",designIntent:"Skeleton uses a shimmer animation to communicate that content is loading without showing a spinner. The three variants (text, circle, rectangle) cover the most common content shapes: inline text, avatars/thumbnails, and generic content blocks. The text variant with lines > 1 mimics a paragraph by stacking multiple text skeletons and shortening the last line to 70%, which is a widely recognised convention for body copy placeholders. The shimmer gradient uses bg-muted and bg-subtle so it adapts correctly in both light and dark themes without hard-coded colors.",props:[{name:"variant",type:"enum",required:!1,default:"rectangle",description:"Shape of the skeleton — text (1em tall), circle (equal width/height), or rectangle.",enumValues:["text","circle","rectangle"]},{name:"width",type:"string",required:!1,default:'"100%"',description:"Width of the skeleton. Accepts any CSS value or a number (interpreted as px)."},{name:"height",type:"string",required:!1,description:"Height of the skeleton. Defaults: text=1em, circle=40px, rectangle=40px."},{name:"lines",type:"number",required:!1,default:"1",description:"Number of stacked text lines to render. Only applies to the text variant."},{name:"animate",type:"boolean",required:!1,default:"true",description:"Enables the shimmer animation. Set to false to render a static placeholder."},{name:"radius",type:"string",required:!1,description:"Override the default border-radius for the variant."},{name:"style",type:"object",required:!1,description:"Inline style overrides."}],usageExamples:[{title:"Paragraph placeholder",code:'<Skeleton variant="text" lines={3} />'},{title:"Avatar placeholder",code:'<Skeleton variant="circle" width={40} height={40} />'},{title:"Card placeholder",code:`<Card>
100
+ `}):null;return t==="text"&&o>1?e.jsxs(e.Fragment,{children:[g,e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"var(--lucent-space-2)",...s},children:Array.from({length:o}).map((y,d)=>e.jsx(X,{width:d===o-1?"70%":n,height:c,radius:f,animate:r},d))})]}):e.jsxs(e.Fragment,{children:[g,e.jsx(X,{width:t==="circle"?a??40:n,height:c,radius:f,animate:r,...s!==void 0&&{style:s}})]})}const Ht={id:"skeleton",name:"Skeleton",tier:"molecule",domain:"neutral",specVersion:"0.1",description:"Animated placeholder that matches the shape of content while it loads.",designIntent:"Skeleton uses a shimmer animation to communicate that content is loading without showing a spinner. The three variants (text, circle, rectangle) cover the most common content shapes: inline text, avatars/thumbnails, and generic content blocks. The text variant with lines > 1 mimics a paragraph by stacking multiple text skeletons and shortening the last line to 70%, which is a widely recognised convention for body copy placeholders. The shimmer gradient uses bg-muted and bg-subtle so it adapts correctly in both light and dark themes without hard-coded colors.",props:[{name:"variant",type:"enum",required:!1,default:"rectangle",description:"Shape of the skeleton — text (1em tall), circle (equal width/height), or rectangle.",enumValues:["text","circle","rectangle"]},{name:"width",type:"string",required:!1,default:'"100%"',description:"Width of the skeleton. Accepts any CSS value or a number (interpreted as px)."},{name:"height",type:"string",required:!1,description:"Height of the skeleton. Defaults: text=1em, circle=40px, rectangle=40px."},{name:"lines",type:"number",required:!1,default:"1",description:"Number of stacked text lines to render. Only applies to the text variant."},{name:"animate",type:"boolean",required:!1,default:"true",description:"Enables the shimmer animation. Set to false to render a static placeholder."},{name:"radius",type:"string",required:!1,description:"Override the default border-radius for the variant."},{name:"style",type:"object",required:!1,description:"Inline style overrides."}],usageExamples:[{title:"Paragraph placeholder",code:'<Skeleton variant="text" lines={3} />'},{title:"Avatar placeholder",code:'<Skeleton variant="circle" width={40} height={40} />'},{title:"Card placeholder",code:`<Card>
101
101
  <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
102
102
  <Skeleton variant="rectangle" height={160} />
103
103
  <Skeleton variant="text" lines={2} />
104
104
  <Skeleton variant="text" width="40%" />
105
105
  </div>
106
- </Card>`},{title:"Static (no animation)",code:'<Skeleton variant="rectangle" width={200} height={32} animate={false} />'}],compositionGraph:[],accessibility:{ariaAttributes:["aria-busy","aria-label"],notes:'Wrap loading regions with aria-busy="true" on the container so screen readers know content is loading. Individual Skeleton elements are presentational and do not need ARIA attributes themselves.'}};function Ut({items:t,separator:n="/",style:a}){return e.jsx("nav",{"aria-label":"Breadcrumb",style:a,children:e.jsx("ol",{style:{display:"flex",alignItems:"center",flexWrap:"wrap",gap:"var(--lucent-space-1)",listStyle:"none",margin:0,padding:0,fontFamily:"var(--lucent-font-family-base)"},children:t.map((o,r)=>{const i=r===t.length-1;return e.jsxs("li",{style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-1)"},children:[i?e.jsx(z,{size:"sm",color:"primary",as:"span","aria-current":"page",children:o.label}):o.href!=null?e.jsx("a",{href:o.href,onClick:o.onClick,style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-text-secondary)",textDecoration:"none",fontFamily:"var(--lucent-font-family-base)",transition:"color var(--lucent-duration-fast) var(--lucent-easing-default)"},onMouseEnter:s=>{s.currentTarget.style.color="var(--lucent-text-primary)"},onMouseLeave:s=>{s.currentTarget.style.color="var(--lucent-text-secondary)"},children:o.label}):e.jsx("button",{onClick:o.onClick,style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-text-secondary)",fontFamily:"var(--lucent-font-family-base)",background:"none",border:"none",padding:0,cursor:"pointer",transition:"color var(--lucent-duration-fast) var(--lucent-easing-default)"},onMouseEnter:s=>{s.currentTarget.style.color="var(--lucent-text-primary)"},onMouseLeave:s=>{s.currentTarget.style.color="var(--lucent-text-secondary)"},children:o.label}),!i&&e.jsx("span",{"aria-hidden":!0,style:{color:"var(--lucent-text-disabled)",fontSize:"var(--lucent-font-size-sm)",userSelect:"none"},children:n})]},r)})})})}function Gt({tabs:t,defaultValue:n,value:a,onChange:o,style:r}){var b;const i=a!==void 0,[s,c]=h.useState(n??((b=t[0])==null?void 0:b.value)??""),f=i?a:s,[g,y]=h.useState(null),d=h.useRef([]),[l,p]=h.useState(null),v=h.useRef(!1);h.useLayoutEffect(()=>{const x=t.findIndex(S=>S.value===f),M=d.current[x];M&&(p({left:M.offsetLeft,width:M.offsetWidth,animate:v.current}),v.current=!0)},[f,t]);const m=x=>{i||c(x),o==null||o(x)},T=(x,M)=>{var E;const S=t.map((w,k)=>w.disabled?-1:k).filter(w=>w!==-1),u=S.indexOf(M);let j=-1;x.key==="ArrowRight"&&(j=S[(u+1)%S.length]??-1),x.key==="ArrowLeft"&&(j=S[(u-1+S.length)%S.length]??-1),x.key==="Home"&&(j=S[0]??-1),x.key==="End"&&(j=S[S.length-1]??-1),j!==-1&&(x.preventDefault(),(E=d.current[j])==null||E.focus(),m(t[j].value))};return e.jsxs("div",{style:{display:"flex",flexDirection:"column",...r},children:[e.jsxs("div",{role:"tablist",style:{position:"relative",display:"flex",borderBottom:"1px solid var(--lucent-border-default)"},children:[t.map((x,M)=>{const S=x.value===f,u=x.disabled??!1;return e.jsx("button",{ref:j=>{d.current[M]=j},role:"tab","aria-selected":S,"aria-controls":`lucent-tabpanel-${x.value}`,id:`lucent-tab-${x.value}`,disabled:u,tabIndex:S?0:-1,onClick:()=>{u||m(x.value)},onKeyDown:j=>T(j,M),onMouseEnter:()=>{u||y(M)},onMouseLeave:()=>y(null),style:{padding:"var(--lucent-space-1) var(--lucent-space-2) var(--lucent-space-3)",background:"none",border:"none",cursor:u?"not-allowed":"pointer",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-md)",fontWeight:S?"var(--lucent-font-weight-medium)":"var(--lucent-font-weight-regular)",color:u?"var(--lucent-text-disabled)":S?"var(--lucent-text-primary)":"var(--lucent-text-secondary)",transition:"color var(--lucent-duration-fast) var(--lucent-easing-default)",whiteSpace:"nowrap",outline:"none"},children:e.jsx("span",{style:{display:"block",padding:"var(--lucent-space-1) var(--lucent-space-3)",borderRadius:"var(--lucent-radius-md)",background:g===M&&!S?"var(--lucent-bg-subtle)":"transparent",transition:"background var(--lucent-duration-fast) var(--lucent-easing-default)"},children:x.label})},x.value)}),l!=null&&e.jsx("span",{"aria-hidden":!0,style:{position:"absolute",bottom:0,left:l.left,width:l.width,height:2,background:"var(--lucent-accent-default)",borderRadius:"var(--lucent-radius-sm)",transition:l.animate?"left 220ms var(--lucent-easing-default), width 220ms var(--lucent-easing-default)":"none"}})]}),t.map(x=>e.jsx("div",{role:"tabpanel",id:`lucent-tabpanel-${x.value}`,"aria-labelledby":`lucent-tab-${x.value}`,hidden:x.value!==f,style:{padding:"var(--lucent-space-4) 0",outline:"none"},tabIndex:0,children:x.content},x.value))]})}const _t=`
106
+ </Card>`},{title:"Static (no animation)",code:'<Skeleton variant="rectangle" width={200} height={32} animate={false} />'}],compositionGraph:[],accessibility:{ariaAttributes:["aria-busy","aria-label"],notes:'Wrap loading regions with aria-busy="true" on the container so screen readers know content is loading. Individual Skeleton elements are presentational and do not need ARIA attributes themselves.'}};function Ut({items:t,separator:n="/",style:a}){return e.jsx("nav",{"aria-label":"Breadcrumb",style:a,children:e.jsx("ol",{style:{display:"flex",alignItems:"center",flexWrap:"wrap",gap:"var(--lucent-space-1)",listStyle:"none",margin:0,padding:0,fontFamily:"var(--lucent-font-family-base)"},children:t.map((o,r)=>{const i=r===t.length-1;return e.jsxs("li",{style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-1)"},children:[i?e.jsx(z,{size:"sm",color:"primary",as:"span","aria-current":"page",children:o.label}):o.href!=null?e.jsx("a",{href:o.href,onClick:o.onClick,style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-text-secondary)",textDecoration:"none",fontFamily:"var(--lucent-font-family-base)",transition:"color var(--lucent-duration-fast) var(--lucent-easing-default)"},onMouseEnter:s=>{s.currentTarget.style.color="var(--lucent-text-primary)"},onMouseLeave:s=>{s.currentTarget.style.color="var(--lucent-text-secondary)"},children:o.label}):e.jsx("button",{onClick:o.onClick,style:{fontSize:"var(--lucent-font-size-sm)",color:"var(--lucent-text-secondary)",fontFamily:"var(--lucent-font-family-base)",background:"none",border:"none",padding:0,cursor:"pointer",transition:"color var(--lucent-duration-fast) var(--lucent-easing-default)"},onMouseEnter:s=>{s.currentTarget.style.color="var(--lucent-text-primary)"},onMouseLeave:s=>{s.currentTarget.style.color="var(--lucent-text-secondary)"},children:o.label}),!i&&e.jsx("span",{"aria-hidden":!0,style:{color:"var(--lucent-text-disabled)",fontSize:"var(--lucent-font-size-sm)",userSelect:"none"},children:n})]},r)})})})}function Gt({tabs:t,defaultValue:n,value:a,onChange:o,style:r}){var b;const i=a!==void 0,[s,c]=h.useState(n??((b=t[0])==null?void 0:b.value)??""),f=i?a:s,[g,y]=h.useState(null),d=h.useRef([]),[l,u]=h.useState(null),v=h.useRef(!1);h.useLayoutEffect(()=>{const x=t.findIndex(j=>j.value===f),M=d.current[x];M&&(u({left:M.offsetLeft,width:M.offsetWidth,animate:v.current}),v.current=!0)},[f,t]);const m=x=>{i||c(x),o==null||o(x)},k=(x,M)=>{var E;const j=t.map((w,S)=>w.disabled?-1:S).filter(w=>w!==-1),p=j.indexOf(M);let T=-1;x.key==="ArrowRight"&&(T=j[(p+1)%j.length]??-1),x.key==="ArrowLeft"&&(T=j[(p-1+j.length)%j.length]??-1),x.key==="Home"&&(T=j[0]??-1),x.key==="End"&&(T=j[j.length-1]??-1),T!==-1&&(x.preventDefault(),(E=d.current[T])==null||E.focus(),m(t[T].value))};return e.jsxs("div",{style:{display:"flex",flexDirection:"column",...r},children:[e.jsxs("div",{role:"tablist",style:{position:"relative",display:"flex",borderBottom:"1px solid var(--lucent-border-default)"},children:[t.map((x,M)=>{const j=x.value===f,p=x.disabled??!1;return e.jsx("button",{ref:T=>{d.current[M]=T},role:"tab","aria-selected":j,"aria-controls":`lucent-tabpanel-${x.value}`,id:`lucent-tab-${x.value}`,disabled:p,tabIndex:j?0:-1,onClick:()=>{p||m(x.value)},onKeyDown:T=>k(T,M),onMouseEnter:()=>{p||y(M)},onMouseLeave:()=>y(null),style:{padding:"var(--lucent-space-1) var(--lucent-space-2) var(--lucent-space-3)",background:"none",border:"none",cursor:p?"not-allowed":"pointer",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-md)",fontWeight:j?"var(--lucent-font-weight-medium)":"var(--lucent-font-weight-regular)",color:p?"var(--lucent-text-disabled)":j?"var(--lucent-text-primary)":"var(--lucent-text-secondary)",transition:"color var(--lucent-duration-fast) var(--lucent-easing-default)",whiteSpace:"nowrap",outline:"none"},children:e.jsx("span",{style:{display:"block",padding:"var(--lucent-space-1) var(--lucent-space-3)",borderRadius:"var(--lucent-radius-md)",background:g===M&&!j?"var(--lucent-bg-subtle)":"transparent",transition:"background var(--lucent-duration-fast) var(--lucent-easing-default)"},children:x.label})},x.value)}),l!=null&&e.jsx("span",{"aria-hidden":!0,style:{position:"absolute",bottom:0,left:l.left,width:l.width,height:2,background:"var(--lucent-accent-default)",borderRadius:"var(--lucent-radius-sm)",transition:l.animate?"left 220ms var(--lucent-easing-default), width 220ms var(--lucent-easing-default)":"none"}})]}),t.map(x=>e.jsx("div",{role:"tabpanel",id:`lucent-tabpanel-${x.value}`,"aria-labelledby":`lucent-tab-${x.value}`,hidden:x.value!==f,style:{padding:"var(--lucent-space-4) 0",outline:"none"},tabIndex:0,children:x.content},x.value))]})}const _t=`
107
107
  @keyframes lucent-collapsible-open {
108
108
  from { opacity: 0; transform: translateY(-4px); }
109
109
  to { opacity: 1; transform: translateY(0); }
110
110
  }
111
- `;function Yt({trigger:t,children:n,defaultOpen:a=!1,open:o,onOpenChange:r,style:i}){const s=o!==void 0,[c,f]=h.useState(a),g=s?o:c,y=h.useRef(null),[d,l]=h.useState(g?void 0:0),p=h.useRef(!1);h.useEffect(()=>{const m=y.current;if(m)if(g){const T=m.scrollHeight;l(T),p.current=!0;const b=setTimeout(()=>{l(void 0),p.current=!1},220);return()=>clearTimeout(b)}else l(m.scrollHeight),m.getBoundingClientRect(),l(0)},[g]);const v=()=>{const m=!g;s||f(m),r==null||r(m)};return e.jsxs(e.Fragment,{children:[e.jsx("style",{children:_t}),e.jsxs("div",{style:{display:"flex",flexDirection:"column",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-md)",...i},children:[e.jsxs("button",{onClick:v,"aria-expanded":g,style:{display:"flex",alignItems:"center",justifyContent:"space-between",width:"100%",background:"none",border:"none",padding:"var(--lucent-space-3) var(--lucent-space-4)",cursor:"pointer",textAlign:"left",outline:"none",fontFamily:"inherit",fontSize:"inherit"},children:[e.jsx("span",{style:{flex:1},children:t}),e.jsx(Kt,{open:g})]}),e.jsx("div",{ref:y,"aria-hidden":!g,style:{overflow:"hidden",height:d!==void 0?d:"auto",transition:"height 200ms var(--lucent-easing-default)"},children:e.jsx("div",{style:{padding:"var(--lucent-space-2) var(--lucent-space-4) var(--lucent-space-3)",animation:g?"lucent-collapsible-open 200ms var(--lucent-easing-default) forwards":void 0},children:n})})]})]})}function Kt({open:t}){return e.jsx("svg",{width:16,height:16,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":!0,style:{flexShrink:0,color:"var(--lucent-text-secondary)",transform:t?"rotate(180deg)":"rotate(0deg)",transition:"transform 200ms var(--lucent-easing-default)"},children:e.jsx("polyline",{points:"6 9 12 15 18 9"})})}function X(t){return typeof t=="number"?`${t}px`:t}function Xt({children:t,header:n,sidebar:a,sidebarWidth:o=240,headerHeight:r=48,sidebarCollapsed:i=!1,mainStyle:s,style:c}){const f=X(r),g=X(o);return e.jsxs("div",{style:{display:"flex",flexDirection:"column",height:"100vh",overflow:"hidden",fontFamily:"var(--lucent-font-family-base)",...c},children:[n!=null&&e.jsx("div",{style:{flexShrink:0,height:f,zIndex:10,background:"var(--lucent-surface-default)"},children:n}),e.jsxs("div",{style:{display:"flex",flex:1,overflow:"hidden"},children:[a!=null&&e.jsx("div",{style:{width:i?0:g,flexShrink:0,overflow:"hidden",overflowY:i?"hidden":"auto",background:"var(--lucent-surface-default)",transition:"width 200ms var(--lucent-easing-default)"},children:a}),e.jsx("main",{style:{flex:1,overflowY:"auto",minWidth:0,margin:"0 var(--lucent-space-3) var(--lucent-space-3) 0",border:"1px solid var(--lucent-border-default)",borderRadius:"var(--lucent-radius-lg)",boxShadow:"var(--lucent-shadow-sm)",background:"var(--lucent-surface-default)",...s},children:t})]})]})}function Jt({state:t}){return e.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 12 12",fill:"none","aria-hidden":!0,style:{flexShrink:0,opacity:t==="none"?.35:1},children:[e.jsx("path",{d:"M6 2L9 5H3L6 2Z",fill:"currentColor",opacity:t==="desc"?.35:1}),e.jsx("path",{d:"M6 10L3 7H9L6 10Z",fill:"currentColor",opacity:t==="asc"?.35:1})]})}function J({dir:t}){return e.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none","aria-hidden":!0,children:e.jsx("path",{d:t==="left"?"M10 12L6 8l4-4":"M6 4l4 4-4 4",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function Qt({columns:t,rows:n,pageSize:a=10,page:o,onPageChange:r,emptyState:i,style:s}){const[c,f]=h.useState(null),[g,y]=h.useState(0),[d,l]=h.useState(null),p=o!==void 0,v=p?o:g,m=c?[...n].sort((u,j)=>{const E=u[c.key],w=j[c.key],k=String(E??"").localeCompare(String(w??""),void 0,{numeric:!0});return c.dir==="asc"?k:-k}):n,T=a>0?m.slice(v*a,(v+1)*a):m,b=a>0?Math.max(1,Math.ceil(m.length/a)):1,x=u=>{p||y(u),r==null||r(u)},M=u=>{f(j=>!j||j.key!==u?{key:u,dir:"asc"}:j.dir==="asc"?{key:u,dir:"desc"}:null),p||y(0),r==null||r(0)},S=[];if(b<=7)for(let u=0;u<b;u++)S.push(u);else{S.push(0),v>2&&S.push("…");for(let u=Math.max(1,v-1);u<=Math.min(b-2,v+1);u++)S.push(u);v<b-3&&S.push("…"),S.push(b-1)}return e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"var(--lucent-space-3)",...s},children:[e.jsx("div",{style:{overflowX:"auto",borderRadius:"var(--lucent-radius-lg)",border:"1px solid var(--lucent-border-default)"},children:e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-sm)"},children:[e.jsx("thead",{children:e.jsx("tr",{style:{borderBottom:"1px solid var(--lucent-border-default)"},children:t.map(u=>{const j=(c==null?void 0:c.key)===u.key?c.dir:"none";return e.jsx("th",{onClick:u.sortable?()=>M(u.key):void 0,style:{padding:"var(--lucent-space-3) var(--lucent-space-4)",textAlign:u.align??"left",fontWeight:"var(--lucent-font-weight-medium)",color:"var(--lucent-text-secondary)",background:"var(--lucent-bg-subtle)",cursor:u.sortable?"pointer":"default",userSelect:"none",whiteSpace:"nowrap",...u.width?{width:u.width}:{}},children:e.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:"var(--lucent-space-1)"},children:[u.header,u.sortable&&e.jsx(Jt,{state:j})]})},u.key)})})}),e.jsx("tbody",{children:T.length===0?e.jsx("tr",{children:e.jsx("td",{colSpan:t.length,style:{padding:"var(--lucent-space-12)",textAlign:"center"},children:i??e.jsx(z,{color:"secondary",children:"No data"})})}):T.map((u,j)=>e.jsx("tr",{onMouseEnter:()=>l(j),onMouseLeave:()=>l(null),style:{borderBottom:j<T.length-1?"1px solid var(--lucent-border-subtle)":"none",background:d===j?"var(--lucent-bg-subtle)":"var(--lucent-surface-default)",transition:"background var(--lucent-duration-fast) var(--lucent-easing-default)"},children:t.map(E=>e.jsx("td",{style:{padding:"var(--lucent-space-3) var(--lucent-space-4)",color:"var(--lucent-text-primary)",textAlign:E.align??"left",verticalAlign:"middle"},children:E.render?E.render(u,j):String(u[E.key]??"")},E.key))},j))})]})}),a>0&&m.length>0&&e.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",gap:"var(--lucent-space-3)",flexWrap:"wrap"},children:[e.jsx(z,{color:"secondary",size:"sm",children:m.length===1?"1 row":`${v*a+1}–${Math.min((v+1)*a,m.length)} of ${m.length} rows`}),e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-1)"},children:[e.jsx(V,{onClick:()=>x(v-1),disabled:v===0,"aria-label":"Previous page",children:e.jsx(J,{dir:"left"})}),S.map((u,j)=>u==="…"?e.jsx("span",{style:{padding:"0 var(--lucent-space-1)",color:"var(--lucent-text-disabled)",fontSize:"var(--lucent-font-size-sm)"},children:"…"},`ellipsis-${j}`):e.jsx(V,{onClick:()=>x(u),active:u===v,"aria-label":`Page ${u+1}`,"aria-current":u===v?"page":void 0,children:u+1},u)),e.jsx(V,{onClick:()=>x(v+1),disabled:v>=b-1,"aria-label":"Next page",children:e.jsx(J,{dir:"right"})})]})]})]})}function V({children:t,onClick:n,disabled:a,active:o,...r}){const[i,s]=h.useState(!1);return e.jsx("button",{...r,onClick:n,disabled:a,onMouseEnter:()=>s(!0),onMouseLeave:()=>s(!1),style:{display:"inline-flex",alignItems:"center",justifyContent:"center",minWidth:32,height:32,padding:"0 var(--lucent-space-2)",borderRadius:"var(--lucent-radius-md)",border:o?"1px solid var(--lucent-accent-default)":"1px solid transparent",background:o?"var(--lucent-accent-default)":i&&!a?"var(--lucent-bg-muted)":"transparent",color:o?"var(--lucent-text-on-accent)":a?"var(--lucent-text-disabled)":"var(--lucent-text-primary)",fontSize:"var(--lucent-font-size-sm)",fontFamily:"var(--lucent-font-family-base)",fontWeight:"var(--lucent-font-weight-regular)",cursor:a?"not-allowed":"pointer",transition:"background var(--lucent-duration-fast) var(--lucent-easing-default)"},children:t})}const Zt={id:"data-table",name:"DataTable",tier:"molecule",domain:"neutral",specVersion:"1.0",description:"A sortable, paginated data table with configurable columns, custom cell renderers, and keyboard-accessible pagination controls.",designIntent:"DataTable is generic over row type T so TypeScript consumers get full type safety on column keys and renderers. Sorting is client-side and composable — each column opts in via sortable:true; clicking a sorted column cycles asc → desc → unsorted. Pagination is either controlled (page prop + onPageChange) or uncontrolled (internal state). A pageSize of 0 disables pagination entirely, useful when the parent manages windowing. Column filtering is intentionally excluded here (see DataTable Filter issue #52) to keep the API focused. Row hover uses bg-subtle, not a border change, so the visual weight stays low for dense data views.",props:[{name:"columns",type:"array",required:!0,description:"Column definitions. Each column has a key, header, optional render function, optional sortable flag, optional width, and optional text align."},{name:"rows",type:"array",required:!0,description:"Array of data objects to display. The generic type T is inferred from this prop."},{name:"pageSize",type:"number",required:!1,default:"10",description:"Number of rows per page. Set to 0 to disable pagination."},{name:"page",type:"number",required:!1,description:"Controlled current page (0-indexed). When provided, the component is fully controlled."},{name:"onPageChange",type:"function",required:!1,description:"Called with the new page index whenever the page changes (from pagination controls or after a sort reset)."},{name:"emptyState",type:"ReactNode",required:!1,description:'Content to render when rows is empty. Defaults to a "No data" text.'},{name:"style",type:"object",required:!1,description:"Inline style overrides for the outer wrapper."}],usageExamples:[{title:"Basic sortable table",code:`<DataTable
111
+ `;function Yt({trigger:t,children:n,defaultOpen:a=!1,open:o,onOpenChange:r,style:i}){const s=o!==void 0,[c,f]=h.useState(a),g=s?o:c,y=h.useRef(null),[d,l]=h.useState(g?void 0:0),u=h.useRef(!1);h.useEffect(()=>{const m=y.current;if(m)if(g){const k=m.scrollHeight;l(k),u.current=!0;const b=setTimeout(()=>{l(void 0),u.current=!1},220);return()=>clearTimeout(b)}else l(m.scrollHeight),m.getBoundingClientRect(),l(0)},[g]);const v=()=>{const m=!g;s||f(m),r==null||r(m)};return e.jsxs(e.Fragment,{children:[e.jsx("style",{children:_t}),e.jsxs("div",{style:{display:"flex",flexDirection:"column",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-md)",...i},children:[e.jsxs("button",{onClick:v,"aria-expanded":g,style:{display:"flex",alignItems:"center",justifyContent:"space-between",width:"100%",background:"none",border:"none",padding:"var(--lucent-space-3) var(--lucent-space-4)",cursor:"pointer",textAlign:"left",outline:"none",fontFamily:"inherit",fontSize:"inherit"},children:[e.jsx("span",{style:{flex:1},children:t}),e.jsx(Kt,{open:g})]}),e.jsx("div",{ref:y,"aria-hidden":!g,style:{overflow:"hidden",height:d!==void 0?d:"auto",transition:"height 200ms var(--lucent-easing-default)"},children:e.jsx("div",{style:{padding:"var(--lucent-space-2) var(--lucent-space-4) var(--lucent-space-3)",animation:g?"lucent-collapsible-open 200ms var(--lucent-easing-default) forwards":void 0},children:n})})]})]})}function Kt({open:t}){return e.jsx("svg",{width:16,height:16,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":!0,style:{flexShrink:0,color:"var(--lucent-text-secondary)",transform:t?"rotate(180deg)":"rotate(0deg)",transition:"transform 200ms var(--lucent-easing-default)"},children:e.jsx("polyline",{points:"6 9 12 15 18 9"})})}function W(t){return typeof t=="number"?`${t}px`:t}function Xt({children:t,header:n,sidebar:a,sidebarWidth:o=240,headerHeight:r=48,sidebarCollapsed:i=!1,rightSidebar:s,rightSidebarWidth:c=240,rightSidebarCollapsed:f=!1,footer:g,footerHeight:y=28,mainStyle:d,style:l}){const u=W(r),v=W(o),m=W(c),k=W(y);return e.jsxs("div",{style:{display:"flex",flexDirection:"column",height:"100vh",overflow:"hidden",fontFamily:"var(--lucent-font-family-base)",...l},children:[n!=null&&e.jsx("div",{style:{flexShrink:0,height:u,zIndex:10,background:"var(--lucent-surface-default)"},children:n}),e.jsxs("div",{style:{display:"flex",flex:1,overflow:"hidden"},children:[a!=null&&e.jsx("div",{style:{width:i?0:v,flexShrink:0,overflow:"hidden",overflowY:i?"hidden":"auto",background:"var(--lucent-surface-default)",transition:"width 200ms var(--lucent-easing-default)"},children:a}),e.jsx("main",{style:{flex:1,overflowY:"auto",minWidth:0,margin:s!=null?"0 0 var(--lucent-space-3) 0":"0 var(--lucent-space-3) var(--lucent-space-3) 0",border:"1px solid var(--lucent-border-default)",borderRadius:"var(--lucent-radius-lg)",boxShadow:"var(--lucent-shadow-sm)",background:"var(--lucent-surface-default)",...d},children:t}),s!=null&&e.jsx("aside",{style:{width:f?0:m,flexShrink:0,overflow:"hidden",overflowY:f?"hidden":"auto",background:"var(--lucent-surface-default)",transition:"width 200ms var(--lucent-easing-default)"},children:s})]}),g!=null&&e.jsx("div",{style:{flexShrink:0,height:k,zIndex:10,background:"var(--lucent-surface-default)"},children:g})]})}function Jt({state:t}){return e.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 12 12",fill:"none","aria-hidden":!0,style:{flexShrink:0,opacity:t==="none"?.35:1},children:[e.jsx("path",{d:"M6 2L9 5H3L6 2Z",fill:"currentColor",opacity:t==="desc"?.35:1}),e.jsx("path",{d:"M6 10L3 7H9L6 10Z",fill:"currentColor",opacity:t==="asc"?.35:1})]})}function J({dir:t}){return e.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none","aria-hidden":!0,children:e.jsx("path",{d:t==="left"?"M10 12L6 8l4-4":"M6 4l4 4-4 4",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function Qt({columns:t,rows:n,pageSize:a=10,page:o,onPageChange:r,emptyState:i,style:s}){const[c,f]=h.useState(null),[g,y]=h.useState(0),[d,l]=h.useState(null),u=o!==void 0,v=u?o:g,m=c?[...n].sort((p,T)=>{const E=p[c.key],w=T[c.key],S=String(E??"").localeCompare(String(w??""),void 0,{numeric:!0});return c.dir==="asc"?S:-S}):n,k=a>0?m.slice(v*a,(v+1)*a):m,b=a>0?Math.max(1,Math.ceil(m.length/a)):1,x=p=>{u||y(p),r==null||r(p)},M=p=>{f(T=>!T||T.key!==p?{key:p,dir:"asc"}:T.dir==="asc"?{key:p,dir:"desc"}:null),u||y(0),r==null||r(0)},j=[];if(b<=7)for(let p=0;p<b;p++)j.push(p);else{j.push(0),v>2&&j.push("…");for(let p=Math.max(1,v-1);p<=Math.min(b-2,v+1);p++)j.push(p);v<b-3&&j.push("…"),j.push(b-1)}return e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"var(--lucent-space-3)",...s},children:[e.jsx("div",{style:{overflowX:"auto",borderRadius:"var(--lucent-radius-lg)",border:"1px solid var(--lucent-border-default)"},children:e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-sm)"},children:[e.jsx("thead",{children:e.jsx("tr",{style:{borderBottom:"1px solid var(--lucent-border-default)"},children:t.map(p=>{const T=(c==null?void 0:c.key)===p.key?c.dir:"none";return e.jsx("th",{onClick:p.sortable?()=>M(p.key):void 0,style:{padding:"var(--lucent-space-3) var(--lucent-space-4)",textAlign:p.align??"left",fontWeight:"var(--lucent-font-weight-medium)",color:"var(--lucent-text-secondary)",background:"var(--lucent-bg-subtle)",cursor:p.sortable?"pointer":"default",userSelect:"none",whiteSpace:"nowrap",...p.width?{width:p.width}:{}},children:e.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:"var(--lucent-space-1)"},children:[p.header,p.sortable&&e.jsx(Jt,{state:T})]})},p.key)})})}),e.jsx("tbody",{children:k.length===0?e.jsx("tr",{children:e.jsx("td",{colSpan:t.length,style:{padding:"var(--lucent-space-12)",textAlign:"center"},children:i??e.jsx(z,{color:"secondary",children:"No data"})})}):k.map((p,T)=>e.jsx("tr",{onMouseEnter:()=>l(T),onMouseLeave:()=>l(null),style:{borderBottom:T<k.length-1?"1px solid var(--lucent-border-subtle)":"none",background:d===T?"var(--lucent-bg-subtle)":"var(--lucent-surface-default)",transition:"background var(--lucent-duration-fast) var(--lucent-easing-default)"},children:t.map(E=>e.jsx("td",{style:{padding:"var(--lucent-space-3) var(--lucent-space-4)",color:"var(--lucent-text-primary)",textAlign:E.align??"left",verticalAlign:"middle"},children:E.render?E.render(p,T):String(p[E.key]??"")},E.key))},T))})]})}),a>0&&m.length>0&&e.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",gap:"var(--lucent-space-3)",flexWrap:"wrap"},children:[e.jsx(z,{color:"secondary",size:"sm",children:m.length===1?"1 row":`${v*a+1}–${Math.min((v+1)*a,m.length)} of ${m.length} rows`}),e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-1)"},children:[e.jsx(O,{onClick:()=>x(v-1),disabled:v===0,"aria-label":"Previous page",children:e.jsx(J,{dir:"left"})}),j.map((p,T)=>p==="…"?e.jsx("span",{style:{padding:"0 var(--lucent-space-1)",color:"var(--lucent-text-disabled)",fontSize:"var(--lucent-font-size-sm)"},children:"…"},`ellipsis-${T}`):e.jsx(O,{onClick:()=>x(p),active:p===v,"aria-label":`Page ${p+1}`,"aria-current":p===v?"page":void 0,children:p+1},p)),e.jsx(O,{onClick:()=>x(v+1),disabled:v>=b-1,"aria-label":"Next page",children:e.jsx(J,{dir:"right"})})]})]})]})}function O({children:t,onClick:n,disabled:a,active:o,...r}){const[i,s]=h.useState(!1);return e.jsx("button",{...r,onClick:n,disabled:a,onMouseEnter:()=>s(!0),onMouseLeave:()=>s(!1),style:{display:"inline-flex",alignItems:"center",justifyContent:"center",minWidth:32,height:32,padding:"0 var(--lucent-space-2)",borderRadius:"var(--lucent-radius-md)",border:o?"1px solid var(--lucent-accent-default)":"1px solid transparent",background:o?"var(--lucent-accent-default)":i&&!a?"var(--lucent-bg-muted)":"transparent",color:o?"var(--lucent-text-on-accent)":a?"var(--lucent-text-disabled)":"var(--lucent-text-primary)",fontSize:"var(--lucent-font-size-sm)",fontFamily:"var(--lucent-font-family-base)",fontWeight:"var(--lucent-font-weight-regular)",cursor:a?"not-allowed":"pointer",transition:"background var(--lucent-duration-fast) var(--lucent-easing-default)"},children:t})}const Zt={id:"data-table",name:"DataTable",tier:"molecule",domain:"neutral",specVersion:"1.0",description:"A sortable, paginated data table with configurable columns, custom cell renderers, and keyboard-accessible pagination controls.",designIntent:"DataTable is generic over row type T so TypeScript consumers get full type safety on column keys and renderers. Sorting is client-side and composable — each column opts in via sortable:true; clicking a sorted column cycles asc → desc → unsorted. Pagination is either controlled (page prop + onPageChange) or uncontrolled (internal state). A pageSize of 0 disables pagination entirely, useful when the parent manages windowing. Column filtering is intentionally excluded here (see DataTable Filter issue #52) to keep the API focused. Row hover uses bg-subtle, not a border change, so the visual weight stays low for dense data views.",props:[{name:"columns",type:"array",required:!0,description:"Column definitions. Each column has a key, header, optional render function, optional sortable flag, optional width, and optional text align."},{name:"rows",type:"array",required:!0,description:"Array of data objects to display. The generic type T is inferred from this prop."},{name:"pageSize",type:"number",required:!1,default:"10",description:"Number of rows per page. Set to 0 to disable pagination."},{name:"page",type:"number",required:!1,description:"Controlled current page (0-indexed). When provided, the component is fully controlled."},{name:"onPageChange",type:"function",required:!1,description:"Called with the new page index whenever the page changes (from pagination controls or after a sort reset)."},{name:"emptyState",type:"ReactNode",required:!1,description:'Content to render when rows is empty. Defaults to a "No data" text.'},{name:"style",type:"object",required:!1,description:"Inline style overrides for the outer wrapper."}],usageExamples:[{title:"Basic sortable table",code:`<DataTable
112
112
  columns={[
113
113
  { key: 'name', header: 'Name', sortable: true },
114
114
  { key: 'role', header: 'Role', sortable: true },
@@ -127,7 +127,7 @@ const [results, setResults] = useState([]);
127
127
  from { opacity: 0; transform: scale(0.96) translateY(-8px); }
128
128
  to { opacity: 1; transform: scale(1) translateY(0); }
129
129
  }
130
- `;function tn(t,n){var o;const a=n.toLowerCase();return t.label.toLowerCase().includes(a)||(((o=t.description)==null?void 0:o.toLowerCase().includes(a))??!1)}function nn({commands:t,placeholder:n="Search commands…",shortcutKey:a="k",open:o,onOpenChange:r,style:i}){const s=o!==void 0,[c,f]=h.useState(!1),g=s?o:c,[y,d]=h.useState(""),[l,p]=h.useState(0),v=h.useRef(null),m=h.useRef(null),T=h.useRef(!1);if(!T.current){const w=document.createElement("style");w.textContent=en,document.head.appendChild(w),T.current=!0}const b=h.useCallback(w=>{s||f(w),r==null||r(w)},[s,r]);h.useEffect(()=>{const w=k=>{(k.metaKey||k.ctrlKey)&&k.key===a&&(k.preventDefault(),b(!g))};return window.addEventListener("keydown",w),()=>window.removeEventListener("keydown",w)},[g,a,b]),h.useEffect(()=>{g&&(d(""),p(0),setTimeout(()=>{var w;return(w=v.current)==null?void 0:w.focus()},10))},[g]);const x=y?t.filter(w=>tn(w,y)):t,M=x.filter(w=>!w.disabled);h.useEffect(()=>{const w=m.current;if(!w)return;const k=w.querySelector('[data-active="true"]');k==null||k.scrollIntoView({block:"nearest"})},[l]);const S=w=>{w.disabled||(w.onSelect(),b(!1))},u=w=>{if(w.key==="Escape"){b(!1);return}if(w.key==="ArrowDown"&&(w.preventDefault(),p(k=>Math.min(k+1,M.length-1))),w.key==="ArrowUp"&&(w.preventDefault(),p(k=>Math.max(k-1,0))),w.key==="Enter"){const k=M[l];k&&S(k)}},j=[];for(const w of x){const k=j[j.length-1];k&&k.group===w.group?k.items.push(w):j.push({group:w.group,items:[w]})}if(!g)return null;let E=0;return e.jsx("div",{role:"dialog","aria-label":"Command palette","aria-modal":"true",onClick:()=>b(!1),style:{position:"fixed",inset:0,zIndex:9e3,display:"flex",alignItems:"flex-start",justifyContent:"center",paddingTop:"15vh",background:"var(--lucent-bg-overlay)",...i},children:e.jsxs("div",{role:"combobox","aria-haspopup":"listbox","aria-expanded":"true",onClick:w=>w.stopPropagation(),style:{width:"100%",maxWidth:560,margin:"0 var(--lucent-space-4)",background:"var(--lucent-surface-overlay)",borderRadius:"var(--lucent-radius-xl)",border:"1px solid var(--lucent-border-default)",boxShadow:"var(--lucent-shadow-xl)",overflow:"hidden",animation:"lucent-palette-in 150ms var(--lucent-easing-decelerate)"},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-3)",padding:"var(--lucent-space-3) var(--lucent-space-4)",borderBottom:x.length>0?"1px solid var(--lucent-border-subtle)":"none"},children:[e.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none","aria-hidden":!0,style:{flexShrink:0,color:"var(--lucent-text-secondary)"},children:[e.jsx("circle",{cx:"7",cy:"7",r:"4.5",stroke:"currentColor",strokeWidth:"1.5"}),e.jsx("path",{d:"M10.5 10.5L13.5 13.5",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})]}),e.jsx("input",{ref:v,role:"searchbox","aria-autocomplete":"list","aria-controls":"lucent-command-list",value:y,onChange:w=>{d(w.target.value),p(0)},onKeyDown:u,placeholder:n,style:{flex:1,border:"none",outline:"none",background:"transparent",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-md)",color:"var(--lucent-text-primary)",lineHeight:"var(--lucent-line-height-base)"}}),e.jsx("kbd",{style:{display:"inline-flex",alignItems:"center",padding:"2px 6px",borderRadius:"var(--lucent-radius-sm)",border:"1px solid var(--lucent-border-default)",background:"var(--lucent-bg-muted)",fontFamily:"var(--lucent-font-family-mono)",fontSize:"var(--lucent-font-size-xs)",color:"var(--lucent-text-secondary)"},children:"Esc"})]}),e.jsx("div",{id:"lucent-command-list",role:"listbox",ref:m,style:{maxHeight:360,overflowY:"auto",padding:"var(--lucent-space-1) 0"},children:x.length===0?e.jsx("div",{style:{padding:"var(--lucent-space-8)",textAlign:"center"},children:e.jsxs(z,{color:"secondary",children:['No results for "',y,'"']})}):j.map(({group:w,items:k},N)=>e.jsxs("div",{children:[w&&e.jsx("div",{style:{padding:"var(--lucent-space-2) var(--lucent-space-4) var(--lucent-space-1)",...N>0?{borderTop:"1px solid var(--lucent-border-subtle)",marginTop:"var(--lucent-space-1)"}:{}},children:e.jsx(z,{size:"xs",color:"secondary",weight:"medium",children:w})}),k.map(D=>{const R=D.disabled??!1;let C=!1;return R||(C=E===l,E++),e.jsxs("div",{role:"option","aria-selected":C,"aria-disabled":R,"data-active":C,onClick:()=>S(D),onMouseEnter:()=>{if(!R){const I=M.indexOf(D);I!==-1&&p(I)}},style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-3)",padding:"var(--lucent-space-2) var(--lucent-space-4)",cursor:R?"not-allowed":"pointer",background:C?"var(--lucent-bg-muted)":"transparent",transition:"background var(--lucent-duration-fast) var(--lucent-easing-default)",opacity:R?.5:1},children:[D.icon&&e.jsx("span",{style:{flexShrink:0,color:"var(--lucent-text-secondary)",display:"flex"},children:D.icon}),e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsx(z,{size:"sm",weight:"medium",truncate:!0,children:D.label}),D.description&&e.jsx(z,{size:"xs",color:"secondary",truncate:!0,children:D.description})]})]},D.id)})]},N))}),e.jsx("div",{style:{display:"flex",gap:"var(--lucent-space-4)",padding:"var(--lucent-space-2) var(--lucent-space-4)",borderTop:"1px solid var(--lucent-border-subtle)",background:"var(--lucent-bg-subtle)"},children:[["↑↓","Navigate"],["↵","Select"],["Esc","Close"]].map(([w,k])=>e.jsxs("span",{style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-1)"},children:[e.jsx("kbd",{style:{padding:"1px 5px",borderRadius:"var(--lucent-radius-sm)",border:"1px solid var(--lucent-border-default)",background:"var(--lucent-surface-default)",fontFamily:"var(--lucent-font-family-mono)",fontSize:"var(--lucent-font-size-xs)",color:"var(--lucent-text-secondary)"},children:w}),e.jsx(z,{size:"xs",color:"secondary",children:k})]},w))})]})})}const an={id:"command-palette",name:"CommandPalette",tier:"overlay",domain:"neutral",specVersion:"1.0",description:"A keyboard-driven modal command palette with fuzzy search, grouped results, and a built-in ⌘K / Ctrl+K global shortcut.",designIntent:"CommandPalette renders as a portal-style overlay (fixed, full-viewport) with a centred panel that animates in with a subtle scale+fade. The ⌘K shortcut is registered as a global window listener so it works regardless of focus — consumers can override the key via shortcutKey prop. Clicking the backdrop dismisses the palette; Escape also closes it. Results are filtered client-side against label and description using simple substring match — no fuzzy library needed. Navigation is purely keyboard-driven: ↑↓ move the active index, Enter selects, mouse hover syncs the active index so mouse and keyboard stay in sync. Groups are derived from the group field on each CommandItem; the order of groups follows the order they first appear in the commands array. The component is controlled-or-uncontrolled: pass open + onOpenChange for controlled use, or omit both to use internal state.",props:[{name:"commands",type:"array",required:!0,description:"Array of CommandItem objects. Each has id, label, onSelect, and optional description, icon, group, disabled."},{name:"placeholder",type:"string",required:!1,default:'"Search commands…"',description:"Placeholder text for the search input."},{name:"shortcutKey",type:"string",required:!1,default:'"k"',description:'Key that opens the palette when pressed with Meta (Mac) or Ctrl (Windows). Defaults to "k" for ⌘K.'},{name:"open",type:"boolean",required:!1,description:"Controlled open state. When provided, the component is fully controlled."},{name:"onOpenChange",type:"function",required:!1,description:"Called when the palette requests an open/close state change."},{name:"style",type:"object",required:!1,description:"Inline style overrides for the backdrop element."}],usageExamples:[{title:"Uncontrolled with ⌘K",code:`<CommandPalette
130
+ `;function tn(t,n){var o;const a=n.toLowerCase();return t.label.toLowerCase().includes(a)||(((o=t.description)==null?void 0:o.toLowerCase().includes(a))??!1)}function nn({commands:t,placeholder:n="Search commands…",shortcutKey:a="k",open:o,onOpenChange:r,style:i}){const s=o!==void 0,[c,f]=h.useState(!1),g=s?o:c,[y,d]=h.useState(""),[l,u]=h.useState(0),v=h.useRef(null),m=h.useRef(null),k=h.useRef(!1);if(!k.current){const w=document.createElement("style");w.textContent=en,document.head.appendChild(w),k.current=!0}const b=h.useCallback(w=>{s||f(w),r==null||r(w)},[s,r]);h.useEffect(()=>{const w=S=>{(S.metaKey||S.ctrlKey)&&S.key===a&&(S.preventDefault(),b(!g))};return window.addEventListener("keydown",w),()=>window.removeEventListener("keydown",w)},[g,a,b]),h.useEffect(()=>{g&&(d(""),u(0),setTimeout(()=>{var w;return(w=v.current)==null?void 0:w.focus()},10))},[g]);const x=y?t.filter(w=>tn(w,y)):t,M=x.filter(w=>!w.disabled);h.useEffect(()=>{const w=m.current;if(!w)return;const S=w.querySelector('[data-active="true"]');S==null||S.scrollIntoView({block:"nearest"})},[l]);const j=w=>{w.disabled||(w.onSelect(),b(!1))},p=w=>{if(w.key==="Escape"){b(!1);return}if(w.key==="ArrowDown"&&(w.preventDefault(),u(S=>Math.min(S+1,M.length-1))),w.key==="ArrowUp"&&(w.preventDefault(),u(S=>Math.max(S-1,0))),w.key==="Enter"){const S=M[l];S&&j(S)}},T=[];for(const w of x){const S=T[T.length-1];S&&S.group===w.group?S.items.push(w):T.push({group:w.group,items:[w]})}if(!g)return null;let E=0;return e.jsx("div",{role:"dialog","aria-label":"Command palette","aria-modal":"true",onClick:()=>b(!1),style:{position:"fixed",inset:0,zIndex:9e3,display:"flex",alignItems:"flex-start",justifyContent:"center",paddingTop:"15vh",background:"var(--lucent-bg-overlay)",...i},children:e.jsxs("div",{role:"combobox","aria-haspopup":"listbox","aria-expanded":"true",onClick:w=>w.stopPropagation(),style:{width:"100%",maxWidth:560,margin:"0 var(--lucent-space-4)",background:"var(--lucent-surface-overlay)",borderRadius:"var(--lucent-radius-xl)",border:"1px solid var(--lucent-border-default)",boxShadow:"var(--lucent-shadow-xl)",overflow:"hidden",animation:"lucent-palette-in 150ms var(--lucent-easing-decelerate)"},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-3)",padding:"var(--lucent-space-3) var(--lucent-space-4)",borderBottom:x.length>0?"1px solid var(--lucent-border-subtle)":"none"},children:[e.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none","aria-hidden":!0,style:{flexShrink:0,color:"var(--lucent-text-secondary)"},children:[e.jsx("circle",{cx:"7",cy:"7",r:"4.5",stroke:"currentColor",strokeWidth:"1.5"}),e.jsx("path",{d:"M10.5 10.5L13.5 13.5",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})]}),e.jsx("input",{ref:v,role:"searchbox","aria-autocomplete":"list","aria-controls":"lucent-command-list",value:y,onChange:w=>{d(w.target.value),u(0)},onKeyDown:p,placeholder:n,style:{flex:1,border:"none",outline:"none",background:"transparent",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-md)",color:"var(--lucent-text-primary)",lineHeight:"var(--lucent-line-height-base)"}}),e.jsx("kbd",{style:{display:"inline-flex",alignItems:"center",padding:"2px 6px",borderRadius:"var(--lucent-radius-sm)",border:"1px solid var(--lucent-border-default)",background:"var(--lucent-bg-muted)",fontFamily:"var(--lucent-font-family-mono)",fontSize:"var(--lucent-font-size-xs)",color:"var(--lucent-text-secondary)"},children:"Esc"})]}),e.jsx("div",{id:"lucent-command-list",role:"listbox",ref:m,style:{maxHeight:360,overflowY:"auto",padding:"var(--lucent-space-1) 0"},children:x.length===0?e.jsx("div",{style:{padding:"var(--lucent-space-8)",textAlign:"center"},children:e.jsxs(z,{color:"secondary",children:['No results for "',y,'"']})}):T.map(({group:w,items:S},N)=>e.jsxs("div",{children:[w&&e.jsx("div",{style:{padding:"var(--lucent-space-2) var(--lucent-space-4) var(--lucent-space-1)",...N>0?{borderTop:"1px solid var(--lucent-border-subtle)",marginTop:"var(--lucent-space-1)"}:{}},children:e.jsx(z,{size:"xs",color:"secondary",weight:"medium",children:w})}),S.map(D=>{const R=D.disabled??!1;let C=!1;return R||(C=E===l,E++),e.jsxs("div",{role:"option","aria-selected":C,"aria-disabled":R,"data-active":C,onClick:()=>j(D),onMouseEnter:()=>{if(!R){const I=M.indexOf(D);I!==-1&&u(I)}},style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-3)",padding:"var(--lucent-space-2) var(--lucent-space-4)",cursor:R?"not-allowed":"pointer",background:C?"var(--lucent-bg-muted)":"transparent",transition:"background var(--lucent-duration-fast) var(--lucent-easing-default)",opacity:R?.5:1},children:[D.icon&&e.jsx("span",{style:{flexShrink:0,color:"var(--lucent-text-secondary)",display:"flex"},children:D.icon}),e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsx(z,{size:"sm",weight:"medium",truncate:!0,children:D.label}),D.description&&e.jsx(z,{size:"xs",color:"secondary",truncate:!0,children:D.description})]})]},D.id)})]},N))}),e.jsx("div",{style:{display:"flex",gap:"var(--lucent-space-4)",padding:"var(--lucent-space-2) var(--lucent-space-4)",borderTop:"1px solid var(--lucent-border-subtle)",background:"var(--lucent-bg-subtle)"},children:[["↑↓","Navigate"],["↵","Select"],["Esc","Close"]].map(([w,S])=>e.jsxs("span",{style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-1)"},children:[e.jsx("kbd",{style:{padding:"1px 5px",borderRadius:"var(--lucent-radius-sm)",border:"1px solid var(--lucent-border-default)",background:"var(--lucent-surface-default)",fontFamily:"var(--lucent-font-family-mono)",fontSize:"var(--lucent-font-size-xs)",color:"var(--lucent-text-secondary)"},children:w}),e.jsx(z,{size:"xs",color:"secondary",children:S})]},w))})]})})}const an={id:"command-palette",name:"CommandPalette",tier:"overlay",domain:"neutral",specVersion:"1.0",description:"A keyboard-driven modal command palette with fuzzy search, grouped results, and a built-in ⌘K / Ctrl+K global shortcut.",designIntent:"CommandPalette renders as a portal-style overlay (fixed, full-viewport) with a centred panel that animates in with a subtle scale+fade. The ⌘K shortcut is registered as a global window listener so it works regardless of focus — consumers can override the key via shortcutKey prop. Clicking the backdrop dismisses the palette; Escape also closes it. Results are filtered client-side against label and description using simple substring match — no fuzzy library needed. Navigation is purely keyboard-driven: ↑↓ move the active index, Enter selects, mouse hover syncs the active index so mouse and keyboard stay in sync. Groups are derived from the group field on each CommandItem; the order of groups follows the order they first appear in the commands array. The component is controlled-or-uncontrolled: pass open + onOpenChange for controlled use, or omit both to use internal state.",props:[{name:"commands",type:"array",required:!0,description:"Array of CommandItem objects. Each has id, label, onSelect, and optional description, icon, group, disabled."},{name:"placeholder",type:"string",required:!1,default:'"Search commands…"',description:"Placeholder text for the search input."},{name:"shortcutKey",type:"string",required:!1,default:'"k"',description:'Key that opens the palette when pressed with Meta (Mac) or Ctrl (Windows). Defaults to "k" for ⌘K.'},{name:"open",type:"boolean",required:!1,description:"Controlled open state. When provided, the component is fully controlled."},{name:"onOpenChange",type:"function",required:!1,description:"Called when the palette requests an open/close state change."},{name:"style",type:"object",required:!1,description:"Inline style overrides for the backdrop element."}],usageExamples:[{title:"Uncontrolled with ⌘K",code:`<CommandPalette
131
131
  commands={[
132
132
  { id: 'new', label: 'New document', icon: <PlusIcon />, onSelect: () => router.push('/new') },
133
133
  { id: 'settings', label: 'Settings', description: 'Open app settings', onSelect: () => router.push('/settings') },
@@ -142,7 +142,7 @@ const [results, setResults] = useState([]);
142
142
  onOpenChange={setOpen}
143
143
  shortcutKey="p"
144
144
  />
145
- </>`}],compositionGraph:[{componentId:"text",componentName:"Text",role:"Group labels, item labels, descriptions, and empty state",required:!0}],accessibility:{role:"dialog",ariaAttributes:["aria-label","aria-modal","aria-expanded","aria-selected","aria-disabled","aria-controls","aria-autocomplete"],keyboardInteractions:["⌘K / Ctrl+K to open","↑↓ to navigate","Enter to select","Escape to close"],notes:'The backdrop and panel use role="dialog" with aria-modal="true". The input is role="searchbox". The result list is role="listbox" with role="option" items. Focus is moved to the search input on open.'}};function rn({label:t,onRemove:n,disabled:a}){return e.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:4,padding:"2px 6px 2px 8px",borderRadius:"var(--lucent-radius-full)",background:"var(--lucent-bg-muted)",border:"1px solid var(--lucent-border-default)",fontSize:"var(--lucent-font-size-sm)",fontFamily:"var(--lucent-font-family-base)",color:"var(--lucent-text-primary)",lineHeight:1.4,flexShrink:0},children:[t,!a&&e.jsx("button",{type:"button",onClick:o=>{o.stopPropagation(),n()},"aria-label":`Remove ${t}`,style:{display:"inline-flex",alignItems:"center",justifyContent:"center",width:14,height:14,padding:0,border:"none",background:"none",cursor:"pointer",color:"var(--lucent-text-secondary)",borderRadius:"var(--lucent-radius-full)"},children:e.jsx("svg",{width:"10",height:"10",viewBox:"0 0 10 10",fill:"none","aria-hidden":!0,children:e.jsx("path",{d:"M2 2l6 6M8 2l-6 6",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})})})]})}function on({options:t,value:n,defaultValue:a=[],onChange:o,placeholder:r="Select…",disabled:i=!1,max:s,style:c}){const f=n!==void 0,[g,y]=h.useState(a),d=f?n:g,[l,p]=h.useState(!1),[v,m]=h.useState(""),[T,b]=h.useState(0),[x,M]=h.useState(!1),S=h.useRef(null),u=h.useRef(null),j=h.useId();h.useEffect(()=>{if(!l)return;const C=I=>{var A;(A=S.current)!=null&&A.contains(I.target)||(p(!1),m(""))};return document.addEventListener("mousedown",C),()=>document.removeEventListener("mousedown",C)},[l]);const E=C=>{const I=d.includes(C)?d.filter(A=>A!==C):s!==void 0&&d.length>=s?d:[...d,C];f||y(I),o==null||o(I)},w=C=>{const I=d.filter(A=>A!==C);f||y(I),o==null||o(I)},k=t.filter(C=>C.label.toLowerCase().includes(v.toLowerCase())),N=C=>{if(C.key==="Escape"){p(!1),m("");return}if(C.key==="ArrowDown"&&(C.preventDefault(),p(!0),b(I=>Math.min(I+1,k.length-1))),C.key==="ArrowUp"&&(C.preventDefault(),b(I=>Math.max(I-1,0))),C.key==="Enter"){C.preventDefault();const I=k[T];I&&!I.disabled&&E(I.value)}C.key==="Backspace"&&v===""&&d.length>0&&w(d[d.length-1])},D=s!==void 0&&d.length>=s,R=i?"var(--lucent-border-default)":x?"var(--lucent-accent-default)":"var(--lucent-border-default)";return e.jsxs("div",{ref:S,style:{position:"relative",...c},children:[e.jsxs("div",{onClick:()=>{var C;i||(p(!0),(C=u.current)==null||C.focus())},style:{display:"flex",flexWrap:"wrap",alignItems:"center",gap:"var(--lucent-space-1)",minHeight:38,padding:"var(--lucent-space-1) var(--lucent-space-3)",borderRadius:"var(--lucent-radius-md)",border:`1px solid ${R}`,background:i?"var(--lucent-bg-muted)":"var(--lucent-surface-default)",cursor:i?"not-allowed":"text",transition:"border-color var(--lucent-duration-fast) var(--lucent-easing-default)",outline:x?"2px solid var(--lucent-focus-ring)":"none",outlineOffset:2},children:[d.map(C=>{const I=t.find(A=>A.value===C);return I?e.jsx(rn,{label:I.label,onRemove:()=>w(C),disabled:i},C):null}),e.jsx("input",{ref:u,value:v,onChange:C=>{m(C.target.value),p(!0),b(0)},onKeyDown:N,onFocus:()=>M(!0),onBlur:()=>M(!1),disabled:i,placeholder:d.length===0?r:"","aria-autocomplete":"list","aria-controls":j,"aria-expanded":l,role:"combobox",style:{flex:"1 1 80px",minWidth:80,border:"none",outline:"none",background:"transparent",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-sm)",color:i?"var(--lucent-text-disabled)":"var(--lucent-text-primary)",cursor:i?"not-allowed":"text",padding:"2px 0"}})]}),l&&!i&&e.jsxs("div",{id:j,role:"listbox","aria-multiselectable":"true",style:{position:"absolute",top:"calc(100% + 4px)",left:0,right:0,zIndex:1e3,background:"var(--lucent-surface-overlay)",border:"1px solid var(--lucent-border-default)",borderRadius:"var(--lucent-radius-md)",boxShadow:"var(--lucent-shadow-md)",maxHeight:240,overflowY:"auto",padding:"var(--lucent-space-1) 0"},children:[k.length===0?e.jsx("div",{style:{padding:"var(--lucent-space-3) var(--lucent-space-4)"},children:e.jsx(z,{color:"secondary",size:"sm",children:"No options"})}):k.map((C,I)=>{const A=d.includes(C.value),B=I===T,L=C.disabled??!1,P=D&&!A;return e.jsxs("div",{role:"option","aria-selected":A,"aria-disabled":L||P,onClick:()=>{!L&&!P&&E(C.value)},onMouseEnter:()=>b(I),style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-2)",padding:"var(--lucent-space-2) var(--lucent-space-3)",cursor:L||P?"not-allowed":"pointer",background:B?"var(--lucent-bg-subtle)":"transparent",opacity:L||P?.5:1},children:[e.jsx("span",{style:{flexShrink:0,width:16,height:16,borderRadius:"var(--lucent-radius-sm)",border:`1.5px solid ${A?"var(--lucent-accent-default)":"var(--lucent-border-strong)"}`,background:A?"var(--lucent-accent-default)":"transparent",display:"flex",alignItems:"center",justifyContent:"center",transition:"background var(--lucent-duration-fast), border-color var(--lucent-duration-fast)"},children:A&&e.jsx("svg",{width:"10",height:"10",viewBox:"0 0 10 10",fill:"none","aria-hidden":!0,children:e.jsx("path",{d:"M2 5l2.5 2.5L8 3",stroke:"var(--lucent-text-on-accent)",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}),e.jsx(z,{size:"sm",children:C.label})]},C.value)}),D&&e.jsx("div",{style:{padding:"var(--lucent-space-2) var(--lucent-space-3)",borderTop:"1px solid var(--lucent-border-subtle)"},children:e.jsxs(z,{size:"xs",color:"secondary",children:["Max ",s," selected"]})})]})]})}const sn={id:"multi-select",name:"MultiSelect",tier:"molecule",domain:"neutral",specVersion:"1.0",description:"A tag-based multi-option selector with inline search, keyboard navigation, and an optional selection cap.",designIntent:"MultiSelect renders selected values as removable tags inside the trigger area, giving immediate visual feedback on what is selected. The search input sits inline with the tags so there is no separate search field to discover. Backspace with an empty query removes the last selected tag — a standard UX pattern in multi-select inputs. The max prop caps selection without disabling the entire component: unselected options beyond the cap are grayed out and show a hint. The dropdown closes on outside click, Escape, or when focus leaves. Checkbox indicators in the dropdown make the selected state scannable at a glance without relying solely on background color.",props:[{name:"options",type:"array",required:!0,description:"Array of { value, label, disabled? } option objects."},{name:"value",type:"array",required:!1,description:"Controlled array of selected values."},{name:"defaultValue",type:"array",required:!1,default:"[]",description:"Initial selected values for uncontrolled usage."},{name:"onChange",type:"function",required:!1,description:"Called with the updated array of selected values on each change."},{name:"placeholder",type:"string",required:!1,default:'"Select…"',description:"Placeholder shown when nothing is selected."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Disables interaction and dims the component."},{name:"max",type:"number",required:!1,description:"Maximum number of selectable options. Unselected options beyond the cap are grayed out."},{name:"style",type:"object",required:!1,description:"Inline style overrides for the outer wrapper."}],usageExamples:[{title:"Uncontrolled",code:`<MultiSelect
145
+ </>`}],compositionGraph:[{componentId:"text",componentName:"Text",role:"Group labels, item labels, descriptions, and empty state",required:!0}],accessibility:{role:"dialog",ariaAttributes:["aria-label","aria-modal","aria-expanded","aria-selected","aria-disabled","aria-controls","aria-autocomplete"],keyboardInteractions:["⌘K / Ctrl+K to open","↑↓ to navigate","Enter to select","Escape to close"],notes:'The backdrop and panel use role="dialog" with aria-modal="true". The input is role="searchbox". The result list is role="listbox" with role="option" items. Focus is moved to the search input on open.'}};function rn({label:t,onRemove:n,disabled:a}){return e.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:4,padding:"2px 6px 2px 8px",borderRadius:"var(--lucent-radius-full)",background:"var(--lucent-bg-muted)",border:"1px solid var(--lucent-border-default)",fontSize:"var(--lucent-font-size-sm)",fontFamily:"var(--lucent-font-family-base)",color:"var(--lucent-text-primary)",lineHeight:1.4,flexShrink:0},children:[t,!a&&e.jsx("button",{type:"button",onClick:o=>{o.stopPropagation(),n()},"aria-label":`Remove ${t}`,style:{display:"inline-flex",alignItems:"center",justifyContent:"center",width:14,height:14,padding:0,border:"none",background:"none",cursor:"pointer",color:"var(--lucent-text-secondary)",borderRadius:"var(--lucent-radius-full)"},children:e.jsx("svg",{width:"10",height:"10",viewBox:"0 0 10 10",fill:"none","aria-hidden":!0,children:e.jsx("path",{d:"M2 2l6 6M8 2l-6 6",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})})})]})}function on({options:t,value:n,defaultValue:a=[],onChange:o,placeholder:r="Select…",disabled:i=!1,max:s,style:c}){const f=n!==void 0,[g,y]=h.useState(a),d=f?n:g,[l,u]=h.useState(!1),[v,m]=h.useState(""),[k,b]=h.useState(0),[x,M]=h.useState(!1),j=h.useRef(null),p=h.useRef(null),T=h.useId();h.useEffect(()=>{if(!l)return;const C=I=>{var A;(A=j.current)!=null&&A.contains(I.target)||(u(!1),m(""))};return document.addEventListener("mousedown",C),()=>document.removeEventListener("mousedown",C)},[l]);const E=C=>{const I=d.includes(C)?d.filter(A=>A!==C):s!==void 0&&d.length>=s?d:[...d,C];f||y(I),o==null||o(I)},w=C=>{const I=d.filter(A=>A!==C);f||y(I),o==null||o(I)},S=t.filter(C=>C.label.toLowerCase().includes(v.toLowerCase())),N=C=>{if(C.key==="Escape"){u(!1),m("");return}if(C.key==="ArrowDown"&&(C.preventDefault(),u(!0),b(I=>Math.min(I+1,S.length-1))),C.key==="ArrowUp"&&(C.preventDefault(),b(I=>Math.max(I-1,0))),C.key==="Enter"){C.preventDefault();const I=S[k];I&&!I.disabled&&E(I.value)}C.key==="Backspace"&&v===""&&d.length>0&&w(d[d.length-1])},D=s!==void 0&&d.length>=s,R=i?"var(--lucent-border-default)":x?"var(--lucent-accent-default)":"var(--lucent-border-default)";return e.jsxs("div",{ref:j,style:{position:"relative",...c},children:[e.jsxs("div",{onClick:()=>{var C;i||(u(!0),(C=p.current)==null||C.focus())},style:{display:"flex",flexWrap:"wrap",alignItems:"center",gap:"var(--lucent-space-1)",minHeight:38,padding:"var(--lucent-space-1) var(--lucent-space-3)",borderRadius:"var(--lucent-radius-md)",border:`1px solid ${R}`,background:i?"var(--lucent-bg-muted)":"var(--lucent-surface-default)",cursor:i?"not-allowed":"text",transition:"border-color var(--lucent-duration-fast) var(--lucent-easing-default)",outline:x?"2px solid var(--lucent-focus-ring)":"none",outlineOffset:2},children:[d.map(C=>{const I=t.find(A=>A.value===C);return I?e.jsx(rn,{label:I.label,onRemove:()=>w(C),disabled:i},C):null}),e.jsx("input",{ref:p,value:v,onChange:C=>{m(C.target.value),u(!0),b(0)},onKeyDown:N,onFocus:()=>M(!0),onBlur:()=>M(!1),disabled:i,placeholder:d.length===0?r:"","aria-autocomplete":"list","aria-controls":T,"aria-expanded":l,role:"combobox",style:{flex:"1 1 80px",minWidth:80,border:"none",outline:"none",background:"transparent",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-sm)",color:i?"var(--lucent-text-disabled)":"var(--lucent-text-primary)",cursor:i?"not-allowed":"text",padding:"2px 0"}})]}),l&&!i&&e.jsxs("div",{id:T,role:"listbox","aria-multiselectable":"true",style:{position:"absolute",top:"calc(100% + 4px)",left:0,right:0,zIndex:1e3,background:"var(--lucent-surface-overlay)",border:"1px solid var(--lucent-border-default)",borderRadius:"var(--lucent-radius-md)",boxShadow:"var(--lucent-shadow-md)",maxHeight:240,overflowY:"auto",padding:"var(--lucent-space-1) 0"},children:[S.length===0?e.jsx("div",{style:{padding:"var(--lucent-space-3) var(--lucent-space-4)"},children:e.jsx(z,{color:"secondary",size:"sm",children:"No options"})}):S.map((C,I)=>{const A=d.includes(C.value),B=I===k,L=C.disabled??!1,P=D&&!A;return e.jsxs("div",{role:"option","aria-selected":A,"aria-disabled":L||P,onClick:()=>{!L&&!P&&E(C.value)},onMouseEnter:()=>b(I),style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-2)",padding:"var(--lucent-space-2) var(--lucent-space-3)",cursor:L||P?"not-allowed":"pointer",background:B?"var(--lucent-bg-subtle)":"transparent",opacity:L||P?.5:1},children:[e.jsx("span",{style:{flexShrink:0,width:16,height:16,borderRadius:"var(--lucent-radius-sm)",border:`1.5px solid ${A?"var(--lucent-accent-default)":"var(--lucent-border-strong)"}`,background:A?"var(--lucent-accent-default)":"transparent",display:"flex",alignItems:"center",justifyContent:"center",transition:"background var(--lucent-duration-fast), border-color var(--lucent-duration-fast)"},children:A&&e.jsx("svg",{width:"10",height:"10",viewBox:"0 0 10 10",fill:"none","aria-hidden":!0,children:e.jsx("path",{d:"M2 5l2.5 2.5L8 3",stroke:"var(--lucent-text-on-accent)",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}),e.jsx(z,{size:"sm",children:C.label})]},C.value)}),D&&e.jsx("div",{style:{padding:"var(--lucent-space-2) var(--lucent-space-3)",borderTop:"1px solid var(--lucent-border-subtle)"},children:e.jsxs(z,{size:"xs",color:"secondary",children:["Max ",s," selected"]})})]})]})}const sn={id:"multi-select",name:"MultiSelect",tier:"molecule",domain:"neutral",specVersion:"1.0",description:"A tag-based multi-option selector with inline search, keyboard navigation, and an optional selection cap.",designIntent:"MultiSelect renders selected values as removable tags inside the trigger area, giving immediate visual feedback on what is selected. The search input sits inline with the tags so there is no separate search field to discover. Backspace with an empty query removes the last selected tag — a standard UX pattern in multi-select inputs. The max prop caps selection without disabling the entire component: unselected options beyond the cap are grayed out and show a hint. The dropdown closes on outside click, Escape, or when focus leaves. Checkbox indicators in the dropdown make the selected state scannable at a glance without relying solely on background color.",props:[{name:"options",type:"array",required:!0,description:"Array of { value, label, disabled? } option objects."},{name:"value",type:"array",required:!1,description:"Controlled array of selected values."},{name:"defaultValue",type:"array",required:!1,default:"[]",description:"Initial selected values for uncontrolled usage."},{name:"onChange",type:"function",required:!1,description:"Called with the updated array of selected values on each change."},{name:"placeholder",type:"string",required:!1,default:'"Select…"',description:"Placeholder shown when nothing is selected."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Disables interaction and dims the component."},{name:"max",type:"number",required:!1,description:"Maximum number of selectable options. Unselected options beyond the cap are grayed out."},{name:"style",type:"object",required:!1,description:"Inline style overrides for the outer wrapper."}],usageExamples:[{title:"Uncontrolled",code:`<MultiSelect
146
146
  options={[
147
147
  { value: 'react', label: 'React' },
148
148
  { value: 'vue', label: 'Vue' },
@@ -156,14 +156,14 @@ const [results, setResults] = useState([]);
156
156
  onChange={setTags}
157
157
  max={3}
158
158
  placeholder="Up to 3 tags"
159
- />`}],compositionGraph:[{componentId:"text",componentName:"Text",role:"Option labels, empty state, and max hint",required:!0}],accessibility:{role:"combobox",ariaAttributes:["aria-autocomplete","aria-controls","aria-expanded","aria-multiselectable","aria-selected","aria-disabled"],keyboardInteractions:["↑↓ to navigate options","Enter to toggle selection","Escape to close","Backspace to remove last tag"],notes:'The input carries role="combobox" with aria-expanded and aria-controls pointing to the listbox. Each option has role="option" with aria-selected. Remove buttons on tags have descriptive aria-label.'}};function ln(t,n){return new Date(t,n+1,0).getDate()}function cn(t,n){return new Date(t,n,1).getDay()}function W(t,n){return t.getFullYear()===n.getFullYear()&&t.getMonth()===n.getMonth()&&t.getDate()===n.getDate()}function O(t,n){return new Date(t.getFullYear(),t.getMonth(),t.getDate())<new Date(n.getFullYear(),n.getMonth(),n.getDate())}function Q(t,n){return new Date(t.getFullYear(),t.getMonth(),t.getDate())>new Date(n.getFullYear(),n.getMonth(),n.getDate())}const dn=["January","February","March","April","May","June","July","August","September","October","November","December"],un=["Su","Mo","Tu","We","Th","Fr","Sa"];function $(t){return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`}function Z({dir:t,onClick:n,disabled:a}){const[o,r]=h.useState(!1);return e.jsx("button",{type:"button",onClick:n,disabled:a,onMouseEnter:()=>r(!0),onMouseLeave:()=>r(!1),"aria-label":t==="prev"?"Previous month":"Next month",style:{display:"inline-flex",alignItems:"center",justifyContent:"center",width:28,height:28,border:"none",borderRadius:"var(--lucent-radius-md)",background:o&&!a?"var(--lucent-bg-muted)":"transparent",color:a?"var(--lucent-text-disabled)":"var(--lucent-text-secondary)",cursor:a?"not-allowed":"pointer",transition:"background var(--lucent-duration-fast)"},children:e.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none","aria-hidden":!0,children:e.jsx("path",{d:t==="prev"?"M10 12L6 8l4-4":"M6 4l4 4-4 4",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})})}function H({year:t,month:n,selected:a,today:o,min:r,max:i,onSelect:s,onPrevMonth:c,onNextMonth:f,highlightRange:g}){const y=ln(t,n),d=cn(t,n),[l,p]=h.useState(null),v=[...Array(d).fill(null),...Array.from({length:y},(m,T)=>T+1)];for(;v.length%7!==0;)v.push(null);return e.jsxs("div",{children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:"var(--lucent-space-3)"},children:[e.jsx(Z,{dir:"prev",onClick:c}),e.jsxs(z,{weight:"medium",size:"sm",children:[dn[n]," ",t]}),e.jsx(Z,{dir:"next",onClick:f})]}),e.jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(7, 1fr)",gap:2,marginBottom:"var(--lucent-space-1)"},children:un.map(m=>e.jsx("div",{style:{textAlign:"center"},children:e.jsx(z,{size:"xs",color:"secondary",children:m})},m))}),e.jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(7, 1fr)",gap:2},children:v.map((m,T)=>{if(!m)return e.jsx("div",{},T);const b=new Date(t,n,m),x=a?W(b,a):!1,M=W(b,o),S=(r?O(b,r):!1)||(i?Q(b,i):!1);let u=!1;return g!=null&&g.start&&(g!=null&&g.end)&&(u=!O(b,g.start)&&!Q(b,g.end)),e.jsx("button",{type:"button",disabled:S,onClick:()=>!S&&s(b),onMouseEnter:()=>p(m),onMouseLeave:()=>p(null),"aria-label":$(b),"aria-pressed":x,style:{display:"flex",alignItems:"center",justifyContent:"center",height:32,width:"100%",border:M&&!x?"1px solid var(--lucent-border-strong)":"1px solid transparent",borderRadius:"var(--lucent-radius-md)",background:x?"var(--lucent-accent-default)":u?"var(--lucent-accent-subtle)":l===m&&!S?"var(--lucent-bg-muted)":"transparent",color:x?"var(--lucent-text-on-accent)":S?"var(--lucent-text-disabled)":"var(--lucent-text-primary)",fontSize:"var(--lucent-font-size-sm)",fontFamily:"var(--lucent-font-family-base)",fontWeight:M?"var(--lucent-font-weight-medium)":"var(--lucent-font-weight-regular)",cursor:S?"not-allowed":"pointer",transition:"background var(--lucent-duration-fast)"},children:m},T)})})]})}function pn({value:t,defaultValue:n,onChange:a,placeholder:o="Pick a date",disabled:r=!1,min:i,max:s,style:c}){const f=t!==void 0,[g,y]=h.useState(n),d=f?t:g,l=new Date,[p,v]=h.useState((d??l).getFullYear()),[m,T]=h.useState((d??l).getMonth()),[b,x]=h.useState(!1),[M,S]=h.useState(!1),u=h.useRef(null);h.useEffect(()=>{if(!b)return;const k=N=>{var D;(D=u.current)!=null&&D.contains(N.target)||x(!1)};return document.addEventListener("mousedown",k),()=>document.removeEventListener("mousedown",k)},[b]);const j=k=>{f||y(k),a==null||a(k),x(!1)},E=()=>{m===0?(T(11),v(k=>k-1)):T(k=>k-1)},w=()=>{m===11?(T(0),v(k=>k+1)):T(k=>k+1)};return e.jsxs("div",{ref:u,style:{position:"relative",display:"inline-block",...c},children:[e.jsxs("button",{type:"button",disabled:r,onClick:()=>!r&&x(k=>!k),onFocus:()=>S(!0),onBlur:()=>S(!1),"aria-haspopup":"dialog","aria-expanded":b,style:{display:"inline-flex",alignItems:"center",gap:"var(--lucent-space-2)",padding:"var(--lucent-space-2) var(--lucent-space-3)",borderRadius:"var(--lucent-radius-md)",border:`1px solid ${M?"var(--lucent-accent-default)":"var(--lucent-border-default)"}`,background:r?"var(--lucent-bg-muted)":"var(--lucent-surface-default)",color:d?"var(--lucent-text-primary)":"var(--lucent-text-secondary)",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-sm)",cursor:r?"not-allowed":"pointer",outline:M?"2px solid var(--lucent-focus-ring)":"none",outlineOffset:2,minWidth:160,transition:"border-color var(--lucent-duration-fast)"},children:[e.jsxs("svg",{width:"14",height:"14",viewBox:"0 0 14 14",fill:"none","aria-hidden":!0,style:{flexShrink:0},children:[e.jsx("rect",{x:"1",y:"2",width:"12",height:"11",rx:"2",stroke:"currentColor",strokeWidth:"1.3"}),e.jsx("path",{d:"M1 6h12",stroke:"currentColor",strokeWidth:"1.3"}),e.jsx("path",{d:"M4 1v2M10 1v2",stroke:"currentColor",strokeWidth:"1.3",strokeLinecap:"round"})]}),e.jsx("span",{style:{flex:1,textAlign:"left"},children:d?$(d):o})]}),b&&e.jsx("div",{role:"dialog","aria-label":"Date picker",style:{position:"absolute",top:"calc(100% + 4px)",left:0,zIndex:1e3,background:"var(--lucent-surface-overlay)",border:"1px solid var(--lucent-border-default)",borderRadius:"var(--lucent-radius-lg)",boxShadow:"var(--lucent-shadow-md)",padding:"var(--lucent-space-4)",minWidth:260},children:e.jsx(H,{year:p,month:m,...d!==void 0&&{selected:d},today:l,...i!==void 0&&{min:i},...s!==void 0&&{max:s},onSelect:j,onPrevMonth:E,onNextMonth:w})})]})}const fn={id:"date-picker",name:"DatePicker",tier:"molecule",domain:"neutral",specVersion:"1.0",description:"A single-date picker with a calendar popover, month navigation, today highlight, min/max constraints, and controlled or uncontrolled modes.",designIntent:`DatePicker deliberately avoids native <input type="date"> to guarantee consistent cross-browser appearance that matches the Lucent token system. The trigger button shows the selected date in YYYY-MM-DD format (ISO-sortable, unambiguous locale-wise) or a placeholder. The calendar popover renders as a role="dialog" and closes on outside click. Today is outlined rather than filled so it doesn't compete visually with the selected date. Disabled dates (outside min/max) are grayed out and non-interactive. The Calendar primitive is exported separately so DateRangePicker can compose two calendars side by side.`,props:[{name:"value",type:"object",required:!1,description:"Controlled selected Date. When provided the component is fully controlled."},{name:"defaultValue",type:"object",required:!1,description:"Initial selected Date for uncontrolled usage."},{name:"onChange",type:"function",required:!1,description:"Called with the newly selected Date when the user picks a day."},{name:"placeholder",type:"string",required:!1,default:'"Pick a date"',description:"Trigger button text when no date is selected."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Disables the trigger button and all interaction."},{name:"min",type:"object",required:!1,description:"Earliest selectable Date. Days before this are grayed out."},{name:"max",type:"object",required:!1,description:"Latest selectable Date. Days after this are grayed out."},{name:"style",type:"object",required:!1,description:"Inline style overrides for the outer wrapper."}],usageExamples:[{title:"Uncontrolled",code:"<DatePicker onChange={(d) => console.log(d)} />"},{title:"Controlled with constraints",code:`const [date, setDate] = useState<Date>();
159
+ />`}],compositionGraph:[{componentId:"text",componentName:"Text",role:"Option labels, empty state, and max hint",required:!0}],accessibility:{role:"combobox",ariaAttributes:["aria-autocomplete","aria-controls","aria-expanded","aria-multiselectable","aria-selected","aria-disabled"],keyboardInteractions:["↑↓ to navigate options","Enter to toggle selection","Escape to close","Backspace to remove last tag"],notes:'The input carries role="combobox" with aria-expanded and aria-controls pointing to the listbox. Each option has role="option" with aria-selected. Remove buttons on tags have descriptive aria-label.'}};function ln(t,n){return new Date(t,n+1,0).getDate()}function cn(t,n){return new Date(t,n,1).getDay()}function V(t,n){return t.getFullYear()===n.getFullYear()&&t.getMonth()===n.getMonth()&&t.getDate()===n.getDate()}function H(t,n){return new Date(t.getFullYear(),t.getMonth(),t.getDate())<new Date(n.getFullYear(),n.getMonth(),n.getDate())}function Q(t,n){return new Date(t.getFullYear(),t.getMonth(),t.getDate())>new Date(n.getFullYear(),n.getMonth(),n.getDate())}const dn=["January","February","March","April","May","June","July","August","September","October","November","December"],un=["Su","Mo","Tu","We","Th","Fr","Sa"];function $(t){return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`}function Z({dir:t,onClick:n,disabled:a}){const[o,r]=h.useState(!1);return e.jsx("button",{type:"button",onClick:n,disabled:a,onMouseEnter:()=>r(!0),onMouseLeave:()=>r(!1),"aria-label":t==="prev"?"Previous month":"Next month",style:{display:"inline-flex",alignItems:"center",justifyContent:"center",width:28,height:28,border:"none",borderRadius:"var(--lucent-radius-md)",background:o&&!a?"var(--lucent-bg-muted)":"transparent",color:a?"var(--lucent-text-disabled)":"var(--lucent-text-secondary)",cursor:a?"not-allowed":"pointer",transition:"background var(--lucent-duration-fast)"},children:e.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none","aria-hidden":!0,children:e.jsx("path",{d:t==="prev"?"M10 12L6 8l4-4":"M6 4l4 4-4 4",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})})}function U({year:t,month:n,selected:a,today:o,min:r,max:i,onSelect:s,onPrevMonth:c,onNextMonth:f,highlightRange:g}){const y=ln(t,n),d=cn(t,n),[l,u]=h.useState(null),v=[...Array(d).fill(null),...Array.from({length:y},(m,k)=>k+1)];for(;v.length%7!==0;)v.push(null);return e.jsxs("div",{children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:"var(--lucent-space-3)"},children:[e.jsx(Z,{dir:"prev",onClick:c}),e.jsxs(z,{weight:"medium",size:"sm",children:[dn[n]," ",t]}),e.jsx(Z,{dir:"next",onClick:f})]}),e.jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(7, 1fr)",gap:2,marginBottom:"var(--lucent-space-1)"},children:un.map(m=>e.jsx("div",{style:{textAlign:"center"},children:e.jsx(z,{size:"xs",color:"secondary",children:m})},m))}),e.jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(7, 1fr)",gap:2},children:v.map((m,k)=>{if(!m)return e.jsx("div",{},k);const b=new Date(t,n,m),x=a?V(b,a):!1,M=V(b,o),j=(r?H(b,r):!1)||(i?Q(b,i):!1);let p=!1;return g!=null&&g.start&&(g!=null&&g.end)&&(p=!H(b,g.start)&&!Q(b,g.end)),e.jsx("button",{type:"button",disabled:j,onClick:()=>!j&&s(b),onMouseEnter:()=>u(m),onMouseLeave:()=>u(null),"aria-label":$(b),"aria-pressed":x,style:{display:"flex",alignItems:"center",justifyContent:"center",height:32,width:"100%",border:M&&!x?"1px solid var(--lucent-border-strong)":"1px solid transparent",borderRadius:"var(--lucent-radius-md)",background:x?"var(--lucent-accent-default)":p?"var(--lucent-accent-subtle)":l===m&&!j?"var(--lucent-bg-muted)":"transparent",color:x?"var(--lucent-text-on-accent)":j?"var(--lucent-text-disabled)":"var(--lucent-text-primary)",fontSize:"var(--lucent-font-size-sm)",fontFamily:"var(--lucent-font-family-base)",fontWeight:M?"var(--lucent-font-weight-medium)":"var(--lucent-font-weight-regular)",cursor:j?"not-allowed":"pointer",transition:"background var(--lucent-duration-fast)"},children:m},k)})})]})}function pn({value:t,defaultValue:n,onChange:a,placeholder:o="Pick a date",disabled:r=!1,min:i,max:s,style:c}){const f=t!==void 0,[g,y]=h.useState(n),d=f?t:g,l=new Date,[u,v]=h.useState((d??l).getFullYear()),[m,k]=h.useState((d??l).getMonth()),[b,x]=h.useState(!1),[M,j]=h.useState(!1),p=h.useRef(null);h.useEffect(()=>{if(!b)return;const S=N=>{var D;(D=p.current)!=null&&D.contains(N.target)||x(!1)};return document.addEventListener("mousedown",S),()=>document.removeEventListener("mousedown",S)},[b]);const T=S=>{f||y(S),a==null||a(S),x(!1)},E=()=>{m===0?(k(11),v(S=>S-1)):k(S=>S-1)},w=()=>{m===11?(k(0),v(S=>S+1)):k(S=>S+1)};return e.jsxs("div",{ref:p,style:{position:"relative",display:"inline-block",...c},children:[e.jsxs("button",{type:"button",disabled:r,onClick:()=>!r&&x(S=>!S),onFocus:()=>j(!0),onBlur:()=>j(!1),"aria-haspopup":"dialog","aria-expanded":b,style:{display:"inline-flex",alignItems:"center",gap:"var(--lucent-space-2)",padding:"var(--lucent-space-2) var(--lucent-space-3)",borderRadius:"var(--lucent-radius-md)",border:`1px solid ${M?"var(--lucent-accent-default)":"var(--lucent-border-default)"}`,background:r?"var(--lucent-bg-muted)":"var(--lucent-surface-default)",color:d?"var(--lucent-text-primary)":"var(--lucent-text-secondary)",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-sm)",cursor:r?"not-allowed":"pointer",outline:M?"2px solid var(--lucent-focus-ring)":"none",outlineOffset:2,minWidth:160,transition:"border-color var(--lucent-duration-fast)"},children:[e.jsxs("svg",{width:"14",height:"14",viewBox:"0 0 14 14",fill:"none","aria-hidden":!0,style:{flexShrink:0},children:[e.jsx("rect",{x:"1",y:"2",width:"12",height:"11",rx:"2",stroke:"currentColor",strokeWidth:"1.3"}),e.jsx("path",{d:"M1 6h12",stroke:"currentColor",strokeWidth:"1.3"}),e.jsx("path",{d:"M4 1v2M10 1v2",stroke:"currentColor",strokeWidth:"1.3",strokeLinecap:"round"})]}),e.jsx("span",{style:{flex:1,textAlign:"left"},children:d?$(d):o})]}),b&&e.jsx("div",{role:"dialog","aria-label":"Date picker",style:{position:"absolute",top:"calc(100% + 4px)",left:0,zIndex:1e3,background:"var(--lucent-surface-overlay)",border:"1px solid var(--lucent-border-default)",borderRadius:"var(--lucent-radius-lg)",boxShadow:"var(--lucent-shadow-md)",padding:"var(--lucent-space-4)",minWidth:260},children:e.jsx(U,{year:u,month:m,...d!==void 0&&{selected:d},today:l,...i!==void 0&&{min:i},...s!==void 0&&{max:s},onSelect:T,onPrevMonth:E,onNextMonth:w})})]})}const fn={id:"date-picker",name:"DatePicker",tier:"molecule",domain:"neutral",specVersion:"1.0",description:"A single-date picker with a calendar popover, month navigation, today highlight, min/max constraints, and controlled or uncontrolled modes.",designIntent:`DatePicker deliberately avoids native <input type="date"> to guarantee consistent cross-browser appearance that matches the Lucent token system. The trigger button shows the selected date in YYYY-MM-DD format (ISO-sortable, unambiguous locale-wise) or a placeholder. The calendar popover renders as a role="dialog" and closes on outside click. Today is outlined rather than filled so it doesn't compete visually with the selected date. Disabled dates (outside min/max) are grayed out and non-interactive. The Calendar primitive is exported separately so DateRangePicker can compose two calendars side by side.`,props:[{name:"value",type:"object",required:!1,description:"Controlled selected Date. When provided the component is fully controlled."},{name:"defaultValue",type:"object",required:!1,description:"Initial selected Date for uncontrolled usage."},{name:"onChange",type:"function",required:!1,description:"Called with the newly selected Date when the user picks a day."},{name:"placeholder",type:"string",required:!1,default:'"Pick a date"',description:"Trigger button text when no date is selected."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Disables the trigger button and all interaction."},{name:"min",type:"object",required:!1,description:"Earliest selectable Date. Days before this are grayed out."},{name:"max",type:"object",required:!1,description:"Latest selectable Date. Days after this are grayed out."},{name:"style",type:"object",required:!1,description:"Inline style overrides for the outer wrapper."}],usageExamples:[{title:"Uncontrolled",code:"<DatePicker onChange={(d) => console.log(d)} />"},{title:"Controlled with constraints",code:`const [date, setDate] = useState<Date>();
160
160
  <DatePicker
161
161
  value={date}
162
162
  onChange={setDate}
163
163
  min={new Date()}
164
164
  placeholder="Select a future date"
165
- />`}],compositionGraph:[{componentId:"text",componentName:"Text",role:"Month/year header and weekday labels",required:!0}],accessibility:{role:"dialog",ariaAttributes:["aria-haspopup","aria-expanded","aria-label","aria-pressed"],keyboardInteractions:["Enter/Space to open calendar","Click day to select","Escape closes popover (click outside)"],notes:'The calendar popover is role="dialog". Each day button has aria-label with the full date and aria-pressed for selected state. Full arrow-key navigation within the calendar grid is a planned enhancement.'}};function hn(t,n){return t?W(t.start,t.end)?$(t.start):`${$(t.start)} → ${$(t.end)}`:n}function mn({value:t,defaultValue:n,onChange:a,placeholder:o="Pick a date range",disabled:r=!1,min:i,max:s,style:c}){const f=t!==void 0,[g,y]=h.useState(n),d=f?t:g,[l,p]=h.useState(null),v=new Date,[m,T]=h.useState(((d==null?void 0:d.start)??v).getFullYear()),[b,x]=h.useState(((d==null?void 0:d.start)??v).getMonth()),M=b===11?0:b+1,S=b===11?m+1:m,[u,j]=h.useState(!1),[E,w]=h.useState(!1),k=h.useRef(null);h.useEffect(()=>{if(!u)return;const I=A=>{var B;(B=k.current)!=null&&B.contains(A.target)||(j(!1),p(null))};return document.addEventListener("mousedown",I),()=>document.removeEventListener("mousedown",I)},[u]);const N=I=>{if(!l)p(I);else{const[A,B]=O(I,l)||W(I,l)?[I,l]:[l,I],L={start:A,end:B};f||y(L),a==null||a(L),p(null),j(!1)}},D=()=>{b===0?(x(11),T(I=>I-1)):x(I=>I-1)},R=()=>{b===11?(x(0),T(I=>I+1)):x(I=>I+1)},C=l?{start:l,end:l}:d?{start:d.start,end:d.end}:void 0;return e.jsxs("div",{ref:k,style:{position:"relative",display:"inline-block",...c},children:[e.jsxs("button",{type:"button",disabled:r,onClick:()=>!r&&j(I=>!I),onFocus:()=>w(!0),onBlur:()=>w(!1),"aria-haspopup":"dialog","aria-expanded":u,style:{display:"inline-flex",alignItems:"center",gap:"var(--lucent-space-2)",padding:"var(--lucent-space-2) var(--lucent-space-3)",borderRadius:"var(--lucent-radius-md)",border:`1px solid ${E?"var(--lucent-accent-default)":"var(--lucent-border-default)"}`,background:r?"var(--lucent-bg-muted)":"var(--lucent-surface-default)",color:d?"var(--lucent-text-primary)":"var(--lucent-text-secondary)",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-sm)",cursor:r?"not-allowed":"pointer",outline:E?"2px solid var(--lucent-focus-ring)":"none",outlineOffset:2,minWidth:220,transition:"border-color var(--lucent-duration-fast)"},children:[e.jsxs("svg",{width:"14",height:"14",viewBox:"0 0 14 14",fill:"none","aria-hidden":!0,style:{flexShrink:0},children:[e.jsx("rect",{x:"1",y:"2",width:"12",height:"11",rx:"2",stroke:"currentColor",strokeWidth:"1.3"}),e.jsx("path",{d:"M1 6h12",stroke:"currentColor",strokeWidth:"1.3"}),e.jsx("path",{d:"M4 1v2M10 1v2",stroke:"currentColor",strokeWidth:"1.3",strokeLinecap:"round"})]}),e.jsx("span",{style:{flex:1,textAlign:"left"},children:hn(d,o)})]}),u&&e.jsxs("div",{role:"dialog","aria-label":"Date range picker",style:{position:"absolute",top:"calc(100% + 4px)",left:0,zIndex:1e3,background:"var(--lucent-surface-overlay)",border:"1px solid var(--lucent-border-default)",borderRadius:"var(--lucent-radius-lg)",boxShadow:"var(--lucent-shadow-md)",padding:"var(--lucent-space-4)",display:"flex",gap:"var(--lucent-space-6)"},children:[e.jsx("div",{style:{minWidth:220},children:e.jsx(H,{year:m,month:b,...(d==null?void 0:d.start)!==void 0&&{selected:d.start},today:v,...i!==void 0&&{min:i},...s!==void 0&&{max:s},onSelect:N,onPrevMonth:D,onNextMonth:R,...C!==void 0&&{highlightRange:C}})}),e.jsx("div",{style:{width:1,background:"var(--lucent-border-subtle)",flexShrink:0}}),e.jsx("div",{style:{minWidth:220},children:e.jsx(H,{year:S,month:M,...(d==null?void 0:d.end)!==void 0&&{selected:d.end},today:v,...i!==void 0&&{min:i},...s!==void 0&&{max:s},onSelect:N,onPrevMonth:D,onNextMonth:R,...C!==void 0&&{highlightRange:C}})})]}),l&&u&&e.jsx("div",{style:{position:"absolute",top:"calc(100% + 4px)",left:0,zIndex:1001,pointerEvents:"none"}}),l&&u&&e.jsx("div",{style:{position:"absolute",bottom:-24,left:0},children:e.jsx(z,{size:"xs",color:"secondary",children:"Now pick the end date"})})]})}const gn={id:"date-range-picker",name:"DateRangePicker",tier:"molecule",domain:"neutral",specVersion:"1.0",description:"A two-calendar date range picker. First click sets the start date, second click sets the end; the selected interval is highlighted across both calendars.",designIntent:'DateRangePicker composes two Calendar primitives from DatePicker side by side, advancing in lockstep (left = current month, right = next month). Selection is a two-click flow: first click anchors the start, a hint appears ("Now pick the end date"), second click resolves the range with automatic start/end ordering so users can click in either direction. The highlight range (accent-subtle background) spans both calendars to give clear visual feedback for the selected interval. Navigation (prev/next month) advances both calendars together to maintain the one-month-apart constraint.',props:[{name:"value",type:"object",required:!1,description:"Controlled DateRange { start: Date; end: Date }. When provided the component is fully controlled."},{name:"defaultValue",type:"object",required:!1,description:"Initial DateRange for uncontrolled usage."},{name:"onChange",type:"function",required:!1,description:"Called with the completed DateRange after the user picks both start and end."},{name:"placeholder",type:"string",required:!1,default:'"Pick a date range"',description:"Trigger button text when no range is selected."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Disables the trigger button and all interaction."},{name:"min",type:"object",required:!1,description:"Earliest selectable Date."},{name:"max",type:"object",required:!1,description:"Latest selectable Date."},{name:"style",type:"object",required:!1,description:"Inline style overrides for the outer wrapper."}],usageExamples:[{title:"Uncontrolled",code:"<DateRangePicker onChange={({ start, end }) => console.log(start, end)} />"},{title:"Controlled",code:`const [range, setRange] = useState<DateRange>();
166
- <DateRangePicker value={range} onChange={setRange} min={new Date()} />`}],compositionGraph:[{componentId:"date-picker",componentName:"DatePicker",role:"Calendar primitive (two instances, left and right)",required:!0},{componentId:"text",componentName:"Text",role:"Mid-selection hint and calendar headers",required:!0}],accessibility:{role:"dialog",ariaAttributes:["aria-haspopup","aria-expanded","aria-label","aria-pressed"],keyboardInteractions:["Enter/Space to open","Click first day to set start","Click second day to set end","Escape/click outside to cancel"],notes:'Inherits Calendar accessibility from DatePicker. The two-step selection flow is reinforced with a visible "Now pick the end date" hint.'}};function U(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(1)} MB`}function vn(){return Math.random().toString(36).slice(2)}function bn({item:t,onRemove:n}){const[a,o]=h.useState(!1),r=t.progress,i=!!t.error;return e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-3)",padding:"var(--lucent-space-2) var(--lucent-space-3)",borderRadius:"var(--lucent-radius-md)",border:`1px solid ${i?"var(--lucent-danger-default)":"var(--lucent-border-default)"}`,background:"var(--lucent-surface-default)"},children:[e.jsxs("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none","aria-hidden":!0,style:{flexShrink:0,color:"var(--lucent-text-secondary)"},children:[e.jsx("path",{d:"M5 2h7l4 4v12a1 1 0 01-1 1H5a1 1 0 01-1-1V3a1 1 0 011-1z",stroke:"currentColor",strokeWidth:"1.3"}),e.jsx("path",{d:"M12 2v4h4",stroke:"currentColor",strokeWidth:"1.3"})]}),e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsx(z,{size:"sm",truncate:!0,children:t.file.name}),i?e.jsx(z,{size:"xs",color:"danger",children:t.error}):e.jsx(z,{size:"xs",color:"secondary",children:U(t.file.size)}),r!==void 0&&!i&&e.jsx("div",{style:{marginTop:4,height:3,borderRadius:"var(--lucent-radius-full)",background:"var(--lucent-bg-muted)",overflow:"hidden"},children:e.jsx("div",{style:{height:"100%",width:`${r}%`,borderRadius:"var(--lucent-radius-full)",background:r===100?"var(--lucent-success-default)":"var(--lucent-accent-default)",transition:"width 200ms var(--lucent-easing-default)"}})})]}),e.jsx("button",{type:"button",onClick:()=>n(t.id),onMouseEnter:()=>o(!0),onMouseLeave:()=>o(!1),"aria-label":`Remove ${t.file.name}`,style:{flexShrink:0,display:"inline-flex",alignItems:"center",justifyContent:"center",width:24,height:24,border:"none",borderRadius:"var(--lucent-radius-md)",background:a?"var(--lucent-bg-muted)":"transparent",color:"var(--lucent-text-secondary)",cursor:"pointer",transition:"background var(--lucent-duration-fast)"},children:e.jsx("svg",{width:"12",height:"12",viewBox:"0 0 12 12",fill:"none","aria-hidden":!0,children:e.jsx("path",{d:"M2 2l8 8M10 2l-8 8",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})})})]})}function yn({accept:t,multiple:n=!1,maxSize:a,value:o,onChange:r,onError:i,disabled:s=!1,style:c}){const f=o!==void 0,[g,y]=h.useState([]),d=f?o:g,[l,p]=h.useState(!1),[v,m]=h.useState(!1),T=h.useRef(null),b=h.useCallback(u=>{if(!u||s)return;const j=[];for(const w of Array.from(u)){if(a&&w.size>a){i==null||i(`"${w.name}" exceeds the ${U(a)} limit.`);continue}if(!n&&d.length+j.length>=1)break;j.push({id:vn(),file:w})}if(j.length===0)return;const E=n?[...d,...j]:j;f||y(E),r==null||r(E)},[s,d,f,a,n,r,i]),x=u=>{const j=d.filter(E=>E.id!==u);f||y(j),r==null||r(j)},M=u=>{u.preventDefault(),p(!1),b(u.dataTransfer.files)},S=u=>{b(u.target.files),u.target.value=""};return e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"var(--lucent-space-3)",...c},children:[e.jsxs("div",{role:"button",tabIndex:s?-1:0,"aria-label":"Upload files","aria-disabled":s,onClick:()=>{var u;return!s&&((u=T.current)==null?void 0:u.click())},onKeyDown:u=>{var j;(u.key==="Enter"||u.key===" ")&&(u.preventDefault(),(j=T.current)==null||j.click())},onFocus:()=>m(!0),onBlur:()=>m(!1),onDragOver:u=>{u.preventDefault(),s||p(!0)},onDragLeave:()=>p(!1),onDrop:M,style:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",gap:"var(--lucent-space-2)",padding:"var(--lucent-space-8) var(--lucent-space-6)",borderRadius:"var(--lucent-radius-lg)",border:`2px dashed ${s?"var(--lucent-border-default)":l||v?"var(--lucent-accent-default)":"var(--lucent-border-default)"}`,background:l?"var(--lucent-accent-subtle)":"var(--lucent-bg-subtle)",cursor:s?"not-allowed":"pointer",transition:"border-color var(--lucent-duration-fast), background var(--lucent-duration-fast)",outline:"none"},children:[e.jsxs("svg",{width:"32",height:"32",viewBox:"0 0 32 32",fill:"none","aria-hidden":!0,style:{color:s?"var(--lucent-text-disabled)":l?"var(--lucent-accent-default)":"var(--lucent-text-secondary)"},children:[e.jsx("path",{d:"M16 20V10M16 10l-4 4M16 10l4 4",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round"}),e.jsx("path",{d:"M8 24h16",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round"})]}),e.jsxs("div",{style:{textAlign:"center"},children:[e.jsx(z,{color:s?"disabled":"primary",weight:"medium",children:l?"Drop to upload":"Drop files here or click to browse"}),(t||a)&&e.jsx(z,{size:"xs",color:"secondary",children:[t&&`Accepted: ${t}`,a&&`Max size: ${U(a)}`].filter(Boolean).join(" · ")})]}),e.jsx("input",{ref:T,type:"file",accept:t,multiple:n,disabled:s,onChange:S,style:{display:"none"},tabIndex:-1})]}),d.length>0&&e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"var(--lucent-space-2)"},children:d.map(u=>e.jsx(bn,{item:u,onRemove:x},u.id))})]})}const xn={id:"file-upload",name:"FileUpload",tier:"molecule",domain:"neutral",specVersion:"1.0",description:"A drag-and-drop file upload zone with a file list, per-file progress bars, error display, and size/type validation.",designIntent:'FileUpload separates concerns between the drop zone (entry point) and the file list (status display). The drop zone uses a dashed border and an upload arrow icon to communicate droppability without words. Progress is modelled as a field on UploadFile rather than as a callback so the parent controls upload logic — this component is purely presentational for the upload state. The progress bar turns success-green at 100% to give clear completion feedback. Errors are shown inline on each file row (not as a toast) so the user knows exactly which file failed and why. The hidden <input type="file"> is triggered programmatically on click/keyboard so the drop zone can have a fully custom appearance.',props:[{name:"accept",type:"string",required:!1,description:'Accepted MIME types or extensions passed to the file input, e.g. "image/*,.pdf".'},{name:"multiple",type:"boolean",required:!1,default:"false",description:"Allow selecting multiple files at once."},{name:"maxSize",type:"number",required:!1,description:"Maximum file size in bytes. Files exceeding this trigger onError and are not added."},{name:"value",type:"array",required:!1,description:"Controlled array of UploadFile objects. Each has id, file (File), optional progress (0–100), and optional error string."},{name:"onChange",type:"function",required:!1,description:"Called with the updated UploadFile array after files are added or removed."},{name:"onError",type:"function",required:!1,description:"Called with an error message string when a file fails validation (e.g. exceeds maxSize)."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Disables the drop zone and all file interaction."},{name:"style",type:"object",required:!1,description:"Inline style overrides for the outer wrapper."}],usageExamples:[{title:"Uncontrolled multi-file upload",code:`<FileUpload
165
+ />`}],compositionGraph:[{componentId:"text",componentName:"Text",role:"Month/year header and weekday labels",required:!0}],accessibility:{role:"dialog",ariaAttributes:["aria-haspopup","aria-expanded","aria-label","aria-pressed"],keyboardInteractions:["Enter/Space to open calendar","Click day to select","Escape closes popover (click outside)"],notes:'The calendar popover is role="dialog". Each day button has aria-label with the full date and aria-pressed for selected state. Full arrow-key navigation within the calendar grid is a planned enhancement.'}};function hn(t,n){return t?V(t.start,t.end)?$(t.start):`${$(t.start)} → ${$(t.end)}`:n}function mn({value:t,defaultValue:n,onChange:a,placeholder:o="Pick a date range",disabled:r=!1,min:i,max:s,style:c}){const f=t!==void 0,[g,y]=h.useState(n),d=f?t:g,[l,u]=h.useState(null),v=new Date,[m,k]=h.useState(((d==null?void 0:d.start)??v).getFullYear()),[b,x]=h.useState(((d==null?void 0:d.start)??v).getMonth()),M=b===11?0:b+1,j=b===11?m+1:m,[p,T]=h.useState(!1),[E,w]=h.useState(!1),S=h.useRef(null);h.useEffect(()=>{if(!p)return;const I=A=>{var B;(B=S.current)!=null&&B.contains(A.target)||(T(!1),u(null))};return document.addEventListener("mousedown",I),()=>document.removeEventListener("mousedown",I)},[p]);const N=I=>{if(!l)u(I);else{const[A,B]=H(I,l)||V(I,l)?[I,l]:[l,I],L={start:A,end:B};f||y(L),a==null||a(L),u(null),T(!1)}},D=()=>{b===0?(x(11),k(I=>I-1)):x(I=>I-1)},R=()=>{b===11?(x(0),k(I=>I+1)):x(I=>I+1)},C=l?{start:l,end:l}:d?{start:d.start,end:d.end}:void 0;return e.jsxs("div",{ref:S,style:{position:"relative",display:"inline-block",...c},children:[e.jsxs("button",{type:"button",disabled:r,onClick:()=>!r&&T(I=>!I),onFocus:()=>w(!0),onBlur:()=>w(!1),"aria-haspopup":"dialog","aria-expanded":p,style:{display:"inline-flex",alignItems:"center",gap:"var(--lucent-space-2)",padding:"var(--lucent-space-2) var(--lucent-space-3)",borderRadius:"var(--lucent-radius-md)",border:`1px solid ${E?"var(--lucent-accent-default)":"var(--lucent-border-default)"}`,background:r?"var(--lucent-bg-muted)":"var(--lucent-surface-default)",color:d?"var(--lucent-text-primary)":"var(--lucent-text-secondary)",fontFamily:"var(--lucent-font-family-base)",fontSize:"var(--lucent-font-size-sm)",cursor:r?"not-allowed":"pointer",outline:E?"2px solid var(--lucent-focus-ring)":"none",outlineOffset:2,minWidth:220,transition:"border-color var(--lucent-duration-fast)"},children:[e.jsxs("svg",{width:"14",height:"14",viewBox:"0 0 14 14",fill:"none","aria-hidden":!0,style:{flexShrink:0},children:[e.jsx("rect",{x:"1",y:"2",width:"12",height:"11",rx:"2",stroke:"currentColor",strokeWidth:"1.3"}),e.jsx("path",{d:"M1 6h12",stroke:"currentColor",strokeWidth:"1.3"}),e.jsx("path",{d:"M4 1v2M10 1v2",stroke:"currentColor",strokeWidth:"1.3",strokeLinecap:"round"})]}),e.jsx("span",{style:{flex:1,textAlign:"left"},children:hn(d,o)})]}),p&&e.jsxs("div",{role:"dialog","aria-label":"Date range picker",style:{position:"absolute",top:"calc(100% + 4px)",left:0,zIndex:1e3,background:"var(--lucent-surface-overlay)",border:"1px solid var(--lucent-border-default)",borderRadius:"var(--lucent-radius-lg)",boxShadow:"var(--lucent-shadow-md)",padding:"var(--lucent-space-4)",display:"flex",gap:"var(--lucent-space-6)"},children:[e.jsx("div",{style:{minWidth:220},children:e.jsx(U,{year:m,month:b,...(d==null?void 0:d.start)!==void 0&&{selected:d.start},today:v,...i!==void 0&&{min:i},...s!==void 0&&{max:s},onSelect:N,onPrevMonth:D,onNextMonth:R,...C!==void 0&&{highlightRange:C}})}),e.jsx("div",{style:{width:1,background:"var(--lucent-border-subtle)",flexShrink:0}}),e.jsx("div",{style:{minWidth:220},children:e.jsx(U,{year:j,month:M,...(d==null?void 0:d.end)!==void 0&&{selected:d.end},today:v,...i!==void 0&&{min:i},...s!==void 0&&{max:s},onSelect:N,onPrevMonth:D,onNextMonth:R,...C!==void 0&&{highlightRange:C}})})]}),l&&p&&e.jsx("div",{style:{position:"absolute",top:"calc(100% + 4px)",left:0,zIndex:1001,pointerEvents:"none"}}),l&&p&&e.jsx("div",{style:{position:"absolute",bottom:-24,left:0},children:e.jsx(z,{size:"xs",color:"secondary",children:"Now pick the end date"})})]})}const gn={id:"date-range-picker",name:"DateRangePicker",tier:"molecule",domain:"neutral",specVersion:"1.0",description:"A two-calendar date range picker. First click sets the start date, second click sets the end; the selected interval is highlighted across both calendars.",designIntent:'DateRangePicker composes two Calendar primitives from DatePicker side by side, advancing in lockstep (left = current month, right = next month). Selection is a two-click flow: first click anchors the start, a hint appears ("Now pick the end date"), second click resolves the range with automatic start/end ordering so users can click in either direction. The highlight range (accent-subtle background) spans both calendars to give clear visual feedback for the selected interval. Navigation (prev/next month) advances both calendars together to maintain the one-month-apart constraint.',props:[{name:"value",type:"object",required:!1,description:"Controlled DateRange { start: Date; end: Date }. When provided the component is fully controlled."},{name:"defaultValue",type:"object",required:!1,description:"Initial DateRange for uncontrolled usage."},{name:"onChange",type:"function",required:!1,description:"Called with the completed DateRange after the user picks both start and end."},{name:"placeholder",type:"string",required:!1,default:'"Pick a date range"',description:"Trigger button text when no range is selected."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Disables the trigger button and all interaction."},{name:"min",type:"object",required:!1,description:"Earliest selectable Date."},{name:"max",type:"object",required:!1,description:"Latest selectable Date."},{name:"style",type:"object",required:!1,description:"Inline style overrides for the outer wrapper."}],usageExamples:[{title:"Uncontrolled",code:"<DateRangePicker onChange={({ start, end }) => console.log(start, end)} />"},{title:"Controlled",code:`const [range, setRange] = useState<DateRange>();
166
+ <DateRangePicker value={range} onChange={setRange} min={new Date()} />`}],compositionGraph:[{componentId:"date-picker",componentName:"DatePicker",role:"Calendar primitive (two instances, left and right)",required:!0},{componentId:"text",componentName:"Text",role:"Mid-selection hint and calendar headers",required:!0}],accessibility:{role:"dialog",ariaAttributes:["aria-haspopup","aria-expanded","aria-label","aria-pressed"],keyboardInteractions:["Enter/Space to open","Click first day to set start","Click second day to set end","Escape/click outside to cancel"],notes:'Inherits Calendar accessibility from DatePicker. The two-step selection flow is reinforced with a visible "Now pick the end date" hint.'}};function G(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(1)} MB`}function vn(){return Math.random().toString(36).slice(2)}function bn({item:t,onRemove:n}){const[a,o]=h.useState(!1),r=t.progress,i=!!t.error;return e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-3)",padding:"var(--lucent-space-2) var(--lucent-space-3)",borderRadius:"var(--lucent-radius-md)",border:`1px solid ${i?"var(--lucent-danger-default)":"var(--lucent-border-default)"}`,background:"var(--lucent-surface-default)"},children:[e.jsxs("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none","aria-hidden":!0,style:{flexShrink:0,color:"var(--lucent-text-secondary)"},children:[e.jsx("path",{d:"M5 2h7l4 4v12a1 1 0 01-1 1H5a1 1 0 01-1-1V3a1 1 0 011-1z",stroke:"currentColor",strokeWidth:"1.3"}),e.jsx("path",{d:"M12 2v4h4",stroke:"currentColor",strokeWidth:"1.3"})]}),e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsx(z,{size:"sm",truncate:!0,children:t.file.name}),i?e.jsx(z,{size:"xs",color:"danger",children:t.error}):e.jsx(z,{size:"xs",color:"secondary",children:G(t.file.size)}),r!==void 0&&!i&&e.jsx("div",{style:{marginTop:4,height:3,borderRadius:"var(--lucent-radius-full)",background:"var(--lucent-bg-muted)",overflow:"hidden"},children:e.jsx("div",{style:{height:"100%",width:`${r}%`,borderRadius:"var(--lucent-radius-full)",background:r===100?"var(--lucent-success-default)":"var(--lucent-accent-default)",transition:"width 200ms var(--lucent-easing-default)"}})})]}),e.jsx("button",{type:"button",onClick:()=>n(t.id),onMouseEnter:()=>o(!0),onMouseLeave:()=>o(!1),"aria-label":`Remove ${t.file.name}`,style:{flexShrink:0,display:"inline-flex",alignItems:"center",justifyContent:"center",width:24,height:24,border:"none",borderRadius:"var(--lucent-radius-md)",background:a?"var(--lucent-bg-muted)":"transparent",color:"var(--lucent-text-secondary)",cursor:"pointer",transition:"background var(--lucent-duration-fast)"},children:e.jsx("svg",{width:"12",height:"12",viewBox:"0 0 12 12",fill:"none","aria-hidden":!0,children:e.jsx("path",{d:"M2 2l8 8M10 2l-8 8",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})})})]})}function yn({accept:t,multiple:n=!1,maxSize:a,value:o,onChange:r,onError:i,disabled:s=!1,style:c}){const f=o!==void 0,[g,y]=h.useState([]),d=f?o:g,[l,u]=h.useState(!1),[v,m]=h.useState(!1),k=h.useRef(null),b=h.useCallback(p=>{if(!p||s)return;const T=[];for(const w of Array.from(p)){if(a&&w.size>a){i==null||i(`"${w.name}" exceeds the ${G(a)} limit.`);continue}if(!n&&d.length+T.length>=1)break;T.push({id:vn(),file:w})}if(T.length===0)return;const E=n?[...d,...T]:T;f||y(E),r==null||r(E)},[s,d,f,a,n,r,i]),x=p=>{const T=d.filter(E=>E.id!==p);f||y(T),r==null||r(T)},M=p=>{p.preventDefault(),u(!1),b(p.dataTransfer.files)},j=p=>{b(p.target.files),p.target.value=""};return e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"var(--lucent-space-3)",...c},children:[e.jsxs("div",{role:"button",tabIndex:s?-1:0,"aria-label":"Upload files","aria-disabled":s,onClick:()=>{var p;return!s&&((p=k.current)==null?void 0:p.click())},onKeyDown:p=>{var T;(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),(T=k.current)==null||T.click())},onFocus:()=>m(!0),onBlur:()=>m(!1),onDragOver:p=>{p.preventDefault(),s||u(!0)},onDragLeave:()=>u(!1),onDrop:M,style:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",gap:"var(--lucent-space-2)",padding:"var(--lucent-space-8) var(--lucent-space-6)",borderRadius:"var(--lucent-radius-lg)",border:`2px dashed ${s?"var(--lucent-border-default)":l||v?"var(--lucent-accent-default)":"var(--lucent-border-default)"}`,background:l?"var(--lucent-accent-subtle)":"var(--lucent-bg-subtle)",cursor:s?"not-allowed":"pointer",transition:"border-color var(--lucent-duration-fast), background var(--lucent-duration-fast)",outline:"none"},children:[e.jsxs("svg",{width:"32",height:"32",viewBox:"0 0 32 32",fill:"none","aria-hidden":!0,style:{color:s?"var(--lucent-text-disabled)":l?"var(--lucent-accent-default)":"var(--lucent-text-secondary)"},children:[e.jsx("path",{d:"M16 20V10M16 10l-4 4M16 10l4 4",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round"}),e.jsx("path",{d:"M8 24h16",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round"})]}),e.jsxs("div",{style:{textAlign:"center"},children:[e.jsx(z,{color:s?"disabled":"primary",weight:"medium",children:l?"Drop to upload":"Drop files here or click to browse"}),(t||a)&&e.jsx(z,{size:"xs",color:"secondary",children:[t&&`Accepted: ${t}`,a&&`Max size: ${G(a)}`].filter(Boolean).join(" · ")})]}),e.jsx("input",{ref:k,type:"file",accept:t,multiple:n,disabled:s,onChange:j,style:{display:"none"},tabIndex:-1})]}),d.length>0&&e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"var(--lucent-space-2)"},children:d.map(p=>e.jsx(bn,{item:p,onRemove:x},p.id))})]})}const xn={id:"file-upload",name:"FileUpload",tier:"molecule",domain:"neutral",specVersion:"1.0",description:"A drag-and-drop file upload zone with a file list, per-file progress bars, error display, and size/type validation.",designIntent:'FileUpload separates concerns between the drop zone (entry point) and the file list (status display). The drop zone uses a dashed border and an upload arrow icon to communicate droppability without words. Progress is modelled as a field on UploadFile rather than as a callback so the parent controls upload logic — this component is purely presentational for the upload state. The progress bar turns success-green at 100% to give clear completion feedback. Errors are shown inline on each file row (not as a toast) so the user knows exactly which file failed and why. The hidden <input type="file"> is triggered programmatically on click/keyboard so the drop zone can have a fully custom appearance.',props:[{name:"accept",type:"string",required:!1,description:'Accepted MIME types or extensions passed to the file input, e.g. "image/*,.pdf".'},{name:"multiple",type:"boolean",required:!1,default:"false",description:"Allow selecting multiple files at once."},{name:"maxSize",type:"number",required:!1,description:"Maximum file size in bytes. Files exceeding this trigger onError and are not added."},{name:"value",type:"array",required:!1,description:"Controlled array of UploadFile objects. Each has id, file (File), optional progress (0–100), and optional error string."},{name:"onChange",type:"function",required:!1,description:"Called with the updated UploadFile array after files are added or removed."},{name:"onError",type:"function",required:!1,description:"Called with an error message string when a file fails validation (e.g. exceeds maxSize)."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Disables the drop zone and all file interaction."},{name:"style",type:"object",required:!1,description:"Inline style overrides for the outer wrapper."}],usageExamples:[{title:"Uncontrolled multi-file upload",code:`<FileUpload
167
167
  multiple
168
168
  accept="image/*,.pdf"
169
169
  maxSize={5 * 1024 * 1024}
@@ -195,10 +195,10 @@ const handleChange = async (updated: UploadFile[]) => {
195
195
  { id: 'review', title: 'PR #47 merged', icon: <GitMergeIcon />, date: '3h ago' },
196
196
  { id: 'alert', title: 'Error rate spike', status: 'warning', icon: <AlertIcon />, date: '4h ago' },
197
197
  ]}
198
- />`}],compositionGraph:[{componentId:"text",componentName:"Text",role:"Event title, description, and date label",required:!0}],accessibility:{role:"list",ariaAttributes:[],keyboardInteractions:["Standard document flow — no interactive elements unless custom icons include them"],notes:"Timeline is a semantic <ol> with <li> items. It is non-interactive by default. If items contain interactive elements (links, buttons), those elements carry their own keyboard behaviour."}},se={fontFamilyBase:'"DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',fontFamilyMono:'"DM Mono", "Fira Code", "Cascadia Code", monospace',fontFamilyDisplay:'"Georama", "DM Sans", sans-serif',fontSizeXs:"0.75rem",fontSizeSm:"0.875rem",fontSizeMd:"1rem",fontSizeLg:"1.125rem",fontSizeXl:"1.25rem",fontSize2xl:"1.5rem",fontSize3xl:"1.875rem",fontWeightRegular:"400",fontWeightMedium:"500",fontWeightSemibold:"600",fontWeightBold:"700",lineHeightTight:"1.25",lineHeightBase:"1.5",lineHeightRelaxed:"1.75",letterSpacingTight:"-0.02em",letterSpacingBase:"0em",letterSpacingWide:"0.04em"},le={space0:"0px",space1:"0.25rem",space2:"0.5rem",space3:"0.75rem",space4:"1rem",space5:"1.25rem",space6:"1.5rem",space8:"2rem",space10:"2.5rem",space12:"3rem",space16:"4rem",space20:"5rem",space24:"6rem"},ce={radiusNone:"0px",radiusSm:"0.25rem",radiusMd:"0.375rem",radiusLg:"0.5rem",radiusXl:"0.75rem",radiusFull:"9999px"},de={durationFast:"100ms",durationBase:"200ms",durationSlow:"350ms",easingDefault:"cubic-bezier(0.4, 0, 0.2, 1)",easingEmphasized:"cubic-bezier(0.2, 0, 0, 1)",easingDecelerate:"cubic-bezier(0, 0, 0.2, 1)"},Cn={shadowNone:"none",shadowSm:"0 1px 2px 0 rgb(0 0 0 / 0.05)",shadowMd:"0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",shadowLg:"0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",shadowXl:"0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)"},In={shadowNone:"none",shadowSm:"0 1px 2px 0 rgb(0 0 0 / 0.3)",shadowMd:"0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.4)",shadowLg:"0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.4)",shadowXl:"0 20px 25px -5px rgb(0 0 0 / 0.5), 0 8px 10px -6px rgb(0 0 0 / 0.5)"},_={...se,...le,...ce,...de,...Cn,bgBase:"#ffffff",bgSubtle:"#f9fafb",bgMuted:"#f3f4f6",bgOverlay:"rgb(0 0 0 / 0.4)",surfaceDefault:"#ffffff",surfaceRaised:"#ffffff",surfaceOverlay:"#ffffff",borderDefault:"#e5e7eb",borderSubtle:"#f3f4f6",borderStrong:"#9ca3af",textPrimary:"#111827",textSecondary:"#6b7280",textDisabled:"#9ca3af",textInverse:"#ffffff",textOnAccent:"#ffffff",accentDefault:"#111827",accentHover:"#1f2937",accentActive:"#374151",accentSubtle:"#f3f4f6",successDefault:"#16a34a",successSubtle:"#f0fdf4",successText:"#15803d",warningDefault:"#d97706",warningSubtle:"#fffbeb",warningText:"#b45309",dangerDefault:"#dc2626",dangerHover:"#b91c1c",dangerSubtle:"#fef2f2",dangerText:"#b91c1c",infoDefault:"#2563eb",infoSubtle:"#eff6ff",infoText:"#1d4ed8",focusRing:"#111827"},ue={...se,...le,...ce,...de,...In,bgBase:"#0b0d12",bgSubtle:"#111318",bgMuted:"#1c1f2a",bgOverlay:"rgb(0 0 0 / 0.6)",surfaceDefault:"#111318",surfaceRaised:"#1c1f2a",surfaceOverlay:"#1c1f2a",borderDefault:"#1c1f2a",borderSubtle:"#16181f",borderStrong:"#4a4d57",textPrimary:"#f0ede6",textSecondary:"#9ca3af",textDisabled:"#4a4d57",textInverse:"#0b0d12",textOnAccent:"#111827",accentDefault:"#f9fafb",accentHover:"#e5e7eb",accentActive:"#d1d5db",accentSubtle:"rgb(249 250 251 / 0.1)",successDefault:"#22c55e",successSubtle:"rgb(34 197 94 / 0.1)",successText:"#4ade80",warningDefault:"#f59e0b",warningSubtle:"rgb(245 158 11 / 0.1)",warningText:"#fbbf24",dangerDefault:"#ef4444",dangerHover:"#f87171",dangerSubtle:"rgb(239 68 68 / 0.1)",dangerText:"#f87171",infoDefault:"#3b82f6",infoSubtle:"rgb(59 130 246 / 0.1)",infoText:"#60a5fa",focusRing:"#f9fafb"};function Mn(t){return"--lucent-"+t.replace(/([A-Z])/g,n=>`-${n.toLowerCase()}`).replace(/([a-z])(\d)/g,(n,a,o)=>`${a}-${o}`)}function pe(t,n=":root"){const a=Object.entries(t).map(([o,r])=>` ${Mn(o)}: ${r};`).join(`
198
+ />`}],compositionGraph:[{componentId:"text",componentName:"Text",role:"Event title, description, and date label",required:!0}],accessibility:{role:"list",ariaAttributes:[],keyboardInteractions:["Standard document flow — no interactive elements unless custom icons include them"],notes:"Timeline is a semantic <ol> with <li> items. It is non-interactive by default. If items contain interactive elements (links, buttons), those elements carry their own keyboard behaviour."}},se={fontFamilyBase:'"DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',fontFamilyMono:'"DM Mono", "Fira Code", "Cascadia Code", monospace',fontFamilyDisplay:'"Georama", "DM Sans", sans-serif',fontSizeXs:"0.75rem",fontSizeSm:"0.875rem",fontSizeMd:"1rem",fontSizeLg:"1.125rem",fontSizeXl:"1.25rem",fontSize2xl:"1.5rem",fontSize3xl:"1.875rem",fontWeightRegular:"400",fontWeightMedium:"500",fontWeightSemibold:"600",fontWeightBold:"700",lineHeightTight:"1.25",lineHeightBase:"1.5",lineHeightRelaxed:"1.75",letterSpacingTight:"-0.02em",letterSpacingBase:"0em",letterSpacingWide:"0.04em"},le={space0:"0px",space1:"0.25rem",space2:"0.5rem",space3:"0.75rem",space4:"1rem",space5:"1.25rem",space6:"1.5rem",space8:"2rem",space10:"2.5rem",space12:"3rem",space16:"4rem",space20:"5rem",space24:"6rem"},ce={radiusNone:"0px",radiusSm:"0.25rem",radiusMd:"0.375rem",radiusLg:"0.5rem",radiusXl:"0.75rem",radiusFull:"9999px"},de={durationFast:"100ms",durationBase:"200ms",durationSlow:"350ms",easingDefault:"cubic-bezier(0.4, 0, 0.2, 1)",easingEmphasized:"cubic-bezier(0.2, 0, 0, 1)",easingDecelerate:"cubic-bezier(0, 0, 0.2, 1)"},Cn={shadowNone:"none",shadowSm:"0 1px 2px 0 rgb(0 0 0 / 0.05)",shadowMd:"0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",shadowLg:"0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",shadowXl:"0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)"},In={shadowNone:"none",shadowSm:"0 1px 2px 0 rgb(0 0 0 / 0.3)",shadowMd:"0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.4)",shadowLg:"0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.4)",shadowXl:"0 20px 25px -5px rgb(0 0 0 / 0.5), 0 8px 10px -6px rgb(0 0 0 / 0.5)"},Y={...se,...le,...ce,...de,...Cn,bgBase:"#ffffff",bgSubtle:"#f9fafb",bgMuted:"#f3f4f6",bgOverlay:"rgb(0 0 0 / 0.4)",surfaceDefault:"#ffffff",surfaceRaised:"#ffffff",surfaceOverlay:"#ffffff",borderDefault:"#e5e7eb",borderSubtle:"#f3f4f6",borderStrong:"#9ca3af",textPrimary:"#111827",textSecondary:"#6b7280",textDisabled:"#9ca3af",textInverse:"#ffffff",textOnAccent:"#ffffff",accentDefault:"#111827",accentHover:"#1f2937",accentActive:"#374151",accentSubtle:"#f3f4f6",successDefault:"#16a34a",successSubtle:"#f0fdf4",successText:"#15803d",warningDefault:"#d97706",warningSubtle:"#fffbeb",warningText:"#b45309",dangerDefault:"#dc2626",dangerHover:"#b91c1c",dangerSubtle:"#fef2f2",dangerText:"#b91c1c",infoDefault:"#2563eb",infoSubtle:"#eff6ff",infoText:"#1d4ed8",focusRing:"#111827"},ue={...se,...le,...ce,...de,...In,bgBase:"#0b0d12",bgSubtle:"#111318",bgMuted:"#1c1f2a",bgOverlay:"rgb(0 0 0 / 0.6)",surfaceDefault:"#111318",surfaceRaised:"#1c1f2a",surfaceOverlay:"#1c1f2a",borderDefault:"#1c1f2a",borderSubtle:"#16181f",borderStrong:"#4a4d57",textPrimary:"#f0ede6",textSecondary:"#9ca3af",textDisabled:"#4a4d57",textInverse:"#0b0d12",textOnAccent:"#111827",accentDefault:"#f9fafb",accentHover:"#e5e7eb",accentActive:"#d1d5db",accentSubtle:"rgb(249 250 251 / 0.1)",successDefault:"#22c55e",successSubtle:"rgb(34 197 94 / 0.1)",successText:"#4ade80",warningDefault:"#f59e0b",warningSubtle:"rgb(245 158 11 / 0.1)",warningText:"#fbbf24",dangerDefault:"#ef4444",dangerHover:"#f87171",dangerSubtle:"rgb(239 68 68 / 0.1)",dangerText:"#f87171",infoDefault:"#3b82f6",infoSubtle:"rgb(59 130 246 / 0.1)",infoText:"#60a5fa",focusRing:"#f9fafb"};function Mn(t){return"--lucent-"+t.replace(/([A-Z])/g,n=>`-${n.toLowerCase()}`).replace(/([a-z])(\d)/g,(n,a,o)=>`${a}-${o}`)}function pe(t,n=":root"){const a=Object.entries(t).map(([o,r])=>` ${Mn(o)}: ${r};`).join(`
199
199
  `);return`${n} {
200
200
  ${a}
201
- }`}function zn(t){const n=parseInt(t.slice(1,3),16)/255,a=parseInt(t.slice(3,5),16)/255,o=parseInt(t.slice(5,7),16)/255,r=i=>i<=.03928?i/12.92:Math.pow((i+.055)/1.055,2.4);return .2126*r(n)+.7152*r(a)+.0722*r(o)}function fe(t){return zn(t)<.179?"#ffffff":"#000000"}const he=h.createContext({theme:"light",tokens:_});function En({theme:t="light",tokens:n,children:a}){const o=h.useId().replace(/:/g,""),r=t==="dark"?ue:_,i=n?{...r,...n}:r,s={...i,textOnAccent:(n==null?void 0:n.textOnAccent)??fe(i.accentDefault)},c=`html { font-size: 13px; }
201
+ }`}function zn(t){const n=parseInt(t.slice(1,3),16)/255,a=parseInt(t.slice(3,5),16)/255,o=parseInt(t.slice(5,7),16)/255,r=i=>i<=.03928?i/12.92:Math.pow((i+.055)/1.055,2.4);return .2126*r(n)+.7152*r(a)+.0722*r(o)}function fe(t){return zn(t)<.179?"#ffffff":"#000000"}const he=h.createContext({theme:"light",tokens:Y});function En({theme:t="light",tokens:n,children:a}){const o=h.useId().replace(/:/g,""),r=t==="dark"?ue:Y,i=n?{...r,...n}:r,s={...i,textOnAccent:(n==null?void 0:n.textOnAccent)??fe(i.accentDefault)},c=`html { font-size: 13px; }
202
202
  `+pe(s,":root");return h.useLayoutEffect(()=>{let f=document.getElementById(`lucent-tokens-${o}`);return f||(f=document.createElement("style"),f.id=`lucent-tokens-${o}`,document.head.appendChild(f)),f.textContent=c,()=>{var g;(g=document.getElementById(`lucent-tokens-${o}`))==null||g.remove()}},[o,c]),e.jsx(he.Provider,{value:{theme:t,tokens:s},children:a})}function qn(){return h.useContext(he)}const Dn={accentDefault:"#e9c96b",accentHover:"#ddb84e",accentActive:"#c9a33b",accentSubtle:"#fef9ec",focusRing:"#e9c96b"};function F(t,n){return{field:t,message:n}}function me(t){const n=[];if(typeof t!="object"||t===null)return{valid:!1,errors:[F("manifest","Must be a non-null object")]};const a=t,o=["id","name","description","designIntent","specVersion"];for(const i of o)(typeof a[i]!="string"||a[i].trim()==="")&&n.push(F(i,"Must be a non-empty string"));typeof a.id=="string"&&!/^[a-z][a-z0-9-]*$/.test(a.id)&&n.push(F("id",'Must be kebab-case (e.g. "button", "form-field")'));const r=["atom","molecule","block","flow","overlay"];return r.includes(a.tier)||n.push(F("tier",`Must be one of: ${r.join(", ")}`)),(typeof a.domain!="string"||a.domain.trim()==="")&&n.push(F("domain","Must be a non-empty string")),Array.isArray(a.props)?a.props.forEach((i,s)=>{const c=i,f=`props[${s}]`;(typeof c.name!="string"||c.name==="")&&n.push(F(`${f}.name`,"Must be a non-empty string")),(typeof c.type!="string"||c.type==="")&&n.push(F(`${f}.type`,"Must be a non-empty string")),typeof c.required!="boolean"&&n.push(F(`${f}.required`,"Must be a boolean")),(typeof c.description!="string"||c.description==="")&&n.push(F(`${f}.description`,"Must be a non-empty string"))}):n.push(F("props","Must be an array")),Array.isArray(a.usageExamples)?a.usageExamples.length===0?n.push(F("usageExamples","Must have at least one example")):a.usageExamples.forEach((i,s)=>{const c=i,f=`usageExamples[${s}]`;(typeof c.title!="string"||c.title==="")&&n.push(F(`${f}.title`,"Must be a non-empty string")),(typeof c.code!="string"||c.code==="")&&n.push(F(`${f}.code`,"Must be a non-empty string"))}):n.push(F("usageExamples","Must be an array")),Array.isArray(a.compositionGraph)||n.push(F("compositionGraph","Must be an array (empty array is fine for atoms)")),typeof a.specVersion=="string"&&!/^\d+\.\d+$/.test(a.specVersion)&&n.push(F("specVersion",'Must be "MAJOR.MINOR" format, e.g. "0.1"')),{valid:n.length===0,errors:n}}function An(t){const n=me(t);if(!n.valid){const a=n.errors.map(o=>` ${o.field}: ${o.message}`).join(`
203
203
  `);throw new Error(`Invalid ComponentManifest:
204
- ${a}`)}}function Fn(t){if(typeof t!="object"||t===null)return!1;const n=t;return typeof n.name=="string"&&typeof n.type=="string"&&typeof n.required=="boolean"&&typeof n.description=="string"}const Rn="1.0",Nn="0.1.0";exports.Alert=Lt;exports.AlertManifest=Bt;exports.Avatar=Ae;exports.AvatarManifest=Fe;exports.Badge=Me;exports.BadgeManifest=ze;exports.Breadcrumb=Ut;exports.Button=ee;exports.ButtonManifest=Se;exports.COMMAND_PALETTE_MANIFEST=an;exports.Card=Mt;exports.CardManifest=zt;exports.Checkbox=ae;exports.CheckboxManifest=Ve;exports.Collapsible=Yt;exports.CommandPalette=nn;exports.DATA_TABLE_MANIFEST=Zt;exports.DATE_PICKER_MANIFEST=fn;exports.DATE_RANGE_PICKER_MANIFEST=gn;exports.DataTable=Qt;exports.DatePicker=pn;exports.DateRangePicker=mn;exports.Divider=Be;exports.DividerManifest=$e;exports.EmptyState=$t;exports.EmptyStateManifest=Pt;exports.FILE_UPLOAD_MANIFEST=xn;exports.FileUpload=yn;exports.FormField=yt;exports.FormFieldManifest=xt;exports.Icon=dt;exports.IconManifest=ut;exports.Input=G;exports.InputManifest=je;exports.LUCENT_UI_VERSION=Nn;exports.LucentProvider=En;exports.MANIFEST_SPEC_VERSION=Rn;exports.MULTI_SELECT_MANIFEST=sn;exports.MultiSelect=on;exports.NavLink=bt;exports.PageLayout=Xt;exports.Radio=Ue;exports.RadioGroup=oe;exports.RadioGroupUncontrolled=Ge;exports.RadioManifest=_e;exports.SearchInput=St;exports.SearchInputManifest=jt;exports.Select=ie;exports.SelectManifest=et;exports.Skeleton=Ot;exports.SkeletonManifest=Ht;exports.Spinner=ne;exports.SpinnerManifest=Le;exports.TIMELINE_MANIFEST=Tn;exports.Tabs=Gt;exports.Tag=at;exports.TagManifest=rt;exports.Text=z;exports.TextManifest=vt;exports.Textarea=te;exports.TextareaManifest=Te;exports.Timeline=jn;exports.Toggle=Xe;exports.ToggleManifest=Je;exports.Tooltip=st;exports.TooltipManifest=lt;exports.assertManifest=An;exports.brandTokens=Dn;exports.darkTokens=ue;exports.getContrastText=fe;exports.isValidPropDescriptor=Fn;exports.lightTokens=_;exports.makeLibraryCSS=pe;exports.useLucent=qn;exports.validateManifest=me;
204
+ ${a}`)}}function Fn(t){if(typeof t!="object"||t===null)return!1;const n=t;return typeof n.name=="string"&&typeof n.type=="string"&&typeof n.required=="boolean"&&typeof n.description=="string"}const Rn="1.0",Nn="0.1.0";exports.Alert=Lt;exports.AlertManifest=Bt;exports.Avatar=Ae;exports.AvatarManifest=Fe;exports.Badge=Me;exports.BadgeManifest=ze;exports.Breadcrumb=Ut;exports.Button=ee;exports.ButtonManifest=Se;exports.COMMAND_PALETTE_MANIFEST=an;exports.Card=Mt;exports.CardManifest=zt;exports.Checkbox=ae;exports.CheckboxManifest=Ve;exports.Collapsible=Yt;exports.CommandPalette=nn;exports.DATA_TABLE_MANIFEST=Zt;exports.DATE_PICKER_MANIFEST=fn;exports.DATE_RANGE_PICKER_MANIFEST=gn;exports.DataTable=Qt;exports.DatePicker=pn;exports.DateRangePicker=mn;exports.Divider=Be;exports.DividerManifest=$e;exports.EmptyState=$t;exports.EmptyStateManifest=Pt;exports.FILE_UPLOAD_MANIFEST=xn;exports.FileUpload=yn;exports.FormField=yt;exports.FormFieldManifest=xt;exports.Icon=dt;exports.IconManifest=ut;exports.Input=_;exports.InputManifest=je;exports.LUCENT_UI_VERSION=Nn;exports.LucentProvider=En;exports.MANIFEST_SPEC_VERSION=Rn;exports.MULTI_SELECT_MANIFEST=sn;exports.MultiSelect=on;exports.NavLink=bt;exports.PageLayout=Xt;exports.Radio=Ue;exports.RadioGroup=oe;exports.RadioGroupUncontrolled=Ge;exports.RadioManifest=_e;exports.SearchInput=St;exports.SearchInputManifest=jt;exports.Select=ie;exports.SelectManifest=et;exports.Skeleton=Ot;exports.SkeletonManifest=Ht;exports.Spinner=ne;exports.SpinnerManifest=Le;exports.TIMELINE_MANIFEST=Tn;exports.Tabs=Gt;exports.Tag=at;exports.TagManifest=rt;exports.Text=z;exports.TextManifest=vt;exports.Textarea=te;exports.TextareaManifest=Te;exports.Timeline=jn;exports.Toggle=Xe;exports.ToggleManifest=Je;exports.Tooltip=st;exports.TooltipManifest=lt;exports.assertManifest=An;exports.brandTokens=Dn;exports.darkTokens=ue;exports.getContrastText=fe;exports.isValidPropDescriptor=Fn;exports.lightTokens=Y;exports.makeLibraryCSS=pe;exports.useLucent=qn;exports.validateManifest=me;