@@ -247,7 +247,7 @@ func list_item_styles(page : &mut HtmlPage) : *char {
247247 }
248248}
249249
250- func table_styles (page : &mut HtmlPage) : *char {
250+ public func table_styles (page : &mut HtmlPage) : *char {
251251 return #css {
252252 width: 100 %;
253253 border-collapse: separate;
@@ -259,7 +259,7 @@ func table_styles(page : &mut HtmlPage) : *char {
259259 }
260260}
261261
262- func table_head_cell_styles (page : &mut HtmlPage) : *char {
262+ public func table_head_cell_styles (page : &mut HtmlPage) : *char {
263263 return #css {
264264 padding: 0 .85rem 1rem;
265265 text-align: left;
@@ -272,7 +272,7 @@ func table_head_cell_styles(page : &mut HtmlPage) : *char {
272272 }
273273}
274274
275- func table_cell_styles (page : &mut HtmlPage) : *char {
275+ public func table_cell_styles (page : &mut HtmlPage) : *char {
276276 return #css {
277277 padding: 0 .9rem 1rem;
278278 color: hsl (var (--foreground));
@@ -303,98 +303,62 @@ public #universal Progress(props) {
303303// Shadcn-style Accordion: manages open state for child AccordionItems.
304304// `defaultValue` is an array of item values open by default.
305305// `multiple` allows multiple items open at the same time.
306+ // Accordion: styled wrapper for a group of AccordionItems.
307+ // Passes through all props. data-accordion-root enables keyboard nav via JS.
306308public #universal Accordion (props) {
307309 var multiple = props.multiple || false
308- var defaultValues = props.defaultValue || []
309- state openItems = defaultValues
310- var toggleItem = (value) => {
311- var idx = openItems.indexOf (value)
312- if (idx >= 0 ) {
313- openItems.splice (idx, 1 )
314- } else {
315- if (! multiple) { openItems = [] }
316- openItems.push (value)
317- }
318- if (props.onValueChange) { props.onValueChange (openItems) }
319- }
320- var isItemOpen = (value) => { return openItems.indexOf (value) >= 0 }
321- // Keyboard navigation: ArrowDown/Up/Home/End cycle through triggers
322- var handleKeyDown = (e) => {
323- if (e.key != "ArrowDown" && e.key != "ArrowUp" && e.key != "Home" && e.key != "End" ) { return }
324- const root = e.currentTarget.closest ("[data-accordion-root]" )
325- const triggers = root ? root.querySelectorAll ("button.chx-accordion-trigger:not([disabled])" ) : []
326- if (triggers.length == 0 ) { return }
327- var idx = -1
328- for (var t = 0 ; t < triggers.length; t++) {
329- if (triggers[t] == e.currentTarget) { idx = t; break }
330- }
331- if (idx < 0 ) { return }
332- var next = idx
333- if (e.key == "ArrowDown" ) { e.preventDefault (); next = (idx + 1 ) % triggers.length }
334- else if (e.key == "ArrowUp" ) { e.preventDefault (); next = (idx - 1 + triggers.length) % triggers.length }
335- else if (e.key == "Home" ) { e.preventDefault (); next = 0 }
336- else if (e.key == "End" ) { e.preventDefault (); next = triggers.length - 1 }
337- triggers[next].focus ()
338- }
339- return < div {...props} class={$ {accordion_styles (page)}} data-accordion-root="true" data-state="open" data-multiple={multiple ? "true" : "false" } onKeyDown={handleKeyDown}>
310+ return < div {...props} class={$ {accordion_styles (page)}} data-accordion-root="true" data-multiple={multiple ? "true" : "false" }>
340311 {props.children}
341312 </div>
342313}
343314
344- // Shadcn-style AccordionItem: wraps a trigger + content pair.
315+ // AccordionItem: self-contained item with trigger + content.
316+ // Manages its own open/close state. Accepts:
317+ // trigger - label text or JSX for the clickable header
318+ // defaultOpen - initial open state (default false)
319+ // disabled - disables toggle
320+ // value - identifier for external querying
321+ // children - collapsible content
345322public #universal AccordionItem (props) {
346- var itemValue = props.value || ""
323+ state open = props.defaultOpen ? true : false
347324 var disabled = props.disabled || false
348- return < div {...props} class={$ {accordion_item_styles (page)}} data-state="open" data-disabled={disabled ? "true" : "false" } data-value={itemValue}>
349- {props.children}
325+ var itemValue = props.value || ""
326+ var toggle = () => {
327+ if (disabled) { return }
328+ open = ! open
329+ }
330+ var chevronSvg = '< svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" >< path d="m6 9 6 6 6-6" />< /svg> '
331+ return < div {...props} class={$ {accordion_item_styles (page)}} data-state={open ? "open" : "closed" } data-value={itemValue} data-disabled={disabled ? "true" : "false" }>
332+ <button type="button" class={"chx-accordion-trigger " + ${accordion_trigger_styles(page)}} onClick={toggle} disabled={disabled} aria-expanded={open ? "true" : "false"} data-accordion-trigger="true">
333+ <span>{props.trigger || "" }< /span>
334+ < span class={$ {accordion_icon_styles (page)}} style={open ? "transform:rotate(180deg)" : "" }>
335+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
336+ </span>
337+ </button>
338+ <div class={${accordion_content_styles(page)}} style={open ? "" : "display:none;"} data-accordion-content="true" role="region">
339+ <div class={${accordion_content_inner_styles(page)}}>{props.children}</div>
340+ </div>
350341 </div>
351342}
352343
353- // Shadcn-style AccordionTrigger: the clickable header.
344+ // Standalone AccordionTrigger: styled clickable header (no state).
345+ // Use inside AccordionItem for composition, or standalone for custom layouts.
354346public #universal AccordionTrigger(props) {
355- return < button type="button" class={"chx-accordion-trigger " + $ {accordion_trigger_styles (page)}} data-state= "open" aria-expanded ="true" >
347+ return <button type="button" class={"chx-accordion-trigger " + ${accordion_trigger_styles(page)}} data-accordion-trigger ="true">
356348 {props.children}
357349 <span class={${accordion_icon_styles(page)}}>
358350 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
359351 </span>
360352 </button>
361353}
362354
363- // Shadcn-style AccordionContent: the collapsible content panel.
355+ // Standalone AccordionContent: collapsible content panel (no state) .
364356public #universal AccordionContent(props) {
365- return < div class={$ {accordion_content_styles (page)}} data-state= "open" role="region" >
357+ return <div class={${accordion_content_styles(page)}} role="region">
366358 <div class={${accordion_content_inner_styles(page)}}>{props.children}</div>
367359 </div>
368360}
369361
370- // Legacy aliases
371- public #universal AccordionSummary (props) {
372- return < AccordionTrigger {...props}> {props.children}< /AccordionTrigger>
373- }
374- public #universal AccordionPanel (props) {
375- return < AccordionContent {...props}> {props.children}< /AccordionContent>
376- }
377-
378- // Legacy: old-style accordion item with title/subtitle props.
379- public #universal AccordionItemLegacy (props) {
380- state open = props.defaultOpen ? true : false
381- var disabled = props.disabled || false
382- var toggle = () => {
383- if (disabled) { return }
384- open = ! open
385- }
386- return < div {...props} class={$ {accordion_item_styles (page)}} data-disabled={disabled ? "true" : "false" }>
387- <button type="button" class="chx-accordion-summary" onClick={toggle} disabled={disabled} aria-expanded={open ? "true" : "false"}>
388- <span class="chx-accordion-copy">
389- <span class="chx-accordion-title">{props.title}</span>
390- <span class="chx-accordion-subtitle">{props.subtitle}</span>
391- </span>
392- <span class="chx-accordion-icon">{open ? "+" : "+" }</span>
393- </button>
394- <div class="chx-accordion-panel" style={open ? "" : "display:none;"}>{props.children}</div>
395- </div>
396- }
397-
398362// Tabs in two modes:
399363// - `tabs`/`panels` arrays + optional `defaultIndex`: the component owns the
400364// active-tab state, renders the tab buttons and toggles panel visibility.
0 commit comments