@spfx-fast/sharepoint-ui 0.0.0-stage → 0.4.1

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 (156) hide show
  1. package/AGENTS.md +211 -0
  2. package/LICENSE +21 -0
  3. package/README.md +897 -2
  4. package/components.json +5811 -0
  5. package/dist/appearance.d.ts +6 -0
  6. package/dist/appearance.js +217 -0
  7. package/dist/elements/accordion.d.ts +9 -0
  8. package/dist/elements/accordion.js +338 -0
  9. package/dist/elements/alert.d.ts +2 -0
  10. package/dist/elements/alert.js +62 -0
  11. package/dist/elements/avatar-group-primitives.d.ts +3 -0
  12. package/dist/elements/avatar-group-primitives.js +189 -0
  13. package/dist/elements/avatar-group.d.ts +2 -0
  14. package/dist/elements/avatar-group.js +110 -0
  15. package/dist/elements/avatar.d.ts +4 -0
  16. package/dist/elements/avatar.js +251 -0
  17. package/dist/elements/badge.d.ts +8 -0
  18. package/dist/elements/badge.js +182 -0
  19. package/dist/elements/breadcrumb.d.ts +4 -0
  20. package/dist/elements/breadcrumb.js +233 -0
  21. package/dist/elements/button-group.d.ts +1 -0
  22. package/dist/elements/button-group.js +20 -0
  23. package/dist/elements/button-variants.d.ts +6 -0
  24. package/dist/elements/button-variants.js +339 -0
  25. package/dist/elements/button.d.ts +5 -0
  26. package/dist/elements/button.js +233 -0
  27. package/dist/elements/card-primitives.d.ts +3 -0
  28. package/dist/elements/card-primitives.js +83 -0
  29. package/dist/elements/card.d.ts +5 -0
  30. package/dist/elements/card.js +213 -0
  31. package/dist/elements/carousel.d.ts +92 -0
  32. package/dist/elements/carousel.js +1112 -0
  33. package/dist/elements/color-picker.d.ts +24 -0
  34. package/dist/elements/color-picker.js +1004 -0
  35. package/dist/elements/combobox.d.ts +1 -0
  36. package/dist/elements/combobox.js +1012 -0
  37. package/dist/elements/data-grid-model.d.ts +117 -0
  38. package/dist/elements/data-grid-model.js +294 -0
  39. package/dist/elements/data-grid.d.ts +29 -0
  40. package/dist/elements/data-grid.js +1567 -0
  41. package/dist/elements/date-picker.d.ts +1 -0
  42. package/dist/elements/date-picker.js +486 -0
  43. package/dist/elements/dialog.d.ts +23 -0
  44. package/dist/elements/dialog.js +409 -0
  45. package/dist/elements/divider.d.ts +2 -0
  46. package/dist/elements/divider.js +66 -0
  47. package/dist/elements/drawer.d.ts +3 -0
  48. package/dist/elements/drawer.js +308 -0
  49. package/dist/elements/field-composition.d.ts +26 -0
  50. package/dist/elements/field-composition.js +133 -0
  51. package/dist/elements/field.d.ts +1 -0
  52. package/dist/elements/field.js +67 -0
  53. package/dist/elements/file-upload.d.ts +6 -0
  54. package/dist/elements/file-upload.js +211 -0
  55. package/dist/elements/floating-position.d.ts +26 -0
  56. package/dist/elements/floating-position.js +39 -0
  57. package/dist/elements/image.d.ts +28 -0
  58. package/dist/elements/image.js +38 -0
  59. package/dist/elements/info.d.ts +9 -0
  60. package/dist/elements/info.js +102 -0
  61. package/dist/elements/input.d.ts +5 -0
  62. package/dist/elements/input.js +255 -0
  63. package/dist/elements/label.d.ts +15 -0
  64. package/dist/elements/label.js +56 -0
  65. package/dist/elements/link.d.ts +1 -0
  66. package/dist/elements/link.js +92 -0
  67. package/dist/elements/list.d.ts +19 -0
  68. package/dist/elements/list.js +297 -0
  69. package/dist/elements/menu.d.ts +33 -0
  70. package/dist/elements/menu.js +1103 -0
  71. package/dist/elements/message-bar.d.ts +10 -0
  72. package/dist/elements/message-bar.js +299 -0
  73. package/dist/elements/nav.d.ts +32 -0
  74. package/dist/elements/nav.js +791 -0
  75. package/dist/elements/overflow.d.ts +9 -0
  76. package/dist/elements/overflow.js +218 -0
  77. package/dist/elements/persona.d.ts +3 -0
  78. package/dist/elements/persona.js +125 -0
  79. package/dist/elements/popover.d.ts +77 -0
  80. package/dist/elements/popover.js +840 -0
  81. package/dist/elements/progress-bar.d.ts +4 -0
  82. package/dist/elements/progress-bar.js +82 -0
  83. package/dist/elements/radio-group.d.ts +1 -0
  84. package/dist/elements/radio-group.js +303 -0
  85. package/dist/elements/rating-display.d.ts +2 -0
  86. package/dist/elements/rating-display.js +125 -0
  87. package/dist/elements/rating.d.ts +2 -0
  88. package/dist/elements/rating.js +206 -0
  89. package/dist/elements/rich-text-editor.d.ts +18 -0
  90. package/dist/elements/rich-text-editor.js +508 -0
  91. package/dist/elements/search.d.ts +3 -0
  92. package/dist/elements/search.js +207 -0
  93. package/dist/elements/select.d.ts +6 -0
  94. package/dist/elements/select.js +291 -0
  95. package/dist/elements/shared.d.ts +19 -0
  96. package/dist/elements/shared.js +396 -0
  97. package/dist/elements/skeleton.d.ts +6 -0
  98. package/dist/elements/skeleton.js +91 -0
  99. package/dist/elements/slider.d.ts +1 -0
  100. package/dist/elements/slider.js +248 -0
  101. package/dist/elements/spin-button.d.ts +7 -0
  102. package/dist/elements/spin-button.js +499 -0
  103. package/dist/elements/spinner.d.ts +4 -0
  104. package/dist/elements/spinner.js +124 -0
  105. package/dist/elements/swatch-picker.d.ts +5 -0
  106. package/dist/elements/swatch-picker.js +363 -0
  107. package/dist/elements/table.d.ts +21 -0
  108. package/dist/elements/table.js +261 -0
  109. package/dist/elements/tabs.d.ts +1 -0
  110. package/dist/elements/tabs.js +276 -0
  111. package/dist/elements/tag-picker.d.ts +29 -0
  112. package/dist/elements/tag-picker.js +697 -0
  113. package/dist/elements/tags.d.ts +21 -0
  114. package/dist/elements/tags.js +421 -0
  115. package/dist/elements/teaching-popover.d.ts +32 -0
  116. package/dist/elements/teaching-popover.js +412 -0
  117. package/dist/elements/text.d.ts +9 -0
  118. package/dist/elements/text.js +75 -0
  119. package/dist/elements/textarea.d.ts +1 -0
  120. package/dist/elements/textarea.js +182 -0
  121. package/dist/elements/toast.d.ts +25 -0
  122. package/dist/elements/toast.js +337 -0
  123. package/dist/elements/toaster.d.ts +6 -0
  124. package/dist/elements/toaster.js +356 -0
  125. package/dist/elements/toggle.d.ts +2 -0
  126. package/dist/elements/toggle.js +246 -0
  127. package/dist/elements/toolbar.d.ts +12 -0
  128. package/dist/elements/toolbar.js +381 -0
  129. package/dist/elements/tooltip.d.ts +76 -0
  130. package/dist/elements/tooltip.js +510 -0
  131. package/dist/elements/tree-model.d.ts +19 -0
  132. package/dist/elements/tree-model.js +34 -0
  133. package/dist/elements/tree.d.ts +5 -0
  134. package/dist/elements/tree.js +273 -0
  135. package/dist/elements/view-card.d.ts +2 -0
  136. package/dist/elements/view-card.js +78 -0
  137. package/dist/elements/virtual-list.d.ts +41 -0
  138. package/dist/elements/virtual-list.js +369 -0
  139. package/dist/elements.d.ts +72 -0
  140. package/dist/elements.js +207 -0
  141. package/dist/index.d.ts +76 -0
  142. package/dist/index.js +66 -0
  143. package/dist/react.d.ts +4546 -0
  144. package/dist/react.js +6837 -0
  145. package/dist/theme.d.ts +169 -0
  146. package/dist/theme.js +167 -0
  147. package/dist/virtualizer-hooks.d.ts +80 -0
  148. package/dist/virtualizer-hooks.js +253 -0
  149. package/package.json +87 -4
  150. package/scripts/generate-token-index.mjs +43 -0
  151. package/scripts/sync-component-catalog-signatures.mjs +55 -0
  152. package/scripts/validate-component-catalog.mjs +44 -0
  153. package/skills/sharepoint-ui/SKILL.md +12 -0
  154. package/skills/sharepoint-ui/references/choosing-components.md +20 -0
  155. package/skills/sharepoint-ui/references/sharepoint-patterns.md +17 -0
  156. package/tokens.json +3777 -0
