@mhmo91/schmancy 0.9.15 → 0.9.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/custom-elements.json +83 -35
  2. package/dist/agent/schmancy.agent.js +297 -40
  3. package/dist/agent/schmancy.agent.js.map +1 -1
  4. package/dist/agent/schmancy.manifest.json +342 -19
  5. package/dist/autocomplete-CgWUCUU-.js.map +1 -1
  6. package/dist/autocomplete-EM0jE7X2.cjs.map +1 -1
  7. package/dist/button.cjs.map +1 -1
  8. package/dist/button.js.map +1 -1
  9. package/dist/card-BslSqOsf.cjs.map +1 -1
  10. package/dist/card-CEdgK9nb.js.map +1 -1
  11. package/dist/checkbox-Br84TiCs.js.map +1 -1
  12. package/dist/checkbox-DtcFMgZL.cjs.map +1 -1
  13. package/dist/chips-BNYOweGm.js.map +1 -1
  14. package/dist/chips-DoCu5YQb.cjs.map +1 -1
  15. package/dist/details-B8p62xmR.cjs.map +1 -1
  16. package/dist/details-CCW52lzz.js.map +1 -1
  17. package/dist/divider-CbEWg3G_.js.map +1 -1
  18. package/dist/divider-JyyFw_3J.cjs.map +1 -1
  19. package/dist/expand-BmwIPNjq.cjs.map +1 -1
  20. package/dist/expand-bFa_qVDT.js.map +1 -1
  21. package/dist/extra-BUgyMgjl.cjs.map +1 -1
  22. package/dist/extra-HwbaUnCD.js.map +1 -1
  23. package/dist/form-rCZqoAoK.js.map +1 -1
  24. package/dist/form-wI58M85H.cjs.map +1 -1
  25. package/dist/handover/agent-runtime-followups.md +1 -1
  26. package/dist/handover/agent-runtime-v1.md +3 -3
  27. package/dist/input-BGNZlfL8.cjs.map +1 -1
  28. package/dist/input-Bc3bVISm.js.map +1 -1
  29. package/dist/input-chip-CiG61y-N.js.map +1 -1
  30. package/dist/input-chip-p24lkYtY.cjs.map +1 -1
  31. package/dist/page.cjs.map +1 -1
  32. package/dist/page.js.map +1 -1
  33. package/dist/radio-group-B72sYGnS.js.map +1 -1
  34. package/dist/radio-group-B7DuNxUq.cjs.map +1 -1
  35. package/dist/scroll-CdmXRXh2.js.map +1 -1
  36. package/dist/scroll-V1rAZ9fK.cjs.map +1 -1
  37. package/dist/select-DFxoBgEf.cjs.map +1 -1
  38. package/dist/select-wFDKDLQI.js.map +1 -1
  39. package/dist/surface-0XM4DBaT.js.map +1 -1
  40. package/dist/surface-B6DA01kL.cjs.map +1 -1
  41. package/dist/switch.cjs.map +1 -1
  42. package/dist/switch.js.map +1 -1
  43. package/dist/textarea-B2544vx9.cjs.map +1 -1
  44. package/dist/textarea-CS-KdSLz.js.map +1 -1
  45. package/dist/theme-Cq_c9IO3.js.map +1 -1
  46. package/dist/theme-DU5yXaV-.cjs.map +1 -1
  47. package/dist/tree.cjs.map +1 -1
  48. package/dist/tree.js.map +1 -1
  49. package/package.json +1 -1
  50. package/src/autocomplete/autocomplete.ts +12 -1
  51. package/src/button/button.ts +6 -1
  52. package/src/button/icon-button.ts +7 -1
  53. package/src/card/actions.ts +9 -0
  54. package/src/card/card.ts +18 -0
  55. package/src/card/content.ts +9 -0
  56. package/src/card/media.ts +6 -0
  57. package/src/checkbox/checkbox.ts +7 -1
  58. package/src/chips/chips.ts +14 -0
  59. package/src/chips/input-chip.ts +6 -6
  60. package/src/details/details.ts +12 -0
  61. package/src/divider/divider.ts +11 -0
  62. package/src/expand/expand-root.component.ts +12 -0
  63. package/src/expand/expand.component.ts +14 -0
  64. package/src/extra/countries/countries.ts +10 -0
  65. package/src/extra/timezone/timezone.ts +8 -1
  66. package/src/form/form.ts +9 -2
  67. package/src/input/input.ts +11 -4
  68. package/src/layout/scroll/scroll.ts +5 -1
  69. package/src/page/page.ts +8 -11
  70. package/src/radio-group/radio-button.ts +10 -1
  71. package/src/radio-group/radio-group.ts +18 -0
  72. package/src/select/select.ts +12 -1
  73. package/src/surface/surface.ts +4 -10
  74. package/src/switch/switch.ts +7 -4
  75. package/src/textarea/textarea.ts +9 -1
  76. package/src/theme/theme.component.ts +10 -15
  77. package/src/tree/tree.ts +12 -0
  78. package/types/src/autocomplete/autocomplete.d.ts +12 -1
  79. package/types/src/button/button.d.ts +6 -1
  80. package/types/src/button/icon-button.d.ts +7 -1
  81. package/types/src/card/actions.d.ts +9 -0
  82. package/types/src/card/card.d.ts +18 -0
  83. package/types/src/card/content.d.ts +9 -0
  84. package/types/src/card/media.d.ts +6 -0
  85. package/types/src/checkbox/checkbox.d.ts +7 -1
  86. package/types/src/chips/chips.d.ts +14 -0
  87. package/types/src/chips/input-chip.d.ts +6 -6
  88. package/types/src/details/details.d.ts +12 -0
  89. package/types/src/divider/divider.d.ts +11 -0
  90. package/types/src/expand/expand-root.component.d.ts +12 -0
  91. package/types/src/expand/expand.component.d.ts +14 -0
  92. package/types/src/extra/countries/countries.d.ts +10 -0
  93. package/types/src/extra/timezone/timezone.d.ts +8 -1
  94. package/types/src/form/form.d.ts +9 -2
  95. package/types/src/input/input.d.ts +11 -4
  96. package/types/src/layout/scroll/scroll.d.ts +5 -1
  97. package/types/src/page/page.d.ts +8 -11
  98. package/types/src/radio-group/radio-button.d.ts +10 -1
  99. package/types/src/radio-group/radio-group.d.ts +18 -0
  100. package/types/src/select/select.d.ts +12 -1
  101. package/types/src/surface/surface.d.ts +4 -10
  102. package/types/src/switch/switch.d.ts +7 -4
  103. package/types/src/textarea/textarea.d.ts +9 -1
  104. package/types/src/theme/theme.component.d.ts +10 -15
  105. package/types/src/tree/tree.d.ts +12 -0
