lucent-ui 0.25.1 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.js +132 -132
- package/dist-server/server/index.js +112 -3
- package/dist-server/server/recipe-registry.js +16 -0
- package/dist-server/src/components/atoms/Divider/Divider.manifest.js +1 -1
- package/dist-server/src/manifest/recipes/action-bar.recipe.js +91 -0
- package/dist-server/src/manifest/recipes/collapsible-card.recipe.js +100 -0
- package/dist-server/src/manifest/recipes/empty-state-card.recipe.js +72 -0
- package/dist-server/src/manifest/recipes/form-layout.recipe.js +98 -0
- package/dist-server/src/manifest/recipes/index.js +7 -0
- package/dist-server/src/manifest/recipes/profile-card.recipe.js +101 -0
- package/dist-server/src/manifest/recipes/settings-panel.recipe.js +167 -0
- package/dist-server/src/manifest/recipes/stats-row.recipe.js +106 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
@keyframes lucent-chip-pulse {
|
|
3
3
|
0% { transform: scale(1); opacity: 0.6; }
|
|
4
4
|
100% { transform: scale(2.8); opacity: 0; }
|
|
5
|
-
}`;function Yt({children:e,variant:n="neutral",size:r="md",onDismiss:o,onClick:a,leftIcon:i,swatch:l,dot:s=!1,pulse:c=!1,borderless:u=!1,ghost:p=!1,disabled:f=!1,style:x}){const m=Gn[n],w=Un[r],[g,h]=d.useState(!1),v=s&&c,b=s&&!e,y=!f&&(o||a),C=`color-mix(in srgb, ${m.color} 8%, transparent)`,T=w.dotSize*3,S={display:"inline-flex",alignItems:"center",justifyContent:b?"center":void 0,gap:b?void 0:w.gap,height:b?T:w.height,width:b?T:void 0,padding:b?0:o?w.paddingDismiss:w.padding,fontSize:w.fontSize,fontFamily:"var(--lucent-font-family-base)",fontWeight:"var(--lucent-font-weight-medium)",lineHeight:1,borderRadius:b?"var(--lucent-radius-full)":"var(--lucent-radius-lg)",background:p?g&&y?C:"transparent":g&&y?m.hoverBg:m.bg,color:m.color,border:p||u?"1px solid transparent":`1px solid ${g&&y?m.hoverBorder:m.border}`,whiteSpace:"nowrap",boxSizing:"border-box",opacity:f?.5:1,transform:g&&y?"translateY(-1px)":"none",boxShadow:g&&y&&!p?`0 2px 4px ${m.hoverBorder}22`:"none",transition:["transform var(--lucent-duration-fast) var(--lucent-easing-default)","box-shadow var(--lucent-duration-fast) var(--lucent-easing-default)","border-color var(--lucent-duration-fast) var(--lucent-easing-default)","background var(--lucent-duration-fast) var(--lucent-easing-default)"].join(", "),cursor:y?"pointer":"default",...a?{outline:"none"}:{},...x},B=t.jsxs(t.Fragment,{children:[v&&t.jsx("style",{children:_n}),l&&t.jsx("span",{style:{width:w.dotSize+2,height:w.dotSize+2,borderRadius:"50%",background:l,border:"1px solid rgba(0,0,0,0.1)",flexShrink:0}}),s&&!l&&t.jsxs("span",{style:{position:"relative",width:w.dotSize,height:w.dotSize,flexShrink:0},children:[t.jsx("span",{style:{position:"absolute",inset:0,borderRadius:"50%",background:"currentColor"}}),v&&t.jsx("span",{style:{position:"absolute",inset:0,borderRadius:"50%",background:"currentColor",animation:"lucent-chip-pulse 1.5s ease-out infinite"}})]}),i&&!l&&!s&&t.jsx("span",{style:{display:"inline-flex",alignItems:"center",justifyContent:"center",width:w.iconSize,height:w.iconSize,flexShrink:0},children:i}),e,o&&t.jsx("button",{type:"button",onClick:f?void 0:z=>{z.stopPropagation(),o()},disabled:f,"aria-label":"Dismiss",style:{display:"inline-flex",alignItems:"center",justifyContent:"center",width:w.iconSize+2,height:w.iconSize+2,padding:0,border:"none",borderRadius:"var(--lucent-radius-lg)",background:"transparent",color:"inherit",cursor:f?"not-allowed":"pointer",flexShrink:0,lineHeight:1},children:t.jsx("svg",{width:w.iconSize-2,height:w.iconSize-2,viewBox:"0 0 10 10",fill:"none",stroke:"currentColor",strokeWidth:1.5,strokeLinecap:"round",children:t.jsx("path",{d:"M2 2L8 8M8 2L2 8"})})})]}),I={onMouseEnter:()=>{f||h(!0)},onMouseLeave:()=>h(!1)};return a?t.jsx("button",{type:"button",onClick:f?void 0:a,disabled:f,style:S,...I,children:B}):t.jsx("span",{style:S,...I,children:B})}const Yn={id:"chip",name:"Chip",tier:"atom",domain:"neutral",specVersion:"1.0",description:"A compact label for filters, tags, statuses, and categories. Combines the roles of Badge and Tag into a single flexible component.",designIntent:"Chip is the universal label primitive — use it anywhere you need a compact visual marker. It replaces both Badge (static status) and Tag (dismissible filter) with a single component. Use `onDismiss` for removable chips (filters, multi-select values). Use `onClick` for clickable/selectable chips (filter toggles, category navigation). Use `dot` for status indicators (online/offline). Use `dot` + `pulse` to show in-progress or live states (deploying, syncing, live incident). Use `ghost` + `dot` for subtle inline status that blends into surrounding text or table rows. Omit `children` with `dot` for a minimal dot-only indicator — great for table cells or avatar badges. Use `swatch` for color-coded categories. Use `leftIcon` for chips with leading icons (folders, file types, flags). Use `borderless` for a softer, filled-only appearance in dense UIs. Variant conveys semantic meaning — default to neutral for user-generated content.",props:[{name:"children",type:"ReactNode",required:!1,description:"Chip label content. When omitted with dot=true, renders as a compact dot-only indicator."},{name:"variant",type:"enum",required:!1,default:"neutral",description:"Colour scheme conveying semantic meaning.",enumValues:["neutral","accent","success","warning","danger","info"]},{name:"size",type:"enum",required:!1,default:"md",description:"Controls height, font size, and icon size.",enumValues:["sm","md","lg"]},{name:"onDismiss",type:"function",required:!1,description:"Renders an x button that calls this handler. Use for removable filters and multi-select values."},{name:"onClick",type:"function",required:!1,description:"Makes the chip clickable (renders as <button>). Use for filter toggles and category links."},{name:"leftIcon",type:"ReactNode",required:!1,description:"Icon or element rendered before the label (emoji, flag, avatar)."},{name:"swatch",type:"string",required:!1,description:"Hex color string. Renders a small color dot before the label."},{name:"dot",type:"boolean",required:!1,default:"false",description:"Renders a status dot using the variant colour. Omit children for a compact dot-only indicator."},{name:"pulse",type:"boolean",required:!1,default:"false",description:"Adds a pulsing ring animation to the status dot. Only applies when dot=true. Use for running, deploying, or live states."},{name:"borderless",type:"boolean",required:!1,default:"false",description:"Removes the border for a filled-only look."},{name:"ghost",type:"boolean",required:!1,default:"false",description:"Transparent background with text color only. Pairs well with dot for subtle inline statuses in tables or lists."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Dims the chip and prevents interaction."},{name:"style",type:"object",required:!1,description:"Inline style overrides."}],usageExamples:[{title:"Status dot",code:'<Chip variant="success" dot>Online</Chip>'},{title:"Pulsing status (in-progress)",code:'<Chip variant="warning" dot pulse>Deploying</Chip>'},{title:"Ghost status (inline/table)",code:'<Chip variant="success" ghost dot>Active</Chip>'},{title:"Ghost pulsing",code:'<Chip variant="danger" ghost dot pulse>Live incident</Chip>'},{title:"Dot only (minimal)",code:'<Chip variant="success" dot />'},{title:"Dot only pulsing",code:'<Chip variant="danger" dot pulse />'},{title:"Dismissible filter",code:"<Chip onDismiss={() => removeFilter('react')}>React</Chip>"},{title:"Color swatch",code:'<Chip swatch="#6366f1" onDismiss={() => {}}>Indigo</Chip>'},{title:"With icon",code:"<Chip leftIcon={<FolderIcon />} onDismiss={() => {}}>Documents</Chip>"},{title:"Clickable category",code:`<Chip variant="accent" onClick={() => navigate('/ux')}>UX</Chip>`},{title:"Borderless",code:'<Chip variant="warning" borderless>Pending</Chip>'},{title:"Static label",code:'<Chip variant="info">Beta</Chip>'}],compositionGraph:[],accessibility:{role:"group",notes:'When onClick is provided, renders as <button> with native button semantics. Dismiss button has aria-label="Dismiss" and stopPropagation to prevent parent click handlers.',keyboardInteractions:["Enter / Space — activates onClick or dismiss button when focused"]}},Kn={xs:24,sm:32,md:40,lg:56,xl:80},Xn={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 Jn(e,n){var o,a,i;if(n)return n.slice(0,2).toUpperCase();const r=e.trim().split(/\s+/);return r.length===1?(((o=r[0])==null?void 0:o[0])??"").toUpperCase():((((a=r[0])==null?void 0:a[0])??"")+(((i=r[r.length-1])==null?void 0:i[0])??"")).toUpperCase()}function Zn({src:e,alt:n,size:r="md",initials:o,style:a,...i}){const l=Kn[r],s=Jn(n,o),c={width:l,height:l,borderRadius:"var(--lucent-radius-full)",flexShrink:0,display:"inline-flex",alignItems:"center",justifyContent:"center",overflow:"hidden",boxSizing:"border-box",userSelect:"none",...a};return e?t.jsx("img",{src:e,alt:n,width:l,height:l,style:{...c,objectFit:"cover"},...i}):t.jsx("span",{role:"img","aria-label":n,style:{...c,background:"var(--lucent-accent-default)",color:"var(--lucent-text-on-accent)",fontSize:Xn[r],fontWeight:"var(--lucent-font-weight-semibold)",fontFamily:"var(--lucent-font-family-base)"},children:s})}const Qn={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>.'}},ea={xs:12,sm:16,md:24,lg:36},ta={xs:2.5,sm:2.5,md:2,lg:2};function Kt({size:e="md",label:n="Loading…",color:r}){const o=ea[e],a=ta[e];return t.jsxs("span",{role:"status","aria-label":n,style:{display:"inline-flex",alignItems:"center",justifyContent:"center"},children:[t.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:r??"currentColor"},children:[t.jsx("style",{children:"@keyframes lucent-spin { to { transform: rotate(360deg); } }"}),t.jsx("circle",{cx:12,cy:12,r:10,stroke:"currentColor",strokeWidth:a,strokeOpacity:.2}),t.jsx("path",{d:"M12 2a10 10 0 0 1 10 10",stroke:"currentColor",strokeWidth:a,strokeLinecap:"round"})]}),t.jsx("span",{style:{position:"absolute",width:1,height:1,overflow:"hidden",clip:"rect(0,0,0,0)",whiteSpace:"nowrap"},children:n})]})}const na={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 aa({orientation:e="horizontal",label:n,spacing:r="var(--lucent-space-4)",style:o}){return e==="vertical"?t.jsx("span",{role:"separator","aria-orientation":"vertical",style:{display:"inline-block",width:"1px",alignSelf:"stretch",background:"var(--lucent-border-default)",margin:`0 ${r}`,flexShrink:0,...o}}):n?t.jsxs("div",{role:"separator","aria-label":n,style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-3)",margin:`${r} 0`,...o},children:[t.jsx("span",{style:{flex:1,height:"1px",background:"var(--lucent-border-default)"}}),t.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}),t.jsx("span",{style:{flex:1,height:"1px",background:"var(--lucent-border-default)"}})]}):t.jsx("hr",{role:"separator",style:{border:"none",borderTop:"1px solid var(--lucent-border-default)",margin:`${r} 0`,width:"100%",...o}})}const ra={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"]}},oa={sm:14,md:16,lg:20},ia={sm:"calc(var(--lucent-space-8) * 0.5 + 16px)",md:"calc(var(--lucent-space-10) * 0.5 + 20px)",lg:"calc(var(--lucent-space-12) * 0.5 + 24px)"},sa=`
|
|
5
|
+
}`;function Yt({children:e,variant:n="neutral",size:r="md",onDismiss:o,onClick:a,leftIcon:i,swatch:l,dot:s=!1,pulse:c=!1,borderless:u=!1,ghost:p=!1,disabled:f=!1,style:x}){const m=Gn[n],w=Un[r],[g,h]=d.useState(!1),v=s&&c,b=s&&!e,y=!f&&(o||a),C=`color-mix(in srgb, ${m.color} 8%, transparent)`,T=w.dotSize*3,S={display:"inline-flex",alignItems:"center",justifyContent:b?"center":void 0,gap:b?void 0:w.gap,height:b?T:w.height,width:b?T:void 0,padding:b?0:o?w.paddingDismiss:w.padding,fontSize:w.fontSize,fontFamily:"var(--lucent-font-family-base)",fontWeight:"var(--lucent-font-weight-medium)",lineHeight:1,borderRadius:b?"var(--lucent-radius-full)":"var(--lucent-radius-lg)",background:p?g&&y?C:"transparent":g&&y?m.hoverBg:m.bg,color:m.color,border:p||u?"1px solid transparent":`1px solid ${g&&y?m.hoverBorder:m.border}`,whiteSpace:"nowrap",boxSizing:"border-box",opacity:f?.5:1,transform:g&&y?"translateY(-1px)":"none",boxShadow:g&&y&&!p?`0 2px 4px ${m.hoverBorder}22`:"none",transition:["transform var(--lucent-duration-fast) var(--lucent-easing-default)","box-shadow var(--lucent-duration-fast) var(--lucent-easing-default)","border-color var(--lucent-duration-fast) var(--lucent-easing-default)","background var(--lucent-duration-fast) var(--lucent-easing-default)"].join(", "),cursor:y?"pointer":"default",...a?{outline:"none"}:{},...x},B=t.jsxs(t.Fragment,{children:[v&&t.jsx("style",{children:_n}),l&&t.jsx("span",{style:{width:w.dotSize+2,height:w.dotSize+2,borderRadius:"50%",background:l,border:"1px solid rgba(0,0,0,0.1)",flexShrink:0}}),s&&!l&&t.jsxs("span",{style:{position:"relative",width:w.dotSize,height:w.dotSize,flexShrink:0},children:[t.jsx("span",{style:{position:"absolute",inset:0,borderRadius:"50%",background:"currentColor"}}),v&&t.jsx("span",{style:{position:"absolute",inset:0,borderRadius:"50%",background:"currentColor",animation:"lucent-chip-pulse 1.5s ease-out infinite"}})]}),i&&!l&&!s&&t.jsx("span",{style:{display:"inline-flex",alignItems:"center",justifyContent:"center",width:w.iconSize,height:w.iconSize,flexShrink:0},children:i}),e,o&&t.jsx("button",{type:"button",onClick:f?void 0:z=>{z.stopPropagation(),o()},disabled:f,"aria-label":"Dismiss",style:{display:"inline-flex",alignItems:"center",justifyContent:"center",width:w.iconSize+2,height:w.iconSize+2,padding:0,border:"none",borderRadius:"var(--lucent-radius-lg)",background:"transparent",color:"inherit",cursor:f?"not-allowed":"pointer",flexShrink:0,lineHeight:1},children:t.jsx("svg",{width:w.iconSize-2,height:w.iconSize-2,viewBox:"0 0 10 10",fill:"none",stroke:"currentColor",strokeWidth:1.5,strokeLinecap:"round",children:t.jsx("path",{d:"M2 2L8 8M8 2L2 8"})})})]}),I={onMouseEnter:()=>{f||h(!0)},onMouseLeave:()=>h(!1)};return a?t.jsx("button",{type:"button",onClick:f?void 0:a,disabled:f,style:S,...I,children:B}):t.jsx("span",{style:S,...I,children:B})}const Yn={id:"chip",name:"Chip",tier:"atom",domain:"neutral",specVersion:"1.0",description:"A compact label for filters, tags, statuses, and categories. Combines the roles of Badge and Tag into a single flexible component.",designIntent:"Chip is the universal label primitive — use it anywhere you need a compact visual marker. It replaces both Badge (static status) and Tag (dismissible filter) with a single component. Use `onDismiss` for removable chips (filters, multi-select values). Use `onClick` for clickable/selectable chips (filter toggles, category navigation). Use `dot` for status indicators (online/offline). Use `dot` + `pulse` to show in-progress or live states (deploying, syncing, live incident). Use `ghost` + `dot` for subtle inline status that blends into surrounding text or table rows. Omit `children` with `dot` for a minimal dot-only indicator — great for table cells or avatar badges. Use `swatch` for color-coded categories. Use `leftIcon` for chips with leading icons (folders, file types, flags). Use `borderless` for a softer, filled-only appearance in dense UIs. Variant conveys semantic meaning — default to neutral for user-generated content.",props:[{name:"children",type:"ReactNode",required:!1,description:"Chip label content. When omitted with dot=true, renders as a compact dot-only indicator."},{name:"variant",type:"enum",required:!1,default:"neutral",description:"Colour scheme conveying semantic meaning.",enumValues:["neutral","accent","success","warning","danger","info"]},{name:"size",type:"enum",required:!1,default:"md",description:"Controls height, font size, and icon size.",enumValues:["sm","md","lg"]},{name:"onDismiss",type:"function",required:!1,description:"Renders an x button that calls this handler. Use for removable filters and multi-select values."},{name:"onClick",type:"function",required:!1,description:"Makes the chip clickable (renders as <button>). Use for filter toggles and category links."},{name:"leftIcon",type:"ReactNode",required:!1,description:"Icon or element rendered before the label (emoji, flag, avatar)."},{name:"swatch",type:"string",required:!1,description:"Hex color string. Renders a small color dot before the label."},{name:"dot",type:"boolean",required:!1,default:"false",description:"Renders a status dot using the variant colour. Omit children for a compact dot-only indicator."},{name:"pulse",type:"boolean",required:!1,default:"false",description:"Adds a pulsing ring animation to the status dot. Only applies when dot=true. Use for running, deploying, or live states."},{name:"borderless",type:"boolean",required:!1,default:"false",description:"Removes the border for a filled-only look."},{name:"ghost",type:"boolean",required:!1,default:"false",description:"Transparent background with text color only. Pairs well with dot for subtle inline statuses in tables or lists."},{name:"disabled",type:"boolean",required:!1,default:"false",description:"Dims the chip and prevents interaction."},{name:"style",type:"object",required:!1,description:"Inline style overrides."}],usageExamples:[{title:"Status dot",code:'<Chip variant="success" dot>Online</Chip>'},{title:"Pulsing status (in-progress)",code:'<Chip variant="warning" dot pulse>Deploying</Chip>'},{title:"Ghost status (inline/table)",code:'<Chip variant="success" ghost dot>Active</Chip>'},{title:"Ghost pulsing",code:'<Chip variant="danger" ghost dot pulse>Live incident</Chip>'},{title:"Dot only (minimal)",code:'<Chip variant="success" dot />'},{title:"Dot only pulsing",code:'<Chip variant="danger" dot pulse />'},{title:"Dismissible filter",code:"<Chip onDismiss={() => removeFilter('react')}>React</Chip>"},{title:"Color swatch",code:'<Chip swatch="#6366f1" onDismiss={() => {}}>Indigo</Chip>'},{title:"With icon",code:"<Chip leftIcon={<FolderIcon />} onDismiss={() => {}}>Documents</Chip>"},{title:"Clickable category",code:`<Chip variant="accent" onClick={() => navigate('/ux')}>UX</Chip>`},{title:"Borderless",code:'<Chip variant="warning" borderless>Pending</Chip>'},{title:"Static label",code:'<Chip variant="info">Beta</Chip>'}],compositionGraph:[],accessibility:{role:"group",notes:'When onClick is provided, renders as <button> with native button semantics. Dismiss button has aria-label="Dismiss" and stopPropagation to prevent parent click handlers.',keyboardInteractions:["Enter / Space — activates onClick or dismiss button when focused"]}},Kn={xs:24,sm:32,md:40,lg:56,xl:80},Xn={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 Jn(e,n){var o,a,i;if(n)return n.slice(0,2).toUpperCase();const r=e.trim().split(/\s+/);return r.length===1?(((o=r[0])==null?void 0:o[0])??"").toUpperCase():((((a=r[0])==null?void 0:a[0])??"")+(((i=r[r.length-1])==null?void 0:i[0])??"")).toUpperCase()}function Zn({src:e,alt:n,size:r="md",initials:o,style:a,...i}){const l=Kn[r],s=Jn(n,o),c={width:l,height:l,borderRadius:"var(--lucent-radius-full)",flexShrink:0,display:"inline-flex",alignItems:"center",justifyContent:"center",overflow:"hidden",boxSizing:"border-box",userSelect:"none",...a};return e?t.jsx("img",{src:e,alt:n,width:l,height:l,style:{...c,objectFit:"cover"},...i}):t.jsx("span",{role:"img","aria-label":n,style:{...c,background:"var(--lucent-accent-default)",color:"var(--lucent-text-on-accent)",fontSize:Xn[r],fontWeight:"var(--lucent-font-weight-semibold)",fontFamily:"var(--lucent-font-family-base)"},children:s})}const Qn={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>.'}},ea={xs:12,sm:16,md:24,lg:36},ta={xs:2.5,sm:2.5,md:2,lg:2};function Kt({size:e="md",label:n="Loading…",color:r}){const o=ea[e],a=ta[e];return t.jsxs("span",{role:"status","aria-label":n,style:{display:"inline-flex",alignItems:"center",justifyContent:"center"},children:[t.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:r??"currentColor"},children:[t.jsx("style",{children:"@keyframes lucent-spin { to { transform: rotate(360deg); } }"}),t.jsx("circle",{cx:12,cy:12,r:10,stroke:"currentColor",strokeWidth:a,strokeOpacity:.2}),t.jsx("path",{d:"M12 2a10 10 0 0 1 10 10",stroke:"currentColor",strokeWidth:a,strokeLinecap:"round"})]}),t.jsx("span",{style:{position:"absolute",width:1,height:1,overflow:"hidden",clip:"rect(0,0,0,0)",whiteSpace:"nowrap"},children:n})]})}const na={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 aa({orientation:e="horizontal",label:n,spacing:r="0",style:o}){return e==="vertical"?t.jsx("span",{role:"separator","aria-orientation":"vertical",style:{display:"inline-block",width:"1px",alignSelf:"stretch",background:"var(--lucent-border-default)",margin:`0 ${r}`,flexShrink:0,...o}}):n?t.jsxs("div",{role:"separator","aria-label":n,style:{display:"flex",alignItems:"center",gap:"var(--lucent-space-3)",margin:`${r} 0`,...o},children:[t.jsx("span",{style:{flex:1,height:"1px",background:"var(--lucent-border-default)"}}),t.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}),t.jsx("span",{style:{flex:1,height:"1px",background:"var(--lucent-border-default)"}})]}):t.jsx("hr",{role:"separator",style:{border:"none",borderTop:"1px solid var(--lucent-border-default)",margin:`${r} 0`,width:"100%",...o}})}const ra={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:"0",description:'Margin on the axis perpendicular to the line. Defaults to 0 so parent gap-based layouts (Stack, Row) control spacing. Pass an explicit value like "var(--lucent-space-4)" for standalone use outside flex/grid containers.'}],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"]}},oa={sm:14,md:16,lg:20},ia={sm:"calc(var(--lucent-space-8) * 0.5 + 16px)",md:"calc(var(--lucent-space-10) * 0.5 + 20px)",lg:"calc(var(--lucent-space-12) * 0.5 + 24px)"},sa=`
|
|
6
6
|
@keyframes lucent-cb-pop {
|
|
7
7
|
0% { transform: scale(1); }
|
|
8
8
|
35% { transform: scale(0.82); }
|