@@ -0,0 +1,261 @@
1
+ import { base } from "./shared.js";
2
+ export function createTableColumn(options) {
3
+ return { columnId: options.columnId, compare: options.compare ?? (() => 0), renderHeaderCell: options.renderHeaderCell ?? (() => null), renderCell: options.renderCell ?? (() => null) };
4
+ }
5
+ function defineSlotted({ tag, role, display, css, attributes = [], namedSlots = [], sync }) {
6
+ if (customElements.get(tag))
7
+ return;
8
+ class SpuiTablePart extends HTMLElement {
9
+ static observedAttributes = attributes;
10
+ connectedCallback() {
11
+ if (!this.shadowRoot) {
12
+ const shadow = this.attachShadow({ mode: "open" });
13
+ const style = document.createElement("style");
14
+ const closesHostRule = css.includes("}");
15
+ style.textContent = `${base}:host{box-sizing:border-box;display:${display};min-width:0;color:inherit;font:inherit;${css}${closesHostRule ? "" : "}"}`;
16
+ const slot = document.createElement("slot");
17
+ shadow.append(style, slot, ...namedSlots.map(name => { const named = document.createElement("slot"); named.name = name; return named; }));
18
+ if (!this.hasAttribute("role"))
19
+ this.setAttribute("role", role);
20
+ }
21
+ sync?.(this, this.shadowRoot);
22
+ }
23
+ attributeChangedCallback() { if (this.isConnected)
24
+ sync?.(this, this.shadowRoot); }
25
+ }
26
+ customElements.define(tag, SpuiTablePart);
27
+ }
28
+ export function defineTableCellActions() {
29
+ defineSlotted({ tag: "spui-table-cell-actions", role: "presentation", display: "inline-flex", attributes: ["visible"], css: `gap:var(--spui-table-action-gap,4px);align-items:center;margin-inline-start:auto;opacity:0;transition:opacity var(--spui-motion-duration-fast,120ms) var(--spui-motion-easing-standard,cubic-bezier(.2,0,0,1))}:host([visible]),:host(:focus-within),:host(:hover){opacity:1}:host([hidden]){display:none}@media(prefers-reduced-motion:reduce){:host{transition:none}}@media(forced-colors:active){:host{opacity:1}}` });
30
+ }
31
+ export function defineTableCellLayout() {
32
+ defineSlotted({ tag: "spui-table-cell-layout", role: "presentation", display: "flex", attributes: ["appearance", "truncate"], namedSlots: ["media", "main", "description"], css: `align-items:center;gap:var(--spui-table-cell-gap,8px);min-width:0;min-height:inherit}:host([appearance=primary]){font-weight:600}:host([truncate]){overflow:hidden;text-overflow:ellipsis;white-space:nowrap}::slotted([slot=media]){display:inline-flex;flex:none;align-items:center}::slotted([slot=main]){flex:1;min-width:0}::slotted([slot=description]){color:var(--spui-ui-muted,var(--colorNeutralForeground2,#616161));font-size:.9em}` });
33
+ }
34
+ export function defineTableCell() {
35
+ defineSlotted({ tag: "spui-table-cell", role: "cell", display: "table-cell", css: `padding:var(--spui-table-cell-padding,10px 12px);border-block-end:1px solid var(--spui-ui-stroke,var(--colorNeutralStroke2,#e1dfdd));vertical-align:middle;overflow-wrap:anywhere}:host-context(spui-table[no-native-elements]){display:flex;flex:1 1 0;min-width:0;align-items:center}` });
36
+ }
37
+ export function defineTableHeaderCell() {
38
+ defineSlotted({
39
+ tag: "spui-table-header-cell", role: "columnheader", display: "table-cell", attributes: ["sortable", "sort-direction"], namedSlots: ["button", "aside"],
40
+ css: `position:relative;padding:var(--spui-table-header-padding,10px 12px);border-block-end:1px solid var(--spui-ui-stroke,var(--colorNeutralStroke1,#d1d1d1));color:var(--spui-ui-text,var(--colorNeutralForeground1,#242424));font-weight:600;text-align:start;vertical-align:middle}:host([sortable]){padding:0}:host([aria-sort=ascending]),:host([aria-sort=descending]){color:var(--spui-ui-accent,var(--colorBrandForeground1,#0f6cbd))}.sort-button,::slotted([slot=button]){box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;min-height:40px;padding:10px 12px;border:0;background:transparent;color:inherit;font:inherit;text-align:inherit}.sort-button[role=button],::slotted([slot=button][role=button]){cursor:pointer}.sort-button[role=button]:hover,::slotted([slot=button][role=button]:hover){background:var(--spui-ui-surface-hover,var(--colorNeutralBackground1Hover,#f5f5f5))}.sort-button[role=button]:focus-visible,::slotted([slot=button][role=button]:focus-visible){outline:2px solid var(--spui-focus-ring,var(--spui-ui-accent,#0078d4));outline-offset:-2px}.sort-icon{display:inline-flex;align-items:center;justify-content:center;width:8px;height:8px;flex:none;opacity:.55}.sort-icon::before{content:"";display:block;width:6px;height:6px;border:solid currentColor;border-width:0 1.5px 1.5px 0;transform:rotate(45deg) translate(-1px,-1px)}.sort-icon.custom::before{display:none}.sort-icon slot::slotted(*){display:inline-flex;align-items:center;justify-content:center}:host([sort-direction=ascending]) .sort-icon::before{transform:rotate(225deg) translate(-1px,-1px)}:host(:not([sortable])) .sort-icon{display:none}@media(forced-colors:active){.sort-button:focus-visible,::slotted([slot=button][role=button]:focus-visible){outline-color:Highlight}}:host-context(spui-table[no-native-elements]){display:flex;flex:1 1 0;min-width:0;align-items:center}`,
41
+ sync: (host, root) => {
42
+ const sortable = host.hasAttribute("sortable");
43
+ host.toggleAttribute("data-sortable", sortable);
44
+ const direction = host.getAttribute("sort-direction");
45
+ if (direction === "ascending" || direction === "descending")
46
+ host.setAttribute("aria-sort", direction);
47
+ else
48
+ host.removeAttribute("aria-sort");
49
+ const buttonSlot = root.querySelector('slot[name="button"]');
50
+ let button = buttonSlot.querySelector(".sort-button");
51
+ if (!button) {
52
+ button = document.createElement("div");
53
+ button.className = "sort-button";
54
+ button.part.add("sort-button");
55
+ const content = document.createElement("span");
56
+ content.part.add("content");
57
+ const contentSlot = root.querySelector("slot:not([name])");
58
+ content.append(contentSlot);
59
+ const icon = document.createElement("span");
60
+ icon.className = "sort-icon";
61
+ icon.setAttribute("aria-hidden", "true");
62
+ icon.part.add("sort-icon");
63
+ const iconSlot = document.createElement("slot");
64
+ iconSlot.name = "sort-icon";
65
+ iconSlot.addEventListener("slotchange", () => icon.classList.toggle("custom", iconSlot.assignedNodes({ flatten: true }).length > 0));
66
+ icon.append(iconSlot);
67
+ button.append(content, icon);
68
+ buttonSlot.append(button);
69
+ }
70
+ if (sortable)
71
+ button.setAttribute("role", "button");
72
+ else
73
+ button.removeAttribute("role");
74
+ button.tabIndex = sortable ? 0 : -1;
75
+ const assignedButton = buttonSlot.assignedElements({ flatten: true }).find(element => element instanceof HTMLElement);
76
+ if (assignedButton) {
77
+ if (sortable) {
78
+ if (!assignedButton.hasAttribute("role"))
79
+ assignedButton.setAttribute("role", "button");
80
+ if (!assignedButton.hasAttribute("tabindex"))
81
+ assignedButton.tabIndex = 0;
82
+ }
83
+ }
84
+ if (!host.__spuiHeaderSortListener) {
85
+ host.__spuiHeaderSortListener = true;
86
+ const sort = () => {
87
+ if (!host.hasAttribute("sortable"))
88
+ return;
89
+ const next = host.getAttribute("sort-direction") === "ascending" ? "descending" : "ascending";
90
+ host.dispatchEvent(new CustomEvent("spui-table-sort", { bubbles: true, composed: true, detail: { direction: next, columnId: host.getAttribute("column-id") } }));
91
+ };
92
+ const isTrigger = (event) => event.composedPath().some(node => node === button || buttonSlot.assignedElements({ flatten: true }).includes(node));
93
+ host.addEventListener("click", event => { if (isTrigger(event))
94
+ sort(); });
95
+ host.addEventListener("keydown", event => {
96
+ if ((event.key === "Enter" || event.key === " ") && isTrigger(event)) {
97
+ const path = event.composedPath();
98
+ const target = buttonSlot.assignedElements({ flatten: true }).find(element => path.includes(element)) ?? button;
99
+ if (target.tagName === "BUTTON" || (event.key === "Enter" && target.tagName === "A"))
100
+ return;
101
+ event.preventDefault();
102
+ sort();
103
+ }
104
+ });
105
+ }
106
+ },
107
+ });
108
+ }
109
+ export function defineTableRow() {
110
+ defineSlotted({ tag: "spui-table-row", role: "row", display: "table-row", attributes: ["appearance", "selected"], css: `color:var(--spui-ui-text,var(--colorNeutralForeground1,#242424))}:host(:hover){background:var(--spui-table-row-hover,var(--spui-ui-surface-hover,#f5f5f5))}:host([selected]){background:color-mix(in srgb,var(--spui-ui-accent,#0f6cbd) 10%,var(--spui-ui-surface,#fff))}:host([appearance=brand]){background:color-mix(in srgb,var(--spui-ui-accent,#0f6cbd) 10%,var(--spui-ui-surface,#fff))}:host([appearance=neutral]){background:var(--spui-ui-surface-hover,#f5f5f5)}:host([appearance=none]){background:transparent}:host([selected]) spui-table-cell,:host([selected]) spui-table-header-cell{font-weight:600}@media(forced-colors:active){:host([selected]){outline:1px solid Highlight}}:host-context(spui-table[no-native-elements]){display:flex;align-items:stretch;width:100%;min-width:100%}`, sync: host => { if (host.hasAttribute("selected"))
111
+ host.setAttribute("aria-selected", "true");
112
+ else
113
+ host.removeAttribute("aria-selected"); } });
114
+ }
115
+ export function defineTableHeader() {
116
+ defineSlotted({ tag: "spui-table-header", role: "rowgroup", display: "table-header-group", css: `background:var(--spui-table-header-background,var(--spui-ui-surface-subtle,var(--colorNeutralBackground2,#faf9f8)))}:host-context(spui-table[no-native-elements]){display:flex;flex-direction:column}` });
117
+ }
118
+ export function defineTableBody() {
119
+ defineSlotted({ tag: "spui-table-body", role: "rowgroup", display: "table-row-group", css: `:host-context(spui-table[no-native-elements]){display:flex;flex-direction:column}` });
120
+ }
121
+ export function defineTableSelectionCell() {
122
+ defineSlotted({
123
+ tag: "spui-table-selection-cell", role: "cell", display: "table-cell", attributes: ["type", "checked", "subtle", "invisible", "hidden"], namedSlots: ["checkbox-indicator", "radio-indicator"],
124
+ css: `position:relative;width:44px;min-width:44px;padding:4px 8px;text-align:center;vertical-align:middle}:host([hidden]){display:none}:host([invisible]){visibility:hidden}.select{box-sizing:border-box;display:inline-grid;place-items:center;width:20px;height:20px;margin:0;accent-color:var(--spui-ui-accent,#0f6cbd);cursor:pointer;transition:opacity var(--spui-motion-duration-fast,100ms) var(--spui-motion-easing-standard,cubic-bezier(.2,0,0,1))}:host([data-custom-indicator]) .select{position:absolute;inset:0;margin:auto;opacity:0}:host([subtle]) .select{opacity:0}:host([subtle][checked]) .select,:host([subtle]:focus-within) .select,:host-context(spui-table-row:hover) .select{opacity:1}:host([data-custom-indicator]) .select{opacity:0}::slotted([slot=checkbox-indicator]),::slotted([slot=radio-indicator]){display:inline-flex;align-items:center;justify-content:center;pointer-events:none}::slotted([data-indeterminate=true]){forced-color-adjust:auto}.select:focus-visible{outline:2px solid var(--spui-focus-ring,var(--spui-ui-accent,#0078d4));outline-offset:2px}:host([data-custom-indicator]:focus-within) ::slotted([data-checked]),:host([data-custom-indicator]:focus-within) ::slotted([slot=checkbox-indicator]),:host([data-custom-indicator]:focus-within) ::slotted([slot=radio-indicator]){outline:2px solid var(--spui-focus-ring,var(--spui-ui-accent,#0078d4));outline-offset:2px;border-radius:var(--spui-control-radius,4px)}@media(prefers-reduced-motion:reduce){.select{transition:none}}@media(forced-colors:active){.select{accent-color:Highlight}:host([subtle]) .select{opacity:1}}:host-context(spui-table[no-native-elements]){display:flex;flex:0 0 44px;align-items:center;justify-content:center}`,
125
+ sync: (host, root) => {
126
+ let input = root.querySelector("input");
127
+ if (!input) {
128
+ input = document.createElement("input");
129
+ input.className = "select";
130
+ input.part.add("input");
131
+ root.append(input);
132
+ input.addEventListener("change", () => {
133
+ host.toggleAttribute("checked", input.checked);
134
+ host.dispatchEvent(new CustomEvent("spui-table-selection-change", { bubbles: true, composed: true, detail: { checked: input.checked, type: input.type } }));
135
+ });
136
+ }
137
+ input.type = host.getAttribute("type") === "radio" ? "radio" : "checkbox";
138
+ input.checked = host.hasAttribute("checked");
139
+ input.indeterminate = host.getAttribute("checked") === "mixed";
140
+ const slotName = input.type === "radio" ? "radio-indicator" : "checkbox-indicator";
141
+ const indicatorSlot = root.querySelector(`slot[name="${slotName}"]`);
142
+ const indicators = indicatorSlot.assignedElements({ flatten: true });
143
+ host.toggleAttribute("data-custom-indicator", indicators.length > 0);
144
+ for (const indicator of indicators) {
145
+ indicator.setAttribute("data-checked", String(input.checked));
146
+ indicator.toggleAttribute("data-indeterminate", input.indeterminate);
147
+ }
148
+ input.setAttribute("aria-label", host.getAttribute("aria-label") || (input.type === "radio" ? "Select row" : "Select row"));
149
+ input.disabled = host.hasAttribute("disabled");
150
+ },
151
+ });
152
+ }
153
+ export function defineTableResizeHandle() {
154
+ defineSlotted({
155
+ tag: "spui-table-resize-handle", role: "presentation", display: "inline-flex", attributes: ["value", "min", "max", "step", "label"],
156
+ css: `position:absolute;inset-block:0;inset-inline-end:-4px;z-index:1;display:flex;align-items:center;justify-content:center;width:12px;min-height:40px;touch-action:none;cursor:col-resize}.handle{width:2px;height:60%;min-height:20px;border-radius:2px;background:var(--spui-ui-stroke,var(--colorNeutralStroke1,#d1d1d1));opacity:.55}.handle:focus-visible{outline:2px solid var(--spui-focus-ring,var(--spui-ui-accent,#0078d4));outline-offset:2px}:host(:hover) .handle,:host(:focus-visible) .handle{background:var(--spui-ui-accent,#0f6cbd);opacity:1}`,
157
+ sync: (host, root) => {
158
+ let handle = root.querySelector(".handle");
159
+ if (!handle) {
160
+ handle = document.createElement("div");
161
+ handle.className = "handle";
162
+ handle.tabIndex = 0;
163
+ handle.setAttribute("role", "separator");
164
+ handle.setAttribute("aria-orientation", "vertical");
165
+ handle.part.add("handle");
166
+ root.append(handle);
167
+ }
168
+ const value = Number(host.getAttribute("value"));
169
+ if (Number.isFinite(value))
170
+ handle.setAttribute("aria-valuenow", String(value));
171
+ else
172
+ handle.removeAttribute("aria-valuenow");
173
+ for (const name of ["min", "max"]) {
174
+ const item = host.getAttribute(name);
175
+ if (item)
176
+ handle.setAttribute(`aria-value${name}`, item);
177
+ else
178
+ handle.removeAttribute(`aria-value${name}`);
179
+ }
180
+ handle.setAttribute("aria-label", host.getAttribute("label") || "Resize column");
181
+ const resizeTo = (requested, sourceEvent) => {
182
+ const current = Number(host.getAttribute("value")) || 0;
183
+ const minAttribute = host.getAttribute("min");
184
+ const maxAttribute = host.getAttribute("max");
185
+ const minimum = minAttribute === null ? -Infinity : Number(minAttribute);
186
+ const maximum = maxAttribute === null ? Infinity : Number(maxAttribute);
187
+ const next = Math.max(Number.isFinite(minimum) ? minimum : -Infinity, Math.min(Number.isFinite(maximum) ? maximum : Infinity, requested));
188
+ if (next === current)
189
+ return;
190
+ host.setAttribute("value", String(next));
191
+ host.dispatchEvent(new CustomEvent("spui-table-resize", { bubbles: true, composed: true, detail: { delta: next - current, value: next, sourceEvent } }));
192
+ };
193
+ handle.onkeydown = event => {
194
+ if (!(event instanceof KeyboardEvent) || !["ArrowLeft", "ArrowRight"].includes(event.key))
195
+ return;
196
+ event.preventDefault();
197
+ const step = Math.max(1, Number(host.getAttribute("step")) || 8);
198
+ const rtl = getComputedStyle(host).direction === "rtl";
199
+ const delta = (event.key === "ArrowRight" ? 1 : -1) * (rtl ? -1 : 1) * step;
200
+ const current = Number(host.getAttribute("value")) || 0;
201
+ resizeTo(current + delta, event);
202
+ };
203
+ handle.onpointerdown = event => {
204
+ if (!(event instanceof PointerEvent) || event.button !== 0)
205
+ return;
206
+ event.preventDefault();
207
+ const startX = event.clientX;
208
+ const startWidth = Number(host.getAttribute("value")) || 0;
209
+ const rtl = getComputedStyle(host).direction === "rtl";
210
+ const move = (moveEvent) => { if (moveEvent.pointerId === event.pointerId)
211
+ resizeTo(startWidth + (moveEvent.clientX - startX) * (rtl ? -1 : 1), moveEvent); };
212
+ const finish = (finishEvent) => {
213
+ if (finishEvent.pointerId !== event.pointerId)
214
+ return;
215
+ document.removeEventListener("pointermove", move);
216
+ document.removeEventListener("pointerup", finish);
217
+ document.removeEventListener("pointercancel", finish);
218
+ };
219
+ document.addEventListener("pointermove", move);
220
+ document.addEventListener("pointerup", finish);
221
+ document.addEventListener("pointercancel", finish);
222
+ };
223
+ },
224
+ });
225
+ }
226
+ export function defineTable() {
227
+ defineTableHeader();
228
+ defineTableBody();
229
+ defineTableRow();
230
+ defineTableHeaderCell();
231
+ defineTableCell();
232
+ defineTableCellLayout();
233
+ defineTableCellActions();
234
+ defineTableResizeHandle();
235
+ defineTableSelectionCell();
236
+ if (customElements.get("spui-table"))
237
+ return;
238
+ class SpuiTable extends HTMLElement {
239
+ static observedAttributes = ["size", "appearance", "no-native-elements"];
240
+ connectedCallback() {
241
+ if (this.shadowRoot)
242
+ return;
243
+ const shadow = this.attachShadow({ mode: "open" });
244
+ const style = document.createElement("style");
245
+ style.textContent = `${base}:host{box-sizing:border-box;display:block;width:var(--spui-table-width,100%);max-width:100%;min-width:0;background:var(--spui-table-background,var(--spui-ui-surface,#fff));color:var(--spui-ui-text,var(--colorNeutralForeground1,#242424));font-family:var(--spui-font-family,inherit);font-size:var(--spui-table-font-size,14px);line-height:1.4}.viewport{max-width:100%;overflow-x:auto}.table{display:table;width:100%;min-width:100%;border-collapse:collapse;border-spacing:0}:host([no-native-elements]) .table{display:flex;flex-direction:column}slot{display:contents}:host([size=small]){--spui-table-font-size:12px;--spui-table-cell-padding:6px 8px;--spui-table-header-padding:6px 8px}:host([size=large]){--spui-table-font-size:16px;--spui-table-cell-padding:14px 16px;--spui-table-header-padding:14px 16px}:host([appearance=dividers]){--spui-table-row-hover:transparent}:host([appearance=subtle]){--spui-table-header-background:transparent}@media(forced-colors:active){.table{border-color:CanvasText;color:CanvasText}}`;
246
+ const viewport = document.createElement("div");
247
+ viewport.className = "viewport";
248
+ viewport.part.add("viewport");
249
+ const table = document.createElement("div");
250
+ table.className = "table";
251
+ table.part.add("table");
252
+ table.setAttribute("role", "presentation");
253
+ table.append(document.createElement("slot"));
254
+ viewport.append(table);
255
+ shadow.append(style, viewport);
256
+ if (!this.hasAttribute("role"))
257
+ this.setAttribute("role", "table");
258
+ }
259
+ }
260
+ customElements.define("spui-table", SpuiTable);
261
+ }
@@ -0,0 +1 @@
1
+ export declare function defineTabs(): void;
@@ -0,0 +1,276 @@
1
+ import { base } from "./shared.js";
2
+ const valueOf = (element) => element.getAttribute("value") ?? "";
3
+ export function defineTabs() {
4
+ if (customElements.get("spui-tab"))
5
+ return;
6
+ class SpuiTab extends HTMLElement {
7
+ static observedAttributes = ["disabled", "data-list-disabled", "selected", "tabindex", "aria-controls", "appearance", "size", "vertical", "reserve-selected-tab-space"];
8
+ connectedCallback() { if (!this.shadowRoot)
9
+ this.render(); this.sync(); }
10
+ attributeChangedCallback() { if (this.isConnected)
11
+ this.sync(); }
12
+ get value() { return valueOf(this); }
13
+ get disabled() { return this.hasAttribute("disabled") || this.hasAttribute("data-list-disabled"); }
14
+ getContentText() {
15
+ const named = this.shadowRoot?.querySelector('slot[name="content"]')?.assignedNodes({ flatten: true });
16
+ const fallback = this.shadowRoot?.querySelector("slot:not([name])")?.assignedNodes({ flatten: true });
17
+ const assigned = named?.length ? named : fallback;
18
+ return (assigned?.length ? assigned : [...this.childNodes].filter(node => !(node instanceof Element && node.slot)))
19
+ .map(node => node.textContent ?? "").join("").replace(/\s+/g, " ").trim();
20
+ }
21
+ sync() {
22
+ this.setAttribute("role", "tab");
23
+ this.setAttribute("aria-selected", String(this.getAttribute("selected") === "true"));
24
+ if (this.disabled)
25
+ this.setAttribute("aria-disabled", "true");
26
+ else
27
+ this.removeAttribute("aria-disabled");
28
+ const reserved = this.shadowRoot?.querySelector(".reserved");
29
+ if (reserved)
30
+ reserved.textContent = this.hasAttribute("reserve-selected-tab-space") ? this.getContentText() : "";
31
+ }
32
+ render() {
33
+ const shadow = this.attachShadow({ mode: "open" });
34
+ const style = document.createElement("style");
35
+ style.textContent = `${base}:host{box-sizing:border-box;position:relative;display:inline-flex;align-items:center;justify-content:center;gap:8px;min-width:44px;min-height:40px;padding:8px 12px;border:0;border-radius:var(--spui-control-radius,4px);background:transparent;color:var(--spui-ui-muted,var(--spui-semantic-body-text,#616161));font:inherit;line-height:1.25;text-align:center;white-space:nowrap;cursor:pointer;user-select:none}slot[name=icon]{display:inline-flex;align-items:center;justify-content:center;flex:none}.content{display:grid;min-width:0}.content slot,.reserved{grid-area:1/1}.reserved{display:none;visibility:hidden;white-space:pre;font-weight:600}:host([reserve-selected-tab-space][aria-selected=false]) .reserved{display:block}:host([size=small]){min-height:32px;padding:6px 10px;font-size:12px}:host([size=large]){min-height:48px;padding:10px 16px;font-size:16px}:host([aria-selected=true]){color:var(--spui-ui-text,var(--spui-semantic-body-text,#242424));font-weight:600}:host::after{position:absolute;inset-inline:10px;inset-block-end:0;height:2px;border-radius:2px;background:var(--spui-ui-accent,var(--spui-semantic-accent,#78716c));content:"";opacity:0;transform:scaleX(.65);transition:opacity var(--spui-motion-duration-fast,120ms) var(--spui-motion-easing-standard,cubic-bezier(.2,0,0,1)),transform var(--spui-motion-duration-fast,120ms) var(--spui-motion-easing-standard,cubic-bezier(.2,0,0,1))}:host([aria-selected=true])::after{opacity:1;transform:scaleX(1)}:host(:focus-visible){z-index:1;outline:2px solid var(--spui-focus-ring,var(--spui-semantic-focus-border,#78716c));outline-offset:-2px}:host([disabled]),:host([data-list-disabled]){color:var(--spui-ui-muted,#777);cursor:not-allowed;opacity:.6}:host([appearance=subtle-circular]),:host([appearance=filled-circular]){border-radius:999px}:host([appearance=subtle-circular]){border:1px solid var(--spui-ui-stroke,var(--spui-semantic-body-divider,#d1d1d1))}:host([appearance=subtle-circular][aria-selected=true]){background:color-mix(in srgb,var(--spui-ui-accent,#78716c) 12%,var(--spui-base-surface,var(--spui-ui-surface,#fff)))}:host([appearance=filled-circular][aria-selected=true]){background:color-mix(in srgb,var(--spui-ui-accent,#78716c) 18%,var(--spui-base-surface,var(--spui-ui-surface,#fff)))}:host([vertical]){justify-content:flex-start;width:100%;text-align:start}:host([vertical])::after{inset-block:8px;inset-inline-start:0;inset-inline-end:auto;width:2px;height:auto}:host([appearance=filled-circular])::after{display:none}@media(prefers-reduced-motion:reduce){:host::after{transition:none}}@media(forced-colors:active){:host::after{background:Highlight}:host(:focus-visible){outline-color:Highlight}:host([aria-selected=true]){color:CanvasText}}`;
36
+ style.textContent += `:host([appearance=transparent]:hover:not([disabled])),:host([appearance=transparent]:active:not([disabled])){background:var(--colorTransparentBackgroundHover,var(--spui-ui-surface-hover,#f5f5f5))}:host([appearance=transparent]:active:not([disabled])){background:var(--colorTransparentBackgroundPressed,var(--spui-ui-surface-hover,#e0e0e0))}:host([appearance=subtle]){background:var(--colorSubtleBackground,transparent)}:host([appearance=subtle]:hover:not([disabled])){background:var(--colorSubtleBackgroundHover,var(--spui-ui-surface-hover,#f5f5f5))}:host([appearance=subtle]:active:not([disabled])){background:var(--colorSubtleBackgroundPressed,var(--spui-ui-surface-hover,#e0e0e0))}:host([appearance=subtle-circular]){border-color:var(--colorTransparentStroke,transparent);background:var(--colorSubtleBackground,transparent)}:host([appearance=subtle-circular]:hover:not([disabled])){border-color:var(--colorNeutralStroke1Hover,var(--spui-ui-stroke,#d1d1d1));background:var(--colorSubtleBackgroundHover,var(--spui-ui-surface-hover,#f5f5f5))}:host([appearance=subtle-circular][aria-selected=true]){border-color:var(--colorCompoundBrandStroke,color-mix(in srgb,var(--spui-ui-accent,#78716c) 52%,var(--spui-base-surface,var(--spui-ui-surface,#fff))));background:var(--colorBrandBackground2,color-mix(in srgb,var(--spui-ui-accent,#78716c) 18%,var(--spui-base-surface,var(--spui-ui-surface,#fff))));color:var(--colorBrandForeground2,var(--spui-ui-accent,#78716c))}:host([appearance=filled-circular]){border-color:transparent;background:var(--colorNeutralBackground3,var(--spui-ui-surface-hover,#f0f0f0))}:host([appearance=filled-circular][aria-selected=true]){border-color:transparent;background:var(--colorBrandBackground,var(--spui-ui-accent,#78716c));color:var(--colorNeutralForegroundOnBrand,#fff)}:host([appearance=subtle-circular])::after,:host([appearance=filled-circular])::after{display:none}@media(forced-colors:active){:host([appearance=subtle-circular][aria-selected=true]){border-color:Highlight}:host([appearance=filled-circular][aria-selected=true]){border-color:ButtonText;background:ButtonText;color:ButtonFace}}`;
37
+ const content = document.createElement("span");
38
+ content.className = "content";
39
+ const textSlot = document.createElement("slot");
40
+ textSlot.name = "content";
41
+ const fallbackSlot = document.createElement("slot");
42
+ textSlot.append(fallbackSlot);
43
+ textSlot.addEventListener("slotchange", () => this.sync());
44
+ fallbackSlot.addEventListener("slotchange", () => this.sync());
45
+ content.append(textSlot);
46
+ const reserved = document.createElement("span");
47
+ reserved.className = "reserved";
48
+ reserved.setAttribute("aria-hidden", "true");
49
+ content.append(reserved);
50
+ const iconSlot = document.createElement("slot");
51
+ iconSlot.name = "icon";
52
+ shadow.append(style, iconSlot, content);
53
+ }
54
+ }
55
+ customElements.define("spui-tab", SpuiTab);
56
+ class SpuiTabList extends HTMLElement {
57
+ static observedAttributes = ["selected-value", "default-selected-value", "select-on-focus", "reserve-selected-tab-space", "vertical", "disabled", "appearance", "size", "aria-label"];
58
+ events;
59
+ observer;
60
+ connectedCallback() {
61
+ if (!this.shadowRoot)
62
+ this.render();
63
+ this.observer = new MutationObserver(() => this.sync());
64
+ this.observer.observe(this, { childList: true, subtree: true, attributes: true, attributeFilter: ["disabled", "value"] });
65
+ this.events?.abort();
66
+ this.events = new AbortController();
67
+ this.addEventListener("click", this.onClick, { signal: this.events.signal });
68
+ this.addEventListener("keydown", this.onKeyDown, { signal: this.events.signal });
69
+ this.addEventListener("focusin", this.onFocusIn, { signal: this.events.signal });
70
+ queueMicrotask(() => this.sync());
71
+ }
72
+ disconnectedCallback() { this.events?.abort(); this.observer?.disconnect(); }
73
+ attributeChangedCallback() { if (this.isConnected)
74
+ this.sync(); }
75
+ get selectedValue() {
76
+ return this.getAttribute("selected-value") ?? this.dataset.selectedValue ?? this.getAttribute("default-selected-value") ?? this.tabs().find(tab => !tab.disabled)?.value ?? null;
77
+ }
78
+ tabs() { return [...this.children].filter((child) => child.localName === "spui-tab"); }
79
+ sync() {
80
+ const tabs = this.tabs();
81
+ const selected = this.selectedValue;
82
+ const focusValue = this.dataset.focusValue;
83
+ const focusTab = tabs.find(tab => tab.value === focusValue && !tab.disabled) ?? tabs.find(tab => tab.value === selected && !tab.disabled) ?? tabs.find(tab => !tab.disabled);
84
+ this.dataset.orientation = this.hasAttribute("vertical") ? "vertical" : "horizontal";
85
+ const role = this.shadowRoot?.querySelector("[role=tablist]");
86
+ role?.setAttribute("aria-orientation", this.hasAttribute("vertical") ? "vertical" : "horizontal");
87
+ const label = this.getAttribute("aria-label");
88
+ if (label)
89
+ role?.setAttribute("aria-label", label);
90
+ else
91
+ role?.removeAttribute("aria-label");
92
+ tabs.forEach(tab => {
93
+ if (this.hasAttribute("disabled"))
94
+ tab.setAttribute("data-list-disabled", "");
95
+ else
96
+ tab.removeAttribute("data-list-disabled");
97
+ if (this.hasAttribute("vertical"))
98
+ tab.setAttribute("vertical", "");
99
+ else
100
+ tab.removeAttribute("vertical");
101
+ tab.setAttribute("selected", String(tab.value === selected));
102
+ tab.setAttribute("tabindex", String(tab === focusTab ? 0 : -1));
103
+ if (this.hasAttribute("appearance"))
104
+ tab.setAttribute("appearance", this.getAttribute("appearance"));
105
+ else
106
+ tab.removeAttribute("appearance");
107
+ if (this.hasAttribute("size"))
108
+ tab.setAttribute("size", this.getAttribute("size"));
109
+ else
110
+ tab.removeAttribute("size");
111
+ const reserveSpace = this.getAttribute("reserve-selected-tab-space") !== "false";
112
+ if (reserveSpace)
113
+ tab.setAttribute("reserve-selected-tab-space", "");
114
+ else
115
+ tab.removeAttribute("reserve-selected-tab-space");
116
+ const reserved = tab.shadowRoot?.querySelector(".reserved");
117
+ if (reserved)
118
+ reserved.textContent = reserveSpace ? tab.getContentText() : "";
119
+ });
120
+ if (selected !== null && this.dataset.lastEmittedValue !== selected) {
121
+ this.dataset.lastEmittedValue = selected;
122
+ this.dispatchEvent(new CustomEvent("spui-tab-selection-change", { bubbles: true, composed: true, detail: { value: selected } }));
123
+ }
124
+ }
125
+ onFocusIn = (event) => {
126
+ const tab = event.composedPath().find(target => target instanceof SpuiTab);
127
+ if (!tab || tab.disabled)
128
+ return;
129
+ this.dataset.focusValue = tab.value;
130
+ this.sync();
131
+ if (this.hasAttribute("select-on-focus"))
132
+ this.select(tab, event);
133
+ };
134
+ onClick = (event) => {
135
+ const tab = event.composedPath().find(target => target instanceof SpuiTab);
136
+ if (tab)
137
+ this.select(tab, event);
138
+ };
139
+ onKeyDown = (event) => {
140
+ const tabs = this.tabs().filter(tab => !tab.disabled);
141
+ const current = event.composedPath().find(target => target instanceof SpuiTab);
142
+ if (!current || !tabs.length)
143
+ return;
144
+ const index = tabs.indexOf(current);
145
+ const vertical = this.hasAttribute("vertical");
146
+ const rtl = getComputedStyle(this).direction === "rtl";
147
+ const nextKey = vertical ? "ArrowDown" : rtl ? "ArrowLeft" : "ArrowRight";
148
+ const previousKey = vertical ? "ArrowUp" : rtl ? "ArrowRight" : "ArrowLeft";
149
+ let next;
150
+ if (event.key === nextKey)
151
+ next = tabs[(index + 1) % tabs.length];
152
+ else if (event.key === previousKey)
153
+ next = tabs[(index - 1 + tabs.length) % tabs.length];
154
+ else if (event.key === "Home")
155
+ next = tabs[0];
156
+ else if (event.key === "End")
157
+ next = tabs.at(-1);
158
+ else if (event.key === "Enter" || event.key === " ") {
159
+ event.preventDefault();
160
+ this.select(current, event);
161
+ return;
162
+ }
163
+ if (!next)
164
+ return;
165
+ event.preventDefault();
166
+ this.dataset.focusValue = next.value;
167
+ this.sync();
168
+ next.focus();
169
+ };
170
+ select(tab, sourceEvent) {
171
+ if (tab.disabled || this.hasAttribute("disabled"))
172
+ return;
173
+ if (!this.hasAttribute("selected-value"))
174
+ this.dataset.selectedValue = tab.value;
175
+ this.dataset.focusValue = tab.value;
176
+ this.sync();
177
+ this.dispatchEvent(new CustomEvent("spui-tab-select", { bubbles: true, composed: true, detail: { value: tab.value, valueType: tab.getAttribute("data-spui-tab-value-type") ?? undefined, sourceEvent, currentTarget: tab } }));
178
+ }
179
+ render() {
180
+ const shadow = this.attachShadow({ mode: "open" });
181
+ const style = document.createElement("style");
182
+ style.textContent = `${base}:host{display:block;min-width:0;--spui-tab-gap:4px}.list{display:flex;align-items:center;gap:var(--spui-tab-gap);min-width:0;overflow:auto;scrollbar-width:thin;border-block-end:1px solid var(--spui-tabs-divider,var(--spui-ui-stroke,#d1d1d1))}:host([vertical]) .list{flex-direction:column;align-items:stretch;border-block-end:0;border-inline-end:1px solid var(--spui-tabs-divider,var(--spui-ui-stroke,#d1d1d1))}::slotted(spui-tab){flex:none}@media(forced-colors:active){.list{border-color:CanvasText}}`;
183
+ const list = document.createElement("div");
184
+ list.className = "list";
185
+ list.setAttribute("role", "tablist");
186
+ list.append(document.createElement("slot"));
187
+ shadow.append(style, list);
188
+ }
189
+ }
190
+ customElements.define("spui-tab-list", SpuiTabList);
191
+ class SpuiTabPanel extends HTMLElement {
192
+ static observedAttributes = ["selected", "labelledby"];
193
+ connectedCallback() { if (!this.shadowRoot)
194
+ this.render(); this.sync(); }
195
+ attributeChangedCallback() { if (this.isConnected)
196
+ this.sync(); }
197
+ get value() { return valueOf(this); }
198
+ sync() {
199
+ const selected = this.getAttribute("selected") === "true";
200
+ this.setAttribute("role", "tabpanel");
201
+ this.hidden = !selected;
202
+ this.setAttribute("tabindex", "0");
203
+ const labelledBy = this.getAttribute("labelledby");
204
+ if (labelledBy)
205
+ this.setAttribute("aria-labelledby", labelledBy);
206
+ else
207
+ this.removeAttribute("aria-labelledby");
208
+ }
209
+ render() {
210
+ const shadow = this.attachShadow({ mode: "open" });
211
+ const style = document.createElement("style");
212
+ style.textContent = `${base}:host{display:block;min-width:0;padding-block:16px;color:var(--spui-ui-text,var(--spui-semantic-body-text,#242424))}:host([hidden]){display:none}:host(:focus-visible){outline:2px solid var(--spui-focus-ring,var(--spui-semantic-focus-border,#78716c));outline-offset:2px}@media(forced-colors:active){:host(:focus-visible){outline-color:Highlight}}`;
213
+ shadow.append(style, document.createElement("slot"));
214
+ }
215
+ }
216
+ customElements.define("spui-tab-panel", SpuiTabPanel);
217
+ class SpuiTabs extends HTMLElement {
218
+ static observedAttributes = ["selected-value", "default-selected-value", "disabled"];
219
+ observer;
220
+ events;
221
+ connectedCallback() {
222
+ if (!this.shadowRoot)
223
+ this.render();
224
+ this.observer = new MutationObserver(() => this.sync());
225
+ this.observer.observe(this, { childList: true, subtree: true, attributes: true, attributeFilter: ["value", "selected-value", "default-selected-value", "disabled"] });
226
+ this.events?.abort();
227
+ this.events = new AbortController();
228
+ this.addEventListener("spui-tab-selection-change", this.sync, { signal: this.events.signal });
229
+ queueMicrotask(() => this.sync());
230
+ }
231
+ disconnectedCallback() { this.observer?.disconnect(); this.events?.abort(); }
232
+ attributeChangedCallback() { if (this.isConnected)
233
+ this.sync(); }
234
+ sync = () => {
235
+ const list = [...this.querySelectorAll("spui-tab-list")].find(item => item.closest("spui-tabs") === this);
236
+ if (!list)
237
+ return;
238
+ if (this.hasAttribute("disabled")) {
239
+ if (!list.hasAttribute("disabled"))
240
+ list.setAttribute("disabled", "");
241
+ }
242
+ else if (list.hasAttribute("disabled"))
243
+ list.removeAttribute("disabled");
244
+ if (this.hasAttribute("selected-value")) {
245
+ const selected = this.getAttribute("selected-value");
246
+ list.dataset.rootSelectedValue = selected;
247
+ if (list.getAttribute("selected-value") !== selected)
248
+ list.setAttribute("selected-value", selected);
249
+ }
250
+ else if (list.hasAttribute("data-root-selected-value")) {
251
+ delete list.dataset.rootSelectedValue;
252
+ list.removeAttribute("selected-value");
253
+ }
254
+ else if (this.hasAttribute("default-selected-value") && !list.hasAttribute("default-selected-value"))
255
+ list.setAttribute("default-selected-value", this.getAttribute("default-selected-value"));
256
+ const selected = this.getAttribute("selected-value") ?? list.selectedValue ?? this.getAttribute("default-selected-value");
257
+ const tabs = [...this.querySelectorAll("spui-tab")].filter(tab => tab.closest("spui-tab-list") === list);
258
+ const panels = [...this.querySelectorAll("spui-tab-panel")].filter(panel => panel.closest("spui-tabs") === this);
259
+ for (const tab of tabs) {
260
+ const panel = panels.find(candidate => candidate.value === tab.value);
261
+ if (!tab.id)
262
+ tab.id = `spui-tab-${crypto.randomUUID()}`;
263
+ if (panel) {
264
+ if (!panel.id)
265
+ panel.id = `spui-tabpanel-${crypto.randomUUID()}`;
266
+ tab.setAttribute("aria-controls", panel.id);
267
+ panel.setAttribute("labelledby", tab.id);
268
+ }
269
+ }
270
+ for (const panel of panels)
271
+ panel.setAttribute("selected", String(panel.value === selected));
272
+ };
273
+ render() { const shadow = this.attachShadow({ mode: "open" }); const style = document.createElement("style"); style.textContent = `${base}:host{display:block;min-width:0;color:var(--spui-ui-text,var(--spui-semantic-body-text,#242424))}`; shadow.append(style, document.createElement("slot")); }
274
+ }
275
+ customElements.define("spui-tabs", SpuiTabs);
276
+ }
@@ -0,0 +1,29 @@
1
+ export type TagPickerOpenChangeType = "click" | "keydown" | "input" | "focus" | "focusout" | "blur" | "pointerdown" | "programmatic";
2
+ export type TagPickerOnOpenChangeData = {
3
+ open: boolean;
4
+ reason: string;
5
+ } & ({
6
+ event: MouseEvent;
7
+ type: "click";
8
+ } | {
9
+ event: KeyboardEvent;
10
+ type: "keydown";
11
+ } | {
12
+ event: Event;
13
+ type: Exclude<TagPickerOpenChangeType, "click" | "keydown" | "programmatic">;
14
+ } | {
15
+ event: Event;
16
+ type: "programmatic";
17
+ });
18
+ export type TagPickerOnOptionSelectData = {
19
+ value: string;
20
+ selectedOptions: string[];
21
+ } & ({
22
+ event: MouseEvent;
23
+ type: "click";
24
+ } | {
25
+ event: KeyboardEvent;
26
+ type: "keydown";
27
+ });
28
+ /** Searchable multi-select picker with native form association and light-DOM options. */
29
+ export declare function defineTagPicker(): void;