package/dist/tree.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tree.cjs","names":[],"sources":["../src/tree/tree.ts"],"sourcesContent":["import { TailwindElement } from '@mixins/index'\nimport { css, html } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\nimport { fromEvent, merge, switchMap, takeUntil, tap, zip } from 'rxjs'\n\n/**\n * @element schmancy-tree\n * @slot root - The root element of the tree\n * @slot - The children of the tree\n */\n@customElement('schmancy-tree')\nexport class SchmancyTree extends TailwindElement(css`\n\t:host {\n\t\tdisplay: block;\n\t\tposition: relative;\n\t\tbackground-color: initial;\n\t}\n\t::slotted([slot='root']) {\n\t\twidth: 100%;\n\t\ttext-align: left;\n\t}\n\t::slotted([slot='root'] + *) {\n\t\tmargin-top: 0.5rem;\n\t}\n`) {\n\t/**\n\t * Whether the tree’s children are visible\n\t */\n\t@property({ type: Boolean }) open = false\n\n\t@query('#toggler') toggler!: HTMLSlotElement\n\t@query('slot:not([name=\"root\"])') defaultSlot!: HTMLSlotElement\n\n\t// Since it's actually a <schmancy-button>, use HTMLElement or a custom type\n\t@query('#chevron') chevron!: HTMLElement\n\n\tprivate readonly _a11yId = `schmancy-tree-${Math.random().toString(36).slice(2, 10)}`\n\tprivate get _contentId() { return `${this._a11yId}-content` }\n\n\t/** ElementInternals — broadcasts `:state(open)` for consumer CSS. */\n\tprivate readonly _internals: ElementInternals | undefined = (() => {\n\t\ttry { return this.attachInternals() } catch { return undefined }\n\t})()\n\n\tupdated(changed: Map<string, unknown>) {\n\t\tsuper.updated?.(changed)\n\t\tif (changed.has('open')) {\n\t\t\tif (this.open) this._internals?.states.add('open')\n\t\t\telse this._internals?.states.delete('open')\n\t\t}\n\t}\n\n\tfirstUpdated() {\n\t\t// Hide or show the slot initially based on `open`\n\t\tif (!this.open) {\n\t\t\tthis.defaultSlot.hidden = true\n\t\t}\n\n\t\t// Root toggler\n\t\tconst toggleClick$ = fromEvent<MouseEvent>(this.toggler, 'click').pipe(\n\t\t\ttakeUntil(this.disconnecting),\n\t\t\ttap(e => {\n\t\t\t\te.preventDefault()\n\t\t\t\te.stopPropagation()\n\t\t\t\tthis.dispatchEvent(new CustomEvent('toggle', { bubbles: false, composed: true }))\n\t\t\t}),\n\t\t)\n\n\t\t// Chevron (the schmancy-button) click\n\t\tconst chevronClick$ = fromEvent<MouseEvent>(this.chevron, 'click')\n\n\t\tmerge(toggleClick$, chevronClick$)\n\t\t\t.pipe(\n\t\t\t\tswitchMap(() => {\n\t\t\t\t\t// 1. Animate the chevron rotation\n\t\t\t\t\t// If `open` is true, rotate from 180 -> 0; if false, from 0 -> 180\n\t\t\t\t\tconst fromDeg = this.open ? 180 : 0\n\t\t\t\t\tconst toDeg = this.open ? 0 : 180\n\t\t\t\t\tconst chevronAnimation = this.chevron.animate(\n\t\t\t\t\t\t[{ transform: `rotate(${fromDeg}deg)` }, { transform: `rotate(${toDeg}deg)` }],\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tduration: 150,\n\t\t\t\t\t\t\teasing: 'ease-in',\n\t\t\t\t\t\t\tfill: 'forwards',\n\t\t\t\t\t\t},\n\t\t\t\t\t)\n\n\t\t\t\t\t// 2. Animate the slot’s height + opacity\n\t\t\t\t\tif (!this.open) {\n\t\t\t\t\t\t// We are about to open, so remove `hidden` to measure scrollHeight\n\t\t\t\t\t\tthis.defaultSlot.hidden = false\n\t\t\t\t\t}\n\n\t\t\t\t\tconst fromOpacity = this.open ? 1 : 0\n\t\t\t\t\tconst toOpacity = this.open ? 0 : 1\n\n\t\t\t\t\tconst slotAnimation = this.defaultSlot.animate([{ opacity: fromOpacity }, { opacity: toOpacity }], {\n\t\t\t\t\t\tduration: 150,\n\t\t\t\t\t\teasing: 'ease-out',\n\t\t\t\t\t\tfill: 'forwards',\n\t\t\t\t\t})\n\n\t\t\t\t\t// Hide the slot if we just closed it\n\t\t\t\t\tslotAnimation.onfinish = () => {\n\t\t\t\t\t\tif (this.open) {\n\t\t\t\t\t\t\tthis.defaultSlot.hidden = true\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.defaultSlot.style.height = 'auto'\n\t\t\t\t\t\t\tthis.defaultSlot.style.opacity = '1'\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Return an Observable that completes when both animations finish\n\t\t\t\t\treturn zip(fromEvent(chevronAnimation, 'finish'), fromEvent(slotAnimation, 'finish')).pipe(\n\t\t\t\t\t\ttakeUntil(this.disconnecting),\n\t\t\t\t\t)\n\t\t\t\t}),\n\t\t\t\ttap(() => {\n\t\t\t\t\t// Flip the open state\n\t\t\t\t\tthis.open = !this.open\n\t\t\t\t}),\n\t\t\t\ttakeUntil(this.disconnecting),\n\t\t\t)\n\t\t\t.subscribe()\n\t}\n\n\trender() {\n\t\treturn html`\n\t\t\t<div class=\"flex content-center items-center justify-between\">\n\t\t\t\t<!-- Root toggler content -->\n\t\t\t\t<slot id=\"toggler\" name=\"root\"></slot>\n\n\t\t\t\t<!-- The chevron or arrow symbol -->\n\t\t\t\t<!-- Stop propagation on the schmancy-button itself just to avoid double triggers -->\n\t\t\t\t<schmancy-button\n\t\t\t\t\tslot=\"trailing\"\n\t\t\t\t\tid=\"chevron\"\n\t\t\t\t\taria-expanded=${this.open ? 'true' : 'false'}\n\t\t\t\t\taria-controls=${this._contentId}\n\t\t\t\t\taria-label=${this.open ? 'Collapse' : 'Expand'}\n\t\t\t\t\t@click=${(e: Event) => e.stopPropagation()}\n\t\t\t\t>\n\t\t\t\t\t⌅\n\t\t\t\t</schmancy-button>\n\t\t\t</div>\n\n\t\t\t<!-- The default slot: tree children -->\n\t\t\t<slot id=${this._contentId}></slot>\n\t\t`\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'schmancy-tree': SchmancyTree\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tree.cjs","names":[],"sources":["../src/tree/tree.ts"],"sourcesContent":["import { TailwindElement } from '@mixins/index'\nimport { css, html } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\nimport { fromEvent, merge, switchMap, takeUntil, tap, zip } from 'rxjs'\n\n/**\n * Expandable tree node — a recursive disclosure widget. One root slot, one default slot for child nodes. Each node can itself contain schmancy-tree children.\n *\n * @element schmancy-tree\n * @summary Use for hierarchical navigation / file-explorer layouts. Each level is a schmancy-tree with a `root` slot (the parent label) and default slot (the children, which may be more schmancy-trees).\n * @example\n * <schmancy-tree>\n * <schmancy-list-item slot=\"root\">src/</schmancy-list-item>\n * <schmancy-tree>\n * <schmancy-list-item slot=\"root\">components/</schmancy-list-item>\n * <schmancy-list-item>button.ts</schmancy-list-item>\n * </schmancy-tree>\n * </schmancy-tree>\n * @platform details toggle - Recursive `<details>`-like disclosure. Degrades to a plain nested list if the tag never registers — loses expand/collapse but stays navigable.\n * @slot root - The root element of the tree\n * @slot - The children of the tree\n */\n@customElement('schmancy-tree')\nexport class SchmancyTree extends TailwindElement(css`\n\t:host {\n\t\tdisplay: block;\n\t\tposition: relative;\n\t\tbackground-color: initial;\n\t}\n\t::slotted([slot='root']) {\n\t\twidth: 100%;\n\t\ttext-align: left;\n\t}\n\t::slotted([slot='root'] + *) {\n\t\tmargin-top: 0.5rem;\n\t}\n`) {\n\t/**\n\t * Whether the tree’s children are visible\n\t */\n\t@property({ type: Boolean }) open = false\n\n\t@query('#toggler') toggler!: HTMLSlotElement\n\t@query('slot:not([name=\"root\"])') defaultSlot!: HTMLSlotElement\n\n\t// Since it's actually a <schmancy-button>, use HTMLElement or a custom type\n\t@query('#chevron') chevron!: HTMLElement\n\n\tprivate readonly _a11yId = `schmancy-tree-${Math.random().toString(36).slice(2, 10)}`\n\tprivate get _contentId() { return `${this._a11yId}-content` }\n\n\t/** ElementInternals — broadcasts `:state(open)` for consumer CSS. */\n\tprivate readonly _internals: ElementInternals | undefined = (() => {\n\t\ttry { return this.attachInternals() } catch { return undefined }\n\t})()\n\n\tupdated(changed: Map<string, unknown>) {\n\t\tsuper.updated?.(changed)\n\t\tif (changed.has('open')) {\n\t\t\tif (this.open) this._internals?.states.add('open')\n\t\t\telse this._internals?.states.delete('open')\n\t\t}\n\t}\n\n\tfirstUpdated() {\n\t\t// Hide or show the slot initially based on `open`\n\t\tif (!this.open) {\n\t\t\tthis.defaultSlot.hidden = true\n\t\t}\n\n\t\t// Root toggler\n\t\tconst toggleClick$ = fromEvent<MouseEvent>(this.toggler, 'click').pipe(\n\t\t\ttakeUntil(this.disconnecting),\n\t\t\ttap(e => {\n\t\t\t\te.preventDefault()\n\t\t\t\te.stopPropagation()\n\t\t\t\tthis.dispatchEvent(new CustomEvent('toggle', { bubbles: false, composed: true }))\n\t\t\t}),\n\t\t)\n\n\t\t// Chevron (the schmancy-button) click\n\t\tconst chevronClick$ = fromEvent<MouseEvent>(this.chevron, 'click')\n\n\t\tmerge(toggleClick$, chevronClick$)\n\t\t\t.pipe(\n\t\t\t\tswitchMap(() => {\n\t\t\t\t\t// 1. Animate the chevron rotation\n\t\t\t\t\t// If `open` is true, rotate from 180 -> 0; if false, from 0 -> 180\n\t\t\t\t\tconst fromDeg = this.open ? 180 : 0\n\t\t\t\t\tconst toDeg = this.open ? 0 : 180\n\t\t\t\t\tconst chevronAnimation = this.chevron.animate(\n\t\t\t\t\t\t[{ transform: `rotate(${fromDeg}deg)` }, { transform: `rotate(${toDeg}deg)` }],\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tduration: 150,\n\t\t\t\t\t\t\teasing: 'ease-in',\n\t\t\t\t\t\t\tfill: 'forwards',\n\t\t\t\t\t\t},\n\t\t\t\t\t)\n\n\t\t\t\t\t// 2. Animate the slot’s height + opacity\n\t\t\t\t\tif (!this.open) {\n\t\t\t\t\t\t// We are about to open, so remove `hidden` to measure scrollHeight\n\t\t\t\t\t\tthis.defaultSlot.hidden = false\n\t\t\t\t\t}\n\n\t\t\t\t\tconst fromOpacity = this.open ? 1 : 0\n\t\t\t\t\tconst toOpacity = this.open ? 0 : 1\n\n\t\t\t\t\tconst slotAnimation = this.defaultSlot.animate([{ opacity: fromOpacity }, { opacity: toOpacity }], {\n\t\t\t\t\t\tduration: 150,\n\t\t\t\t\t\teasing: 'ease-out',\n\t\t\t\t\t\tfill: 'forwards',\n\t\t\t\t\t})\n\n\t\t\t\t\t// Hide the slot if we just closed it\n\t\t\t\t\tslotAnimation.onfinish = () => {\n\t\t\t\t\t\tif (this.open) {\n\t\t\t\t\t\t\tthis.defaultSlot.hidden = true\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.defaultSlot.style.height = 'auto'\n\t\t\t\t\t\t\tthis.defaultSlot.style.opacity = '1'\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Return an Observable that completes when both animations finish\n\t\t\t\t\treturn zip(fromEvent(chevronAnimation, 'finish'), fromEvent(slotAnimation, 'finish')).pipe(\n\t\t\t\t\t\ttakeUntil(this.disconnecting),\n\t\t\t\t\t)\n\t\t\t\t}),\n\t\t\t\ttap(() => {\n\t\t\t\t\t// Flip the open state\n\t\t\t\t\tthis.open = !this.open\n\t\t\t\t}),\n\t\t\t\ttakeUntil(this.disconnecting),\n\t\t\t)\n\t\t\t.subscribe()\n\t}\n\n\trender() {\n\t\treturn html`\n\t\t\t<div class=\"flex content-center items-center justify-between\">\n\t\t\t\t<!-- Root toggler content -->\n\t\t\t\t<slot id=\"toggler\" name=\"root\"></slot>\n\n\t\t\t\t<!-- The chevron or arrow symbol -->\n\t\t\t\t<!-- Stop propagation on the schmancy-button itself just to avoid double triggers -->\n\t\t\t\t<schmancy-button\n\t\t\t\t\tslot=\"trailing\"\n\t\t\t\t\tid=\"chevron\"\n\t\t\t\t\taria-expanded=${this.open ? 'true' : 'false'}\n\t\t\t\t\taria-controls=${this._contentId}\n\t\t\t\t\taria-label=${this.open ? 'Collapse' : 'Expand'}\n\t\t\t\t\t@click=${(e: Event) => e.stopPropagation()}\n\t\t\t\t>\n\t\t\t\t\t⌅\n\t\t\t\t</schmancy-button>\n\t\t\t</div>\n\n\t\t\t<!-- The default slot: tree children -->\n\t\t\t<slot id=${this._contentId}></slot>\n\t\t`\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'schmancy-tree': SchmancyTree\n\t}\n}\n"],"mappings":"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"}
package/dist/tree.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tree.js","names":[],"sources":["../src/tree/tree.ts"],"sourcesContent":["import { TailwindElement } from '@mixins/index'\nimport { css, html } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\nimport { fromEvent, merge, switchMap, takeUntil, tap, zip } from 'rxjs'\n\n/**\n * @element schmancy-tree\n * @slot root - The root element of the tree\n * @slot - The children of the tree\n */\n@customElement('schmancy-tree')\nexport class SchmancyTree extends TailwindElement(css`\n\t:host {\n\t\tdisplay: block;\n\t\tposition: relative;\n\t\tbackground-color: initial;\n\t}\n\t::slotted([slot='root']) {\n\t\twidth: 100%;\n\t\ttext-align: left;\n\t}\n\t::slotted([slot='root'] + *) {\n\t\tmargin-top: 0.5rem;\n\t}\n`) {\n\t/**\n\t * Whether the tree’s children are visible\n\t */\n\t@property({ type: Boolean }) open = false\n\n\t@query('#toggler') toggler!: HTMLSlotElement\n\t@query('slot:not([name=\"root\"])') defaultSlot!: HTMLSlotElement\n\n\t// Since it's actually a <schmancy-button>, use HTMLElement or a custom type\n\t@query('#chevron') chevron!: HTMLElement\n\n\tprivate readonly _a11yId = `schmancy-tree-${Math.random().toString(36).slice(2, 10)}`\n\tprivate get _contentId() { return `${this._a11yId}-content` }\n\n\t/** ElementInternals — broadcasts `:state(open)` for consumer CSS. */\n\tprivate readonly _internals: ElementInternals | undefined = (() => {\n\t\ttry { return this.attachInternals() } catch { return undefined }\n\t})()\n\n\tupdated(changed: Map<string, unknown>) {\n\t\tsuper.updated?.(changed)\n\t\tif (changed.has('open')) {\n\t\t\tif (this.open) this._internals?.states.add('open')\n\t\t\telse this._internals?.states.delete('open')\n\t\t}\n\t}\n\n\tfirstUpdated() {\n\t\t// Hide or show the slot initially based on `open`\n\t\tif (!this.open) {\n\t\t\tthis.defaultSlot.hidden = true\n\t\t}\n\n\t\t// Root toggler\n\t\tconst toggleClick$ = fromEvent<MouseEvent>(this.toggler, 'click').pipe(\n\t\t\ttakeUntil(this.disconnecting),\n\t\t\ttap(e => {\n\t\t\t\te.preventDefault()\n\t\t\t\te.stopPropagation()\n\t\t\t\tthis.dispatchEvent(new CustomEvent('toggle', { bubbles: false, composed: true }))\n\t\t\t}),\n\t\t)\n\n\t\t// Chevron (the schmancy-button) click\n\t\tconst chevronClick$ = fromEvent<MouseEvent>(this.chevron, 'click')\n\n\t\tmerge(toggleClick$, chevronClick$)\n\t\t\t.pipe(\n\t\t\t\tswitchMap(() => {\n\t\t\t\t\t// 1. Animate the chevron rotation\n\t\t\t\t\t// If `open` is true, rotate from 180 -> 0; if false, from 0 -> 180\n\t\t\t\t\tconst fromDeg = this.open ? 180 : 0\n\t\t\t\t\tconst toDeg = this.open ? 0 : 180\n\t\t\t\t\tconst chevronAnimation = this.chevron.animate(\n\t\t\t\t\t\t[{ transform: `rotate(${fromDeg}deg)` }, { transform: `rotate(${toDeg}deg)` }],\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tduration: 150,\n\t\t\t\t\t\t\teasing: 'ease-in',\n\t\t\t\t\t\t\tfill: 'forwards',\n\t\t\t\t\t\t},\n\t\t\t\t\t)\n\n\t\t\t\t\t// 2. Animate the slot’s height + opacity\n\t\t\t\t\tif (!this.open) {\n\t\t\t\t\t\t// We are about to open, so remove `hidden` to measure scrollHeight\n\t\t\t\t\t\tthis.defaultSlot.hidden = false\n\t\t\t\t\t}\n\n\t\t\t\t\tconst fromOpacity = this.open ? 1 : 0\n\t\t\t\t\tconst toOpacity = this.open ? 0 : 1\n\n\t\t\t\t\tconst slotAnimation = this.defaultSlot.animate([{ opacity: fromOpacity }, { opacity: toOpacity }], {\n\t\t\t\t\t\tduration: 150,\n\t\t\t\t\t\teasing: 'ease-out',\n\t\t\t\t\t\tfill: 'forwards',\n\t\t\t\t\t})\n\n\t\t\t\t\t// Hide the slot if we just closed it\n\t\t\t\t\tslotAnimation.onfinish = () => {\n\t\t\t\t\t\tif (this.open) {\n\t\t\t\t\t\t\tthis.defaultSlot.hidden = true\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.defaultSlot.style.height = 'auto'\n\t\t\t\t\t\t\tthis.defaultSlot.style.opacity = '1'\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Return an Observable that completes when both animations finish\n\t\t\t\t\treturn zip(fromEvent(chevronAnimation, 'finish'), fromEvent(slotAnimation, 'finish')).pipe(\n\t\t\t\t\t\ttakeUntil(this.disconnecting),\n\t\t\t\t\t)\n\t\t\t\t}),\n\t\t\t\ttap(() => {\n\t\t\t\t\t// Flip the open state\n\t\t\t\t\tthis.open = !this.open\n\t\t\t\t}),\n\t\t\t\ttakeUntil(this.disconnecting),\n\t\t\t)\n\t\t\t.subscribe()\n\t}\n\n\trender() {\n\t\treturn html`\n\t\t\t<div class=\"flex content-center items-center justify-between\">\n\t\t\t\t<!-- Root toggler content -->\n\t\t\t\t<slot id=\"toggler\" name=\"root\"></slot>\n\n\t\t\t\t<!-- The chevron or arrow symbol -->\n\t\t\t\t<!-- Stop propagation on the schmancy-button itself just to avoid double triggers -->\n\t\t\t\t<schmancy-button\n\t\t\t\t\tslot=\"trailing\"\n\t\t\t\t\tid=\"chevron\"\n\t\t\t\t\taria-expanded=${this.open ? 'true' : 'false'}\n\t\t\t\t\taria-controls=${this._contentId}\n\t\t\t\t\taria-label=${this.open ? 'Collapse' : 'Expand'}\n\t\t\t\t\t@click=${(e: Event) => e.stopPropagation()}\n\t\t\t\t>\n\t\t\t\t\t⌅\n\t\t\t\t</schmancy-button>\n\t\t\t</div>\n\n\t\t\t<!-- The default slot: tree children -->\n\t\t\t<slot id=${this._contentId}></slot>\n\t\t`\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'schmancy-tree': SchmancyTree\n\t}\n}\n"],"mappings":";;;;;;AAWO,IAAA,IAAA,cAA2B,EAAgB,CAAG;;;;;;;;;;;;;;;4BAiBhB,GAAA,KAAA,UAQT,iBAAiB,KAAK,QAAA,CAAS,SAAS,GAAA,CAAI,MAAM,GAAG,GAAA,IAAA,KAAA,oBAAA;AAK/E,OAAA;AAAM,WAAO,KAAK,iBAAA;WAAA;AAA4B;;MAAA;;CAJ/C,IAAA,aAAY;AAAe,SAAO,GAAG,KAAK,QAAA;;CAO1C,QAAQ,GAAA;AACP,QAAM,UAAU,EAAA,EACZ,EAAQ,IAAI,OAAA,KACX,KAAK,OAAM,KAAK,YAAY,OAAO,IAAI,OAAA,GACtC,KAAK,YAAY,OAAO,OAAO,OAAA;;CAItC,eAAA;AAEM,OAAK,SACT,KAAK,YAAY,SAAA,CAAS,IAgB3B,EAZqB,EAAsB,KAAK,SAAS,QAAA,CAAS,KACjE,EAAU,KAAK,cAAA,EACf,GAAI,MAAA;AACH,KAAE,gBAAA,EACF,EAAE,iBAAA,EACF,KAAK,cAAc,IAAI,YAAY,UAAU;IAAE,SAAA,CAAS;IAAO,UAAA,CAAU;IAAA,CAAA,CAAA;IAAA,CAAA,EAKrD,EAAsB,KAAK,SAAS,QAAA,CAAA,CAGxD,KACA,QAAA;GAGC,IAAM,IAAU,KAAK,OAAO,MAAM,GAC5B,IAAQ,KAAK,OAAO,IAAI,KACxB,IAAmB,KAAK,QAAQ,QACrC,CAAC,EAAE,WAAW,UAAU,EAAA,OAAA,EAAiB,EAAE,WAAW,UAAU,EAAA,OAAA,CAAA,EAChE;IACC,UAAU;IACV,QAAQ;IACR,MAAM;IAAA,CAAA;AAKH,QAAK,SAET,KAAK,YAAY,SAAA,CAAS;GAG3B,IAAM,IAAc,QAAK,MACnB,IAAY,OAAK,MAEjB,IAAgB,KAAK,YAAY,QAAQ,CAAC,EAAE,SAAS,GAAA,EAAe,EAAE,SAAS,GAAA,CAAA,EAAc;IAClG,UAAU;IACV,QAAQ;IACR,MAAM;IAAA,CAAA;AAcP,UAVA,EAAc,iBAAA;AACT,SAAK,OACR,KAAK,YAAY,SAAA,CAAS,KAE1B,KAAK,YAAY,MAAM,SAAS,QAChC,KAAK,YAAY,MAAM,UAAU;MAK5B,EAAI,EAAU,GAAkB,SAAA,EAAW,EAAU,GAAe,SAAA,CAAA,CAAW,KACrF,EAAU,KAAK,cAAA,CAAA;IAAA,EAGjB,QAAA;AAEC,QAAK,OAAA,CAAQ,KAAK;IAAA,EAEnB,EAAU,KAAK,cAAA,CAAA,CAEf,WAAA;;CAGH,SAAA;AACC,SAAO,CAAI;;;;;;;;;;qBAUQ,KAAK,OAAO,SAAS,QAAA;qBACrB,KAAK,WAAA;kBACR,KAAK,OAAO,aAAa,SAAA;eAC5B,MAAa,EAAE,iBAAA,CAAA;;;;;;;cAOhB,KAAK,WAAA;;;;GAvHjB,EAAS,EAAE,MAAM,SAAA,CAAA,CAAA,EAAU,EAAA,WAAA,QAAA,KAAA,EAAA,EAAA,EAAA,CAE3B,EAAM,WAAA,CAAA,EAAW,EAAA,WAAA,WAAA,KAAA,EAAA,EAAA,EAAA,CACjB,EAAM,4BAAA,CAAA,EAA0B,EAAA,WAAA,eAAA,KAAA,EAAA,EAAA,EAAA,CAGhC,EAAM,WAAA,CAAA,EAAW,EAAA,WAAA,WAAA,KAAA,EAAA,EAAA,IAAA,EAAA,CAxBlB,EAAc,gBAAA,CAAA,EAAgB,EAAA;AAAA,SAAA,KAAA"}
1
+ {"version":3,"file":"tree.js","names":[],"sources":["../src/tree/tree.ts"],"sourcesContent":["import { TailwindElement } from '@mixins/index'\nimport { css, html } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\nimport { fromEvent, merge, switchMap, takeUntil, tap, zip } from 'rxjs'\n\n/**\n * Expandable tree node — a recursive disclosure widget. One root slot, one default slot for child nodes. Each node can itself contain schmancy-tree children.\n *\n * @element schmancy-tree\n * @summary Use for hierarchical navigation / file-explorer layouts. Each level is a schmancy-tree with a `root` slot (the parent label) and default slot (the children, which may be more schmancy-trees).\n * @example\n * <schmancy-tree>\n * <schmancy-list-item slot=\"root\">src/</schmancy-list-item>\n * <schmancy-tree>\n * <schmancy-list-item slot=\"root\">components/</schmancy-list-item>\n * <schmancy-list-item>button.ts</schmancy-list-item>\n * </schmancy-tree>\n * </schmancy-tree>\n * @platform details toggle - Recursive `<details>`-like disclosure. Degrades to a plain nested list if the tag never registers — loses expand/collapse but stays navigable.\n * @slot root - The root element of the tree\n * @slot - The children of the tree\n */\n@customElement('schmancy-tree')\nexport class SchmancyTree extends TailwindElement(css`\n\t:host {\n\t\tdisplay: block;\n\t\tposition: relative;\n\t\tbackground-color: initial;\n\t}\n\t::slotted([slot='root']) {\n\t\twidth: 100%;\n\t\ttext-align: left;\n\t}\n\t::slotted([slot='root'] + *) {\n\t\tmargin-top: 0.5rem;\n\t}\n`) {\n\t/**\n\t * Whether the tree’s children are visible\n\t */\n\t@property({ type: Boolean }) open = false\n\n\t@query('#toggler') toggler!: HTMLSlotElement\n\t@query('slot:not([name=\"root\"])') defaultSlot!: HTMLSlotElement\n\n\t// Since it's actually a <schmancy-button>, use HTMLElement or a custom type\n\t@query('#chevron') chevron!: HTMLElement\n\n\tprivate readonly _a11yId = `schmancy-tree-${Math.random().toString(36).slice(2, 10)}`\n\tprivate get _contentId() { return `${this._a11yId}-content` }\n\n\t/** ElementInternals — broadcasts `:state(open)` for consumer CSS. */\n\tprivate readonly _internals: ElementInternals | undefined = (() => {\n\t\ttry { return this.attachInternals() } catch { return undefined }\n\t})()\n\n\tupdated(changed: Map<string, unknown>) {\n\t\tsuper.updated?.(changed)\n\t\tif (changed.has('open')) {\n\t\t\tif (this.open) this._internals?.states.add('open')\n\t\t\telse this._internals?.states.delete('open')\n\t\t}\n\t}\n\n\tfirstUpdated() {\n\t\t// Hide or show the slot initially based on `open`\n\t\tif (!this.open) {\n\t\t\tthis.defaultSlot.hidden = true\n\t\t}\n\n\t\t// Root toggler\n\t\tconst toggleClick$ = fromEvent<MouseEvent>(this.toggler, 'click').pipe(\n\t\t\ttakeUntil(this.disconnecting),\n\t\t\ttap(e => {\n\t\t\t\te.preventDefault()\n\t\t\t\te.stopPropagation()\n\t\t\t\tthis.dispatchEvent(new CustomEvent('toggle', { bubbles: false, composed: true }))\n\t\t\t}),\n\t\t)\n\n\t\t// Chevron (the schmancy-button) click\n\t\tconst chevronClick$ = fromEvent<MouseEvent>(this.chevron, 'click')\n\n\t\tmerge(toggleClick$, chevronClick$)\n\t\t\t.pipe(\n\t\t\t\tswitchMap(() => {\n\t\t\t\t\t// 1. Animate the chevron rotation\n\t\t\t\t\t// If `open` is true, rotate from 180 -> 0; if false, from 0 -> 180\n\t\t\t\t\tconst fromDeg = this.open ? 180 : 0\n\t\t\t\t\tconst toDeg = this.open ? 0 : 180\n\t\t\t\t\tconst chevronAnimation = this.chevron.animate(\n\t\t\t\t\t\t[{ transform: `rotate(${fromDeg}deg)` }, { transform: `rotate(${toDeg}deg)` }],\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tduration: 150,\n\t\t\t\t\t\t\teasing: 'ease-in',\n\t\t\t\t\t\t\tfill: 'forwards',\n\t\t\t\t\t\t},\n\t\t\t\t\t)\n\n\t\t\t\t\t// 2. Animate the slot’s height + opacity\n\t\t\t\t\tif (!this.open) {\n\t\t\t\t\t\t// We are about to open, so remove `hidden` to measure scrollHeight\n\t\t\t\t\t\tthis.defaultSlot.hidden = false\n\t\t\t\t\t}\n\n\t\t\t\t\tconst fromOpacity = this.open ? 1 : 0\n\t\t\t\t\tconst toOpacity = this.open ? 0 : 1\n\n\t\t\t\t\tconst slotAnimation = this.defaultSlot.animate([{ opacity: fromOpacity }, { opacity: toOpacity }], {\n\t\t\t\t\t\tduration: 150,\n\t\t\t\t\t\teasing: 'ease-out',\n\t\t\t\t\t\tfill: 'forwards',\n\t\t\t\t\t})\n\n\t\t\t\t\t// Hide the slot if we just closed it\n\t\t\t\t\tslotAnimation.onfinish = () => {\n\t\t\t\t\t\tif (this.open) {\n\t\t\t\t\t\t\tthis.defaultSlot.hidden = true\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.defaultSlot.style.height = 'auto'\n\t\t\t\t\t\t\tthis.defaultSlot.style.opacity = '1'\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Return an Observable that completes when both animations finish\n\t\t\t\t\treturn zip(fromEvent(chevronAnimation, 'finish'), fromEvent(slotAnimation, 'finish')).pipe(\n\t\t\t\t\t\ttakeUntil(this.disconnecting),\n\t\t\t\t\t)\n\t\t\t\t}),\n\t\t\t\ttap(() => {\n\t\t\t\t\t// Flip the open state\n\t\t\t\t\tthis.open = !this.open\n\t\t\t\t}),\n\t\t\t\ttakeUntil(this.disconnecting),\n\t\t\t)\n\t\t\t.subscribe()\n\t}\n\n\trender() {\n\t\treturn html`\n\t\t\t<div class=\"flex content-center items-center justify-between\">\n\t\t\t\t<!-- Root toggler content -->\n\t\t\t\t<slot id=\"toggler\" name=\"root\"></slot>\n\n\t\t\t\t<!-- The chevron or arrow symbol -->\n\t\t\t\t<!-- Stop propagation on the schmancy-button itself just to avoid double triggers -->\n\t\t\t\t<schmancy-button\n\t\t\t\t\tslot=\"trailing\"\n\t\t\t\t\tid=\"chevron\"\n\t\t\t\t\taria-expanded=${this.open ? 'true' : 'false'}\n\t\t\t\t\taria-controls=${this._contentId}\n\t\t\t\t\taria-label=${this.open ? 'Collapse' : 'Expand'}\n\t\t\t\t\t@click=${(e: Event) => e.stopPropagation()}\n\t\t\t\t>\n\t\t\t\t\t⌅\n\t\t\t\t</schmancy-button>\n\t\t\t</div>\n\n\t\t\t<!-- The default slot: tree children -->\n\t\t\t<slot id=${this._contentId}></slot>\n\t\t`\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'schmancy-tree': SchmancyTree\n\t}\n}\n"],"mappings":";;;;;;AAuBO,IAAA,IAAA,cAA2B,EAAgB,CAAG;;;;;;;;;;;;;;;4BAiBhB,GAAA,KAAA,UAQT,iBAAiB,KAAK,QAAA,CAAS,SAAS,GAAA,CAAI,MAAM,GAAG,GAAA,IAAA,KAAA,oBAAA;AAK/E,OAAA;AAAM,WAAO,KAAK,iBAAA;WAAA;AAA4B;;MAAA;;CAJ/C,IAAA,aAAY;AAAe,SAAO,GAAG,KAAK,QAAA;;CAO1C,QAAQ,GAAA;AACP,QAAM,UAAU,EAAA,EACZ,EAAQ,IAAI,OAAA,KACX,KAAK,OAAM,KAAK,YAAY,OAAO,IAAI,OAAA,GACtC,KAAK,YAAY,OAAO,OAAO,OAAA;;CAItC,eAAA;AAEM,OAAK,SACT,KAAK,YAAY,SAAA,CAAS,IAgB3B,EAZqB,EAAsB,KAAK,SAAS,QAAA,CAAS,KACjE,EAAU,KAAK,cAAA,EACf,GAAI,MAAA;AACH,KAAE,gBAAA,EACF,EAAE,iBAAA,EACF,KAAK,cAAc,IAAI,YAAY,UAAU;IAAE,SAAA,CAAS;IAAO,UAAA,CAAU;IAAA,CAAA,CAAA;IAAA,CAAA,EAKrD,EAAsB,KAAK,SAAS,QAAA,CAAA,CAGxD,KACA,QAAA;GAGC,IAAM,IAAU,KAAK,OAAO,MAAM,GAC5B,IAAQ,KAAK,OAAO,IAAI,KACxB,IAAmB,KAAK,QAAQ,QACrC,CAAC,EAAE,WAAW,UAAU,EAAA,OAAA,EAAiB,EAAE,WAAW,UAAU,EAAA,OAAA,CAAA,EAChE;IACC,UAAU;IACV,QAAQ;IACR,MAAM;IAAA,CAAA;AAKH,QAAK,SAET,KAAK,YAAY,SAAA,CAAS;GAG3B,IAAM,IAAc,QAAK,MACnB,IAAY,OAAK,MAEjB,IAAgB,KAAK,YAAY,QAAQ,CAAC,EAAE,SAAS,GAAA,EAAe,EAAE,SAAS,GAAA,CAAA,EAAc;IAClG,UAAU;IACV,QAAQ;IACR,MAAM;IAAA,CAAA;AAcP,UAVA,EAAc,iBAAA;AACT,SAAK,OACR,KAAK,YAAY,SAAA,CAAS,KAE1B,KAAK,YAAY,MAAM,SAAS,QAChC,KAAK,YAAY,MAAM,UAAU;MAK5B,EAAI,EAAU,GAAkB,SAAA,EAAW,EAAU,GAAe,SAAA,CAAA,CAAW,KACrF,EAAU,KAAK,cAAA,CAAA;IAAA,EAGjB,QAAA;AAEC,QAAK,OAAA,CAAQ,KAAK;IAAA,EAEnB,EAAU,KAAK,cAAA,CAAA,CAEf,WAAA;;CAGH,SAAA;AACC,SAAO,CAAI;;;;;;;;;;qBAUQ,KAAK,OAAO,SAAS,QAAA;qBACrB,KAAK,WAAA;kBACR,KAAK,OAAO,aAAa,SAAA;eAC5B,MAAa,EAAE,iBAAA,CAAA;;;;;;;cAOhB,KAAK,WAAA;;;;GAvHjB,EAAS,EAAE,MAAM,SAAA,CAAA,CAAA,EAAU,EAAA,WAAA,QAAA,KAAA,EAAA,EAAA,EAAA,CAE3B,EAAM,WAAA,CAAA,EAAW,EAAA,WAAA,WAAA,KAAA,EAAA,EAAA,EAAA,CACjB,EAAM,4BAAA,CAAA,EAA0B,EAAA,WAAA,eAAA,KAAA,EAAA,EAAA,EAAA,CAGhC,EAAM,WAAA,CAAA,EAAW,EAAA,WAAA,WAAA,KAAA,EAAA,EAAA,IAAA,EAAA,CAxBlB,EAAc,gBAAA,CAAA,EAAgB,EAAA;AAAA,SAAA,KAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mhmo91/schmancy",
3
- "version": "0.9.15",
3
+ "version": "0.9.17",
4
4
  "description": "UI library build with web components",
5
5
  "main": "./dist/index.js",
6
6
  "customElements": "custom-elements.json",
@@ -36,7 +36,18 @@ interface FilteredOption {
36
36
  }
37
37
 
38
38
  /**
39
- * Autocomplete input component with filtering and multi-select support.
39
+ * Combobox with type-ahead filtering over a list of `<schmancy-option>` children. Single or multi-select. Form-associated.
40
+ *
41
+ * @element schmancy-autocomplete
42
+ * @summary Use when users need to pick from a known list of options but the list is too long for a plain select dropdown. Prefer schmancy-select for short static lists.
43
+ * @example
44
+ * <schmancy-autocomplete name="country" label="Country" placeholder="Start typing…">
45
+ * <schmancy-option value="US">United States</schmancy-option>
46
+ * <schmancy-option value="CA">Canada</schmancy-option>
47
+ * <schmancy-option value="GB">United Kingdom</schmancy-option>
48
+ * </schmancy-autocomplete>
49
+ * @platform combobox change - Composed of a schmancy-input + a floating listbox populated from `<schmancy-option>` children. Multi-select renders selections as schmancy-input-chip chips. Degrades to a datalist-backed native input if the tag never registers.
50
+ * @fires change - `SchmancyAutocompleteChangeEvent` with `{ value }` (single) or `{ value, values }` (multi).
40
51
  *
41
52
  * @prop {string} name - Name attribute for form submission
42
53
  * @prop {string} label - Label text displayed above the input
@@ -16,8 +16,13 @@ export type ButtonVariant = 'elevated' | 'filled' | 'filled tonal' | 'tonal' | '
16
16
  export type ButtonColor = 'primary' | 'secondary' | 'success' | 'error' | 'warning' | 'info' | 'neutral'
17
17
 
18
18
  /**
19
- * A button component.
19
+ * Material Design button — primary interactive surface for triggering actions or navigation.
20
20
  * @element schmancy-button
21
+ * @summary Trigger actions or navigate. Form-associated; participates in native form submission.
22
+ * @example
23
+ * <schmancy-button variant="filled" @click=${() => save()}>Save</schmancy-button>
24
+ * <schmancy-button variant="outlined" href="/next">Continue</schmancy-button>
25
+ * @platform button click - Schmancy-skinned native `<button type="submit">`. When `href` is set, degrades to `<a href="…">`. Falls back to plain `<button>` styled with Tailwind if the tag never registers.
21
26
  * @slot - The default slot.
22
27
  * @slot prefix - The prefix slot.
23
28
  * @slot suffix - The suffix slot.
@@ -7,8 +7,14 @@ import { magnetic } from '../directives/magnetic'
7
7
  import { ButtonVariant } from './button'
8
8
 
9
9
  /**
10
- * An icon button component.
10
+ * Icon-only button for toolbar actions, close affordances, or overflow menus.
11
11
  * @element schmancy-icon-button
12
+ * @summary Compact round/square button wrapping a single icon glyph. Form-associated like schmancy-button.
13
+ * @example
14
+ * <schmancy-icon-button aria-label="Close" @click=${() => close()}>
15
+ * <schmancy-icon>close</schmancy-icon>
16
+ * </schmancy-icon-button>
17
+ * @platform button click - Schmancy-skinned native `<button>` (or `<a>` when `href` is set). aria-label is required for a11y because there's no text content.
12
18
  * @slot - The default slot (usually an icon or glyph).
13
19
  * @csspart base - The underlying native `<button>` (or `<a>` when `href` is set).
14
20
  */
@@ -3,7 +3,16 @@ import { css, html } from 'lit'
3
3
  import { customElement } from 'lit/decorators.js'
4
4
 
5
5
  /**
6
+ * Action row of a schmancy-card — holds the card's buttons / links (typically aligned bottom-right).
7
+ *
6
8
  * @element schmancy-card-action
9
+ * @summary Always nested inside schmancy-card. Holds the primary + secondary CTAs for the card.
10
+ * @example
11
+ * <schmancy-card-action>
12
+ * <schmancy-button variant="text">Cancel</schmancy-button>
13
+ * <schmancy-button variant="filled">Save</schmancy-button>
14
+ * </schmancy-card-action>
15
+ * @platform div - Styled flex container. Degrades to a plain div if the tag never registers.
7
16
  * @slot - The content of the action
8
17
  */
9
18
  @customElement('schmancy-card-action')
package/src/card/card.ts CHANGED
@@ -4,6 +4,24 @@ import { customElement, property, state } from 'lit/decorators.js'
4
4
  import { cursorGlow } from '../directives/cursor-glow'
5
5
  import { ifDefined } from 'lit/directives/if-defined.js'
6
6
 
7
+ /**
8
+ * Material Design card — a surface-level container for grouping related content with media / content / actions slots.
9
+ *
10
+ * @element schmancy-card
11
+ * @summary Use for discrete pieces of content that appear in a list (a product, a note, a message). Combine with schmancy-card-media / schmancy-card-content / schmancy-card-action children.
12
+ * @example
13
+ * <schmancy-card type="elevated" href="/items/42">
14
+ * <schmancy-card-media src="/thumb.jpg" alt="Thumbnail"></schmancy-card-media>
15
+ * <schmancy-card-content>
16
+ * <h3>Title</h3>
17
+ * <p>One-line description of the card's content.</p>
18
+ * </schmancy-card-content>
19
+ * <schmancy-card-action>
20
+ * <schmancy-button variant="text">Open</schmancy-button>
21
+ * </schmancy-card-action>
22
+ * </schmancy-card>
23
+ * @platform div - Styled `<div>`; becomes an `<a>` when `href` is set so the whole card is a single interactive surface. Degrades to a plain div/a if the tag never registers.
24
+ */
7
25
  @customElement('schmancy-card')
8
26
  export default class SchmancyCard extends TailwindElement(css`
9
27
  :host {
@@ -3,7 +3,16 @@ import { css, html } from 'lit'
3
3
  import { customElement } from 'lit/decorators.js'
4
4
 
5
5
  /**
6
+ * Content region of a schmancy-card — holds the card's headline, supporting text, and inline controls.
7
+ *
6
8
  * @element schmancy-card-content
9
+ * @summary Always nested inside schmancy-card. The padded content block between the media and action rows.
10
+ * @example
11
+ * <schmancy-card-content>
12
+ * <h3>Card title</h3>
13
+ * <p>Supporting text that describes the card's subject.</p>
14
+ * </schmancy-card-content>
15
+ * @platform div - Styled `<div>` with padding. Degrades to a plain div if the tag never registers.
7
16
  */
8
17
  @customElement('schmancy-card-content')
9
18
  export default class SchmancyCardContent extends TailwindElement(css`
package/src/card/media.ts CHANGED
@@ -3,7 +3,13 @@ import { css, html } from 'lit'
3
3
  import { customElement, property } from 'lit/decorators.js'
4
4
 
5
5
  /**
6
+ * Media region of a schmancy-card — the image / thumbnail at the top of the card.
7
+ *
6
8
  * @element schmancy-card-media
9
+ * @summary Always nested inside schmancy-card. Pass `src` + `alt` props or slot an `<img>` / `<video>` for custom media.
10
+ * @example
11
+ * <schmancy-card-media src="/thumb.jpg" alt="Product photo"></schmancy-card-media>
12
+ * @platform img - Renders an `<img>` (or wraps a slotted media element). Degrades to a styled `<img>` if the tag never registers.
7
13
  */
8
14
  @customElement('schmancy-card-media')
9
15
  export default class SchmancyCardMedia extends TailwindElement(css`
@@ -9,9 +9,15 @@ export type schmancyCheckBoxChangeEvent = CustomEvent<{
9
9
  }>
10
10
 
11
11
  /**
12
+ * Binary checkbox for multi-select or boolean form fields. Wraps Material Web's `<md-checkbox>`; form-associated.
13
+ *
12
14
  * @element schmancy-checkbox
15
+ * @summary Use for "select many from a list" or any boolean that's part of a form submission. Prefer schmancy-switch for immediate-effect toggles.
16
+ * @example
17
+ * <schmancy-checkbox name="tos" required>I accept the terms</schmancy-checkbox>
18
+ * @platform checkbox change - Wraps `<md-checkbox>` from `@material/web`. Degrades to styled native `<input type="checkbox">` if the tag never registers.
13
19
  * @slot - The label for the checkbox.
14
- * @fires valueChange - Event fired when the checkbox value changes.
20
+ * @fires valueChange - `CustomEvent<{ value: boolean }>` when the checkbox is toggled.
15
21
  **/
16
22
 
17
23
  @customElement('schmancy-checkbox')
@@ -7,6 +7,20 @@ import { fullWidth } from '../directives/layout'
7
7
  import type { FilterChipChangeEvent as SchmancyChipChangeEvent } from './filter-chip'
8
8
  import { SchmancyFilterChip as SchmancyChip } from './filter-chip'
9
9
 
10
+ /**
11
+ * Filter-chip group — container for selectable `<schmancy-chip>` children. Single or multi-select.
12
+ *
13
+ * @element schmancy-chips
14
+ * @summary Use for filtering or choosing from 2–8 mutually-visible options ("Status: active / paused / archived"). Prefer schmancy-select when the list gets long or vertical.
15
+ * @example
16
+ * <schmancy-chips multi @change=${(e) => this.filters = e.detail.values}>
17
+ * <schmancy-chip value="active">Active</schmancy-chip>
18
+ * <schmancy-chip value="paused">Paused</schmancy-chip>
19
+ * <schmancy-chip value="archived">Archived</schmancy-chip>
20
+ * </schmancy-chips>
21
+ * @platform chip-group change - No direct native equivalent. Degrades to a styled schmancy-select with similar semantics if the tag never registers.
22
+ * @fires change - `CustomEvent<{ value: string }>` (single) or `{ values: string[] }` (multi).
23
+ */
10
24
  @customElement('schmancy-chips')
11
25
  export default class SchmancyChips extends $LitElement(
12
26
 
@@ -6,7 +6,7 @@ import { BehaviorSubject, combineLatest } from 'rxjs'
6
6
  import { map, takeUntil, tap } from 'rxjs/operators'
7
7
 
8
8
  /**
9
- * Input chip component - represents user-provided information that can be removed.
9
+ * Input chip displays user-provided information (tags, recipients, filters) that can be removed but not toggled.
10
10
  *
11
11
  * IMPORTANT: Per Material Design 3 specification, input chips do NOT have selected state.
12
12
  * They represent discrete pieces of user input (like entered tags, selections from lists, etc.)
@@ -18,15 +18,15 @@ import { map, takeUntil, tap } from 'rxjs/operators'
18
18
  * - Tags or keywords entered by the user
19
19
  * - Selected items from a multi-select dropdown
20
20
  *
21
- * @fires click - Optional click event on chip body (value)
22
- * @fires remove - Dispatched when remove button is clicked (value)
23
- *
21
+ * @element schmancy-input-chip
22
+ * @summary Removable pill that represents a single user input value. No selected state — use schmancy-chip (filter chip) for toggleable options.
24
23
  * @example
25
- * ```html
26
24
  * <schmancy-input-chip value="john@example.com" avatar="/avatars/john.jpg">
27
25
  * John Doe
28
26
  * </schmancy-input-chip>
29
- * ```
27
+ * @platform chip remove - No native equivalent. Composed of a labeled pill + close button. Degrades to a styled `<span>` with a trailing close `<button>` if the tag never registers.
28
+ * @fires click - Optional click event on chip body (value)
29
+ * @fires remove - Dispatched when remove button is clicked (value)
30
30
  */
31
31
  @customElement('schmancy-input-chip')
32
32
  export class SchmancyInputChip extends TailwindElement(css`
@@ -9,6 +9,18 @@ import { distinctUntilChanged, filter, take, takeUntil, tap } from 'rxjs/operato
9
9
  import { SPRING_SNAPPY } from '../utils/animation.js'
10
10
  import { reducedMotion$ } from '../directives/reduced-motion'
11
11
 
12
+ /**
13
+ * Expandable disclosure panel — a styled `<details>` / `<summary>` pair with animated expand + overlay options.
14
+ *
15
+ * @element schmancy-details
16
+ * @summary Use for progressive-disclosure content: FAQs, collapsible settings sections, accordion-style lists. Prefer schmancy-expand for full-page accordions where only one section can be open at a time.
17
+ * @example
18
+ * <schmancy-details summary="Shipping details">
19
+ * <p>Order ships in 2 business days.</p>
20
+ * </schmancy-details>
21
+ * @platform details toggle - Wraps native `<details>`/`<summary>`. Degrades to the native element if the tag never registers — same keyboard accessibility, just no animation.
22
+ * @fires toggle - When the open state changes (bubbles from the native `<details>`).
23
+ */
12
24
  @customElement('schmancy-details')
13
25
  export default class SchmancyDetails extends SurfaceMixin(
14
26
  TailwindElement(css`
@@ -3,6 +3,17 @@ import { $LitElement } from '@mixins/index'
3
3
  import { css, html } from 'lit'
4
4
  import { customElement, property } from 'lit/decorators.js'
5
5
 
6
+ /**
7
+ * Thin horizontal (or vertical) separator rule between sections of content.
8
+ *
9
+ * @element schmancy-divider
10
+ * @summary Semantic separator between groups — list items, menu sections, content blocks. Uses outline theme token.
11
+ * @example
12
+ * <schmancy-list-item>First</schmancy-list-item>
13
+ * <schmancy-divider></schmancy-divider>
14
+ * <schmancy-list-item>Second</schmancy-list-item>
15
+ * @platform hr - Styled horizontal rule. Degrades to a native `<hr>` if the tag never registers.
16
+ */
6
17
  @customElement('schmancy-divider')
7
18
  export default class SchmancyDivider extends $LitElement(css`
8
19
  :host {
@@ -7,6 +7,18 @@ import { SPRING_SMOOTH } from '../utils/animation.js'
7
7
  import { reducedMotion$ } from '../directives/reduced-motion'
8
8
  import '../surface/surface.js'
9
9
 
10
+ /**
11
+ * Container for schmancy-expand children — coordinates mutual-exclusion so only one child is open at a time. Also renders the portal panel that the active child expands into.
12
+ *
13
+ * @element schmancy-expand-root
14
+ * @summary Always wrap a group of schmancy-expand children. Without a root, each schmancy-expand behaves independently (which is usually not what you want — prefer schmancy-details for that).
15
+ * @example
16
+ * <schmancy-expand-root>
17
+ * <schmancy-expand summary="Step 1">…</schmancy-expand>
18
+ * <schmancy-expand summary="Step 2">…</schmancy-expand>
19
+ * </schmancy-expand-root>
20
+ * @platform div - Coordinating wrapper. Degrades to a plain div if the tag never registers — children fall back to independent `<details>` behavior.
21
+ */
10
22
  @customElement('schmancy-expand-root')
11
23
  export class SchmancyExpandRoot extends SurfaceMixin(TailwindElement(css`
12
24
  :host {
@@ -12,6 +12,20 @@ import { SchmancyExpandRoot } from './expand-root.component.js'
12
12
  /** Dispatch this event on window to close whichever schmancy-expand is currently open */
13
13
  export const SCHMANCY_EXPAND_REQUEST_CLOSE = 'schmancy-expand-request-close'
14
14
 
15
+ /**
16
+ * Accordion-style section — expands on click, coordinates with siblings via schmancy-expand-root to close any sibling when a new one opens. Only one schmancy-expand can be open at a time within the same root.
17
+ *
18
+ * @element schmancy-expand
19
+ * @summary Use for grouped progressive-disclosure where only one section should be open at a time. Prefer schmancy-details when sections should be independent.
20
+ * @example
21
+ * <schmancy-expand-root>
22
+ * <schmancy-expand summary="Billing">Billing form…</schmancy-expand>
23
+ * <schmancy-expand summary="Shipping">Shipping form…</schmancy-expand>
24
+ * <schmancy-expand summary="Review">Order review…</schmancy-expand>
25
+ * </schmancy-expand-root>
26
+ * @platform details toggle - Schmancy-skinned accordion section. Degrades to `<details>` if the tag never registers — loses mutual-exclusion behavior but stays functional.
27
+ * @fires toggle - When the open state changes.
28
+ */
15
29
  @customElement('schmancy-expand')
16
30
  export default class SchmancyExpand extends TailwindElement(css`
17
31
  :host {
@@ -6,6 +6,16 @@ import { repeat } from 'lit/directives/repeat.js'
6
6
  import countriesData from './countries.data'
7
7
  import SchmancyAutocomplete from '@schmancy/autocomplete/autocomplete'
8
8
 
9
+ /**
10
+ * Country picker — type-ahead autocomplete over the ISO 3166-1 country list. Form-associated.
11
+ *
12
+ * @element schmancy-select-countries
13
+ * @summary Drop-in replacement for schmancy-autocomplete when the options are specifically "every country". Pre-seeds the list from countries.data.
14
+ * @example
15
+ * <schmancy-select-countries name="country" label="Shipping country" required></schmancy-select-countries>
16
+ * @platform combobox change - Composes schmancy-autocomplete with a static options list. Value is the 2-letter ISO code.
17
+ * @fires change - `SchmancyAutocompleteChangeEvent` with `{ value: string }` (the ISO code).
18
+ */
9
19
  @customElement('schmancy-select-countries')
10
20
  export class SchmancyCountriesSelect extends $LitElement(css`
11
21
  :host {
@@ -6,7 +6,14 @@ import timezonesData from './timezones.data'
6
6
  import SchmancyAutocomplete from '@schmancy/autocomplete/autocomplete'
7
7
 
8
8
  /**
9
- * Timezone selector component with autocomplete filtering.
9
+ * Timezone picker type-ahead autocomplete over the IANA tz database. Form-associated.
10
+ *
11
+ * @element schmancy-select-timezones
12
+ * @summary Drop-in replacement for schmancy-autocomplete when the options are IANA timezone names. Value is the IANA identifier ("America/Los_Angeles").
13
+ * @example
14
+ * <schmancy-select-timezones name="tz" label="Timezone" value="America/New_York"></schmancy-select-timezones>
15
+ * @platform combobox change - Composes schmancy-autocomplete with a static IANA timezones list.
16
+ * @fires change - `SchmancyAutocompleteChangeEvent` with `{ value: string }` (the IANA tz name).
10
17
  *
11
18
  * @prop {string} name - Name attribute for form submission
12
19
  * @prop {string} value - Selected timezone value
package/src/form/form.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import { customElement } from 'lit/decorators.js'
2
2
 
3
3
  /**
4
- * A thin ergonomic wrapper around a native `<form>` element. Its children are
5
- * reparented into a `<form>` element in light DOM on connection, so:
4
+ * Ergonomic wrapper around a native `<form>`. Children are reparented into a light-DOM `<form>` on connection so form-associated custom elements resolve `internals.form` via native DOM ancestry.
6
5
  *
7
6
  * - Form-associated custom elements (FACE) resolve their `internals.form`
8
7
  * correctly via native DOM ancestry.
@@ -18,6 +17,14 @@ import { customElement } from 'lit/decorators.js'
18
17
  * lifting is the platform's.
19
18
  *
20
19
  * @element schmancy-form
20
+ * @summary Always wrap form-associated schmancy components in schmancy-form (or a native `<form>`) so `new FormData(form)` just works.
21
+ * @example
22
+ * <schmancy-form @submit=${(e) => console.log(Object.fromEntries(e.detail))}>
23
+ * <schmancy-input name="email" type="email" required></schmancy-input>
24
+ * <schmancy-input name="password" type="password" required></schmancy-input>
25
+ * <schmancy-button type="submit" variant="filled">Sign in</schmancy-button>
26
+ * </schmancy-form>
27
+ * @platform form submit - Light-DOM native `<form>` element. Degrades to a `<form>` if the tag never registers — same semantics, just no CustomEvent translation.
21
28
  * @fires submit - `CustomEvent<FormData>` emitted when the form is submitted.
22
29
  * @fires reset - Emitted after the underlying form resets.
23
30
  */
@@ -43,11 +43,18 @@ export type SchmancyInputEnterEvent = CustomEvent<EventDetails>
43
43
  export type InputSize = 'xxs' | 'xs' | 'sm' | 'md' | 'lg'
44
44
 
45
45
  /**
46
- * Enhanced version of the SchmancyInput component with improved form integration
47
- * and compatibility with legacy API.
46
+ * Single-line text input the primary form-text primitive. Form-associated via ElementInternals, so it participates in native `<form>` submission, validation, and reset without additional wiring.
48
47
  *
49
- * This component uses the native form association API and maintains parity with
50
- * native input behaviors while providing a stylish, accessible interface.
48
+ * @element schmancy-input
49
+ * @summary Text input with Material Design styling, native form integration, and RxJS-debounced input/change/enter events.
50
+ * @example
51
+ * <schmancy-form @submit=${onSubmit}>
52
+ * <schmancy-input name="email" type="email" label="Email" required></schmancy-input>
53
+ * </schmancy-form>
54
+ * @platform input change - Schmancy-skinned native `<input>`. Degrades to `<input class="…">` styled via Tailwind if the tag never registers.
55
+ * @fires input - `CustomEvent<{value: string}>` on every keystroke.
56
+ * @fires change - `CustomEvent<{value: string}>` on blur/change.
57
+ * @fires enter - `CustomEvent<{value: string}>` when user presses Enter.
51
58
  *
52
59
  * @prop {string} name - Name attribute for form submission (inherited from FormFieldMixin)
53
60
  * @prop {string} label - Label text for the form field (inherited from FormFieldMixin)
@@ -49,7 +49,11 @@ declare global {
49
49
  }
50
50
 
51
51
  /**
52
- * A custom scrollable container with enhanced features.
52
+ * Scrollable container with debounced scroll events, horizontal/vertical direction, optional hidden scrollbar, and programmatic scrollTo via command events or refs.
53
+ *
54
+ * @element schmancy-scroll
55
+ * @summary Use anywhere you'd reach for `overflow: auto` but also need debounced scroll events (for sticky headers, scroll spies, virtualization triggers) or the ability to drive scroll from elsewhere in the app by dispatching a schmancy-scroll-command event.
56
+ * @platform div - Styled scrollable `<div>`. Degrades to a plain scrollable div if the tag never registers — loses the debounced scroll event and the command-bus integration.
53
57
  *
54
58
  * @fires {SchmancyScrollEvent} scroll - Fired when scrolling occurs (with a configurable debounce)
55
59
  * @slot - Default slot for content to be scrolled
package/src/page/page.ts CHANGED
@@ -8,20 +8,17 @@ import { theme } from '../theme/theme.service'
8
8
  import { fromResizeObserver } from '../directives/layout'
9
9
 
10
10
  /**
11
- * Native mobile-like page container.
12
- * Prevents double-tap zoom, pull-to-refresh, rubber-banding.
13
- * Automatically fills remaining viewport height.
11
+ * Mobile-first page container — fills remaining viewport height, suppresses double-tap zoom / pull-to-refresh / rubber-banding. Lays children in a CSS grid whose row template is `rows`.
14
12
  *
15
13
  * @element schmancy-page
16
- *
14
+ * @summary The root of any app view — wraps header / main / footer children in a full-viewport grid. Use rows="auto_1fr_auto" to make the middle child scroll while header/footer stay pinned.
17
15
  * @example
18
- * html`
19
- * <schmancy-page rows="1fr_2fr_auto">
20
- * <header>App Bar</header>
21
- * <main>Scrollable content</main>
22
- * <footer>Navigation</footer>
23
- * </schmancy-page>
24
- * `
16
+ * <schmancy-page rows="auto_1fr_auto">
17
+ * <schmancy-nav-drawer-appbar>Title</schmancy-nav-drawer-appbar>
18
+ * <main>Scrollable content</main>
19
+ * <schmancy-navigation-bar></schmancy-navigation-bar>
20
+ * </schmancy-page>
21
+ * @platform div - Full-height CSS-grid container. Degrades to a plain div if the tag never registers — children still flow vertically but without the height fill and gesture suppression.
25
22
  */
26
23
  @customElement('schmancy-page')
27
24
  export class SchmancyPage extends $LitElement(css`
@@ -5,7 +5,16 @@ import { FormFieldMixin } from '../../mixins/formField.mixin'
5
5
  import { fromEvent, takeUntil } from 'rxjs'
6
6
 
7
7
  /**
8
- * Radio button component for use within radio groups.
8
+ * Single radio button always rendered as a child of `<schmancy-radio-group>`, never standalone.
9
+ *
10
+ * @element schmancy-radio-button
11
+ * @summary Low-level primitive. Use schmancy-radio-group and pass `.options` for the common path; only instantiate schmancy-radio-button directly when you need per-button custom rendering.
12
+ * @example
13
+ * <schmancy-radio-group name="plan">
14
+ * <schmancy-radio-button value="free">Free</schmancy-radio-button>
15
+ * <schmancy-radio-button value="pro" checked>Pro</schmancy-radio-button>
16
+ * </schmancy-radio-group>
17
+ * @platform radio change - Schmancy-skinned `<input type="radio">` semantics. Degrades to native radio if the tag never registers.
9
18
  *
10
19
  * @prop {string} name - Name attribute for grouping radio buttons
11
20
  * @prop {string} value - Value of this radio button
@@ -13,6 +13,24 @@ export type SchmancyRadioGroupOption = {
13
13
  export type SchmancyRadioGroupChangeEvent = CustomEvent<{
14
14
  value: string
15
15
  }>
16
+ /**
17
+ * Radio-button group — single-select from a static list of mutually-exclusive options. Form-associated.
18
+ *
19
+ * @element schmancy-radio-group
20
+ * @summary Use for 2–5 mutually-exclusive options where all should stay visible ("Shipping: standard / express / overnight"). Prefer schmancy-select when the list grows.
21
+ * @example
22
+ * <schmancy-radio-group
23
+ * name="shipping"
24
+ * label="Shipping"
25
+ * .options=${[
26
+ * { label: 'Standard (5 days)', value: 'standard' },
27
+ * { label: 'Express (2 days)', value: 'express' },
28
+ * { label: 'Overnight', value: 'overnight' },
29
+ * ]}
30
+ * ></schmancy-radio-group>
31
+ * @platform radiogroup change - Renders schmancy-radio-button children. Degrades to a fieldset with native `<input type="radio" name="…">` siblings if the tag never registers.
32
+ * @fires change - `SchmancyRadioGroupChangeEvent` with the selected `value`.
33
+ */
16
34
  @customElement('schmancy-radio-group')
17
35
  export class RadioGroup extends FormFieldMixin(TailwindElement(style)) {
18
36
  @property({ type: String }) override label = ''
@@ -15,7 +15,18 @@ export type SchmancySelectChangeEvent = CustomEvent<{
15
15
  }>
16
16
 
17
17
  /**
18
- * Select dropdown component with single and multi-select support.
18
+ * Dropdown selector single or multi-select from a list of `<schmancy-option>` children. Form-associated.
19
+ *
20
+ * @element schmancy-select
21
+ * @summary Material Design dropdown with type-to-filter, keyboard nav, single or multi-select. Options are declared as `<schmancy-option>` children; value / values props sync with selection.
22
+ * @example
23
+ * <schmancy-select name="priority" label="Priority" value="medium">
24
+ * <schmancy-option value="low">Low</schmancy-option>
25
+ * <schmancy-option value="medium">Medium</schmancy-option>
26
+ * <schmancy-option value="high">High</schmancy-option>
27
+ * </schmancy-select>
28
+ * @platform select change - Floating-UI-positioned listbox. Degrades to native `<select>` styled via Tailwind if the tag never registers, though multi-select UX is lost.
29
+ * @fires change - `SchmancySelectChangeEvent` with `{ value }` (single) or `{ value: string[] }` (multi).
19
30
  *
20
31
  * @prop {string} name - Name attribute for form submission
21
32
  * @prop {string} label - Label text displayed above the select
@@ -11,22 +11,16 @@ export const SchmancySurfaceTypeContext = createContext<TSurfaceColor>('surface'
11
11
  export type { SchmancySurfaceFill, SchmancySurfaceRounded, SchmancySurfaceElevation } from '@mixins/surface.mixin'
12
12
 
13
13
  /**
14
- * `<schmancy-surface>` component
15
- *
16
- * This component renders a styled container that adapts its dimensions based on the `fill` property.
17
- * It supports various rounding options, elevation levels, and applies background and text color classes
18
- * based on the specified surface variant. Additionally, when the `scroller` property is true, the component
19
- * enables internal scrolling by applying overflow and scroll-behavior styles.
20
- *
21
- * SurfaceMixin automatically provides surfaceStyles CSS.
14
+ * Themed container — the root surface primitive. Sets background, text color, rounding, elevation, and (optionally) internal scroll. Provides a `SchmancySurfaceTypeContext` so descendants can adapt to the enclosing surface variant.
22
15
  *
23
16
  * @element schmancy-surface
24
- * @slot - Default slot for projecting child content.
25
- *
17
+ * @summary Wrap a region of a page when you need it to pick up theme tokens (background + on-color + elevation). Nest surfaces to express Material Design's hierarchical color stacking.
26
18
  * @example
27
19
  * <schmancy-surface fill="all" rounded="all" elevation="3" type="surfaceBright" scroller>
28
20
  * <p>Your scrollable content here</p>
29
21
  * </schmancy-surface>
22
+ * @platform div - Styled `<div>` with theme-driven background/color/elevation. Degrades to a plain `<div>` if the tag never registers — text stays readable, just loses theming.
23
+ * @slot - Default slot for projecting child content.
30
24
  */
31
25
  @customElement('schmancy-surface')
32
26
  export class SchmancySurface extends SurfaceMixin(
@@ -5,12 +5,15 @@ import { customElement, property } from 'lit/decorators.js'
5
5
  export type SchmancySwitchChangeEvent = CustomEvent<{ value: boolean }>
6
6
 
7
7
  /**
8
- * Binary on/off control. Form-associated, keyboard-accessible, semantically a
9
- * switch (ARIA role="switch"). Distinct from `schmancy-checkbox`: a switch
10
- * represents an immediate state change, a checkbox represents a selection in
11
- * a form to be submitted.
8
+ * Binary on/off control with immediate effect. Form-associated, keyboard-accessible, semantically a switch (ARIA role="switch"). Distinct from `schmancy-checkbox`: a switch represents an immediate state change, a checkbox represents a selection in a form to be submitted.
12
9
  *
13
10
  * @element schmancy-switch
11
+ * @summary Use when flipping the control takes effect right away (e.g. "Dark mode", "Enable notifications"). Prefer schmancy-checkbox for form submissions.
12
+ * @example
13
+ * <schmancy-switch ?checked=${this.darkMode} @change=${(e) => this.darkMode = e.detail.value}>
14
+ * Dark mode
15
+ * </schmancy-switch>
16
+ * @platform switch change - Accessible native `<button role="switch" aria-checked>` under the hood. No native HTML element exists; falls back to a styled checkbox if the tag never registers.
14
17
  * @fires change - `CustomEvent<{ value: boolean }>` when the state changes.
15
18
  * @attr checked - Initial checked state (also reflected via `value`).
16
19
  * @attr disabled - Disables interaction.