@spfx-fast/sharepoint-ui 0.4.1 → 0.4.3

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 (57) hide show
  1. package/AGENTS.md +23 -3
  2. package/QUICKSTART.md +133 -0
  3. package/README.md +21 -878
  4. package/components.json +178 -33
  5. package/dist/appearance.js +4 -4
  6. package/dist/elements/alert.js +26 -2
  7. package/dist/elements/avatar.js +1 -1
  8. package/dist/elements/button.js +1 -1
  9. package/dist/elements/carousel.js +5 -2
  10. package/dist/elements/color-picker.js +1 -1
  11. package/dist/elements/combobox.js +5 -5
  12. package/dist/elements/data-grid-model.d.ts +3 -0
  13. package/dist/elements/data-grid.d.ts +2 -1
  14. package/dist/elements/data-grid.js +19 -10
  15. package/dist/elements/date-picker.js +1 -1
  16. package/dist/elements/dialog.js +1 -1
  17. package/dist/elements/file-upload.js +1 -1
  18. package/dist/elements/floating-position.d.ts +10 -0
  19. package/dist/elements/floating-position.js +53 -0
  20. package/dist/elements/info.js +1 -1
  21. package/dist/elements/input.js +2 -2
  22. package/dist/elements/link.js +1 -1
  23. package/dist/elements/message-bar.js +33 -5
  24. package/dist/elements/nav.js +4 -1
  25. package/dist/elements/overflow.d.ts +1 -0
  26. package/dist/elements/overflow.js +2 -2
  27. package/dist/elements/popover.js +1 -1
  28. package/dist/elements/rating.js +3 -3
  29. package/dist/elements/rich-text-editor.d.ts +15 -1
  30. package/dist/elements/rich-text-editor.js +938 -79
  31. package/dist/elements/search.js +3 -10
  32. package/dist/elements/select.js +2 -2
  33. package/dist/elements/shared.d.ts +4 -1
  34. package/dist/elements/shared.js +14 -1
  35. package/dist/elements/sharepoint-field-basic-controls.d.ts +1 -0
  36. package/dist/elements/sharepoint-field-basic-controls.js +14 -0
  37. package/dist/elements/sharepoint-fields.d.ts +29 -0
  38. package/dist/elements/sharepoint-fields.js +460 -0
  39. package/dist/elements/spin-button.js +2 -2
  40. package/dist/elements/tag-picker.js +29 -11
  41. package/dist/elements/textarea.js +2 -2
  42. package/dist/elements/toaster.js +2 -1
  43. package/dist/elements/tooltip.js +18 -1
  44. package/dist/elements/tree.js +1 -1
  45. package/dist/elements.d.ts +3 -2
  46. package/dist/elements.js +4 -1
  47. package/dist/index.d.ts +6 -4
  48. package/dist/index.js +4 -2
  49. package/dist/react.d.ts +188 -19
  50. package/dist/react.js +321 -61
  51. package/dist/sharepoint-fields.d.ts +135 -0
  52. package/dist/sharepoint-fields.js +402 -0
  53. package/dist/theme.d.ts +9 -1
  54. package/dist/theme.js +78 -1
  55. package/package.json +11 -2
  56. package/skills/sharepoint-ui/SKILL.md +1 -1
  57. package/skills/sharepoint-ui/references/sharepoint-patterns.md +1 -1
@@ -150,10 +150,9 @@ export function defineSearchBox() {
150
150
  if (value !== null)
151
151
  input.setAttribute(name, value);
152
152
  }
153
- const clear = document.createElement("span");
153
+ const clear = document.createElement("button");
154
+ clear.type = "button";
154
155
  clear.className = "dismiss";
155
- clear.setAttribute("role", "button");
156
- clear.tabIndex = -1;
157
156
  clear.part.add("clear-button");
158
157
  clear.setAttribute("aria-label", this.getAttribute("clear-label") || "Clear search");
159
158
  clear.hidden = !this.#value || input.disabled || input.readOnly;
@@ -172,12 +171,6 @@ export function defineSearchBox() {
172
171
  this.dispatchEvent(new CustomEvent("spui-input", { bubbles: true, composed: true, detail: { value: "", reason: "clear", sourceEvent: event } }));
173
172
  this.dispatchEvent(new CustomEvent("spui-clear", { bubbles: true, composed: true }));
174
173
  });
175
- clear.addEventListener("keydown", event => {
176
- if (event.key !== "Enter" && event.key !== " ")
177
- return;
178
- event.preventDefault();
179
- clear.click();
180
- });
181
174
  input.addEventListener("input", event => {
182
175
  this.setValue(input.value, true);
183
176
  this.dispatchEvent(new CustomEvent("spui-input", { bubbles: true, composed: true, detail: { value: input.value, reason: "input", sourceEvent: event } }));
@@ -198,7 +191,7 @@ export function defineSearchBox() {
198
191
  after.append(afterSlot, clear);
199
192
  control.append(contentBefore, input, after);
200
193
  const style = document.createElement("style");
201
- style.textContent = `${base}:host{display:grid;gap:var(--spui-control-gap,6px);min-width:0}label:empty{display:none}label{font-size:14px;font-weight:600;color:var(--spui-ui-muted,var(--spui-semantic-body-text,inherit))}.control{box-sizing:border-box;display:flex;align-items:center;gap:8px;min-width:0;min-height:var(--spui-control-height,36px);padding-inline:var(--spui-control-padding-inline,12px);border:1px solid var(--spui-control-border,var(--spui-ui-stroke,var(--spui-semantic-input-border,var(--colorNeutralStroke1,#8a8886))));border-radius:var(--spui-control-radius,4px);background:var(--spui-control-background,var(--spui-ui-surface,var(--spui-semantic-input-background,var(--colorNeutralBackground1,#fff))));box-shadow:var(--spui-control-shadow,none);color:var(--spui-ui-text,var(--spui-semantic-body-text,inherit))}.control[data-size=small]{min-height:28px;font-size:13px}.control[data-size=large]{min-height:44px;font-size:16px}.control[data-appearance=underline]{border:0;border-bottom:1px solid var(--spui-control-border,var(--spui-ui-stroke,#8a8886));border-radius:0}.control[data-appearance=filled-darker],.control[data-appearance=filled-darker-shadow]{border-color:transparent;background:var(--spui-input-filled-darker,var(--spui-ui-surface-hover,var(--spui-semantic-input-background,#f3f2f1)))}.control[data-appearance=filled-lighter],.control[data-appearance=filled-lighter-shadow]{border-color:transparent;background:var(--spui-input-filled-lighter,var(--spui-base-surface,var(--spui-ui-canvas,#faf9f8)))}.control[data-appearance=filled-darker-shadow],.control[data-appearance=filled-lighter-shadow]{box-shadow:var(--spui-input-filled-shadow,var(--spui-control-shadow,var(--shadow2,0 1px 2px rgb(0 0 0 / 20%))))}.control:focus-within{border-color:var(--spui-focus-ring,var(--spui-semantic-focus-border,var(--colorStrokeFocus2,#005a9e)))}.control:focus-within[data-appearance=underline]{border-bottom-width:2px}slot[name=content-before],.content-after{display:inline-flex;align-items:center;flex:none;gap:4px}slot[name=content-after]{display:inline-flex;align-items:center;gap:4px}input{box-sizing:border-box;flex:1;min-width:0;width:100%;padding:0;border:0;outline:0;background:transparent;color:inherit;font:inherit}input::placeholder{color:var(--spui-ui-muted,var(--spui-semantic-body-subtext,#616161));opacity:1}input::-webkit-search-cancel-.dismiss{display:none}svg[part=search-icon]{width:18px;height:18px;flex:none;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}.dismiss{display:grid;place-items:center;flex:none;width:28px;height:28px;padding:5px;border:0;border-radius:var(--spui-control-radius,4px);background:transparent;color:inherit;cursor:pointer}.dismiss[hidden]{display:none}.dismiss:hover{background:var(--spui-ui-surface-hover,var(--spui-ui-canvas,#f5f5f5))}.dismiss:focus-visible{outline:2px solid var(--spui-focus-ring,var(--spui-ui-accent,#0078d4));outline-offset:1px}.dismiss svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round}@media(forced-colors:active){.control{border-color:FieldText}.control:focus-within,.dismiss:focus-visible{outline-color:Highlight}}`;
194
+ style.textContent = `${base}:host{display:grid;gap:var(--spui-control-gap,6px);min-width:0}label:empty{display:none}label{font-size:14px;font-weight:600;color:var(--spui-ui-muted,var(--spui-semantic-body-text,inherit))}.control{box-sizing:border-box;display:flex;align-items:center;gap:8px;min-width:0;min-height:var(--spui-control-height,36px);padding-inline:var(--spui-control-padding-inline,12px);border:1px solid var(--spui-control-border,var(--spui-ui-stroke,var(--spui-semantic-input-border,var(--colorNeutralStroke1,#8a8886))));border-radius:var(--spui-control-radius,4px);background:var(--spui-control-background,var(--spui-ui-surface,var(--spui-semantic-input-background,var(--colorNeutralBackground1,#fff))));box-shadow:var(--spui-control-shadow,none);color:var(--spui-ui-text,var(--spui-semantic-body-text,inherit))}.control[data-size=small]{min-height:28px;font-size:13px}.control[data-size=large]{min-height:44px;font-size:16px}.control[data-appearance=underline]{border:0;border-bottom:1px solid var(--spui-control-border,var(--spui-ui-stroke,#8a8886));border-radius:0}.control[data-appearance=filled-darker],.control[data-appearance=filled-darker-shadow]{border-color:transparent;background:var(--spui-input-filled-darker,var(--spui-ui-surface-hover,var(--spui-semantic-input-background,#f3f2f1)))}.control[data-appearance=filled-lighter],.control[data-appearance=filled-lighter-shadow]{border-color:transparent;background:var(--spui-input-filled-lighter,var(--spui-base-surface,var(--spui-ui-canvas,#faf9f8)))}.control[data-appearance=filled-darker-shadow],.control[data-appearance=filled-lighter-shadow]{box-shadow:var(--spui-input-filled-shadow,var(--spui-control-shadow,var(--shadow2,0 1px 2px rgb(0 0 0 / 20%))))}.control:focus-within{border-color:var(--spui-focus-ring,var(--spui-semantic-focus-border,var(--colorStrokeFocus2,#005a9e)))}.control:has(input[aria-invalid]:not([aria-invalid=false])){border-color:var(--spui-input-invalid-border,var(--spui-ui-danger,var(--colorPaletteRedBorder2,#c50f1f)))}.control:focus-within[data-appearance=underline]{border-bottom-width:2px}slot[name=content-before],.content-after{display:inline-flex;align-items:center;flex:none;gap:4px}slot[name=content-after]{display:inline-flex;align-items:center;gap:4px}input{box-sizing:border-box;flex:1;min-width:0;width:100%;padding:0;border:0;outline:0;background:transparent;color:inherit;font:inherit}input:focus-visible{outline:0}input::placeholder{color:var(--spui-ui-muted,var(--spui-semantic-body-subtext,#616161));opacity:1}input[type=search]::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none;display:none}svg[part=search-icon]{width:18px;height:18px;flex:none;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}.dismiss{appearance:none;display:grid;place-items:center;flex:none;width:28px;height:28px;padding:5px;border:0;border-radius:var(--spui-control-radius,4px);background:transparent;color:inherit;font:inherit;cursor:pointer}.dismiss[hidden]{display:none}.dismiss:hover{background:var(--spui-ui-surface-hover,var(--spui-ui-canvas,#f5f5f5))}.dismiss:focus-visible{outline:2px solid var(--spui-focus-ring,var(--spui-ui-accent,#0078d4));outline-offset:1px}.dismiss svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round}@media(forced-colors:active){.control{border-color:FieldText}.control:focus-within{border-color:Highlight}.control:has(input[aria-invalid]:not([aria-invalid=false])){border-color:CanvasText}.control:has(input[aria-invalid]:not([aria-invalid=false])):focus-within{border-color:Highlight}.dismiss:focus-visible{outline-color:Highlight}}`;
202
195
  shadow.replaceChildren(style, label, control);
203
196
  this.syncFormValue();
204
197
  }
@@ -281,8 +281,8 @@ export function defineSelect() {
281
281
  icon.append(fallbackIcon);
282
282
  control.append(selectSlot, icon);
283
283
  const style = document.createElement("style");
284
- style.textContent = `${base}:host{display:grid;gap:var(--spui-control-gap,6px)}label{font-size:14px;font-weight:600;color:var(--spui-ui-muted,var(--spui-semantic-body-text,inherit))}.control{position:relative;min-width:0}select{box-sizing:border-box;width:100%;min-height:var(--spui-control-height,36px);padding-inline:var(--spui-control-padding-inline,12px);padding-inline-end:36px;appearance:none;border:1px solid var(--spui-control-border,var(--spui-ui-stroke,var(--spui-semantic-input-border,var(--colorNeutralStroke1,#8a8886))));border-radius:var(--spui-control-radius,4px);background:var(--spui-control-background,var(--spui-ui-surface,var(--spui-semantic-input-background,var(--colorNeutralBackground1,#fff))));box-shadow:var(--spui-control-shadow,none);color:var(--spui-ui-text,var(--spui-semantic-body-text,inherit));font:inherit}:host([size=small]) select{min-height:24px;font-size:var(--spui-text-font-size-small,12px)}:host([size=large]) select{min-height:40px;font-size:var(--spui-text-font-size-large,18px)}:host([appearance=underline]) select{border-width:0 0 1px;border-radius:0}:host([appearance=filled-darker]) select{background:var(--colorNeutralBackground3,var(--spui-ui-surface-sunken,#f0f0f0));border-color:transparent}:host([appearance=filled-lighter]) select{background:var(--colorNeutralBackground2,var(--spui-ui-surface-hover,#f5f5f5));border-color:transparent}slot[name=icon]{position:absolute;inset-inline-end:8px;top:50%;display:grid;place-items:center;width:20px;height:20px;transform:translateY(-50%);color:var(--spui-ui-muted,var(--spui-ui-text,#424242));pointer-events:none}slot[name=icon]::slotted(*){width:20px;height:20px}select:focus-visible{outline:2px solid var(--spui-focus-ring,var(--colorStrokeFocus2,#005a9e));outline-offset:2px}select:disabled{opacity:.55}@media(forced-colors:active){select{border-color:FieldText}slot[name=icon]{color:ButtonText}select:focus-visible{outline-color:Highlight}}`;
285
- style.textContent += `slot[name=select]{display:block}slot[name=select]::slotted(select){box-sizing:border-box;width:100%;min-height:var(--spui-control-height,36px);padding-inline:var(--spui-control-padding-inline,12px);padding-inline-end:36px;appearance:none;border:1px solid var(--spui-control-border,var(--spui-ui-stroke,var(--colorNeutralStroke1,#8a8886)));border-radius:var(--spui-control-radius,4px);background:var(--spui-control-background,var(--spui-ui-surface,#fff));color:var(--spui-ui-text,inherit);font:inherit}:host([size=small]) slot[name=select]::slotted(select){min-height:24px;font-size:var(--spui-text-font-size-small,12px)}:host([size=large]) slot[name=select]::slotted(select){min-height:40px;font-size:var(--spui-text-font-size-large,18px)}:host([appearance=underline]) slot[name=select]::slotted(select){border-width:0 0 1px;border-radius:0}:host([appearance=filled-darker]) slot[name=select]::slotted(select){background:var(--colorNeutralBackground3,var(--spui-ui-surface-sunken,#f0f0f0));border-color:transparent}:host([appearance=filled-lighter]) slot[name=select]::slotted(select){background:var(--colorNeutralBackground2,var(--spui-ui-surface-hover,#f5f5f5));border-color:transparent}slot[name=select]::slotted(select:focus-visible){outline:2px solid var(--spui-focus-ring,var(--colorStrokeFocus2,#005a9e));outline-offset:2px}slot[name=select]::slotted(select:disabled){opacity:.55}@media(forced-colors:active){slot[name=select]::slotted(select){border-color:FieldText}slot[name=select]::slotted(select:focus-visible){outline-color:Highlight}}`;
284
+ style.textContent = `${base}:host{display:grid;gap:var(--spui-control-gap,6px)}label{font-size:14px;font-weight:600;color:var(--spui-ui-muted,var(--spui-semantic-body-text,inherit))}.control{position:relative;min-width:0}select{box-sizing:border-box;width:100%;min-height:var(--spui-control-height,36px);padding-inline:var(--spui-control-padding-inline,12px);padding-inline-end:36px;appearance:none;border:1px solid var(--spui-control-border,var(--spui-ui-stroke,var(--spui-semantic-input-border,var(--colorNeutralStroke1,#8a8886))));border-radius:var(--spui-control-radius,4px);background:var(--spui-control-background,var(--spui-ui-surface,var(--spui-semantic-input-background,var(--colorNeutralBackground1,#fff))));box-shadow:var(--spui-control-shadow,none);color:var(--spui-ui-text,var(--spui-semantic-body-text,inherit));font:inherit}:host([size=small]) select{min-height:24px;font-size:var(--spui-text-font-size-small,12px)}:host([size=large]) select{min-height:40px;font-size:var(--spui-text-font-size-large,18px)}:host([appearance=underline]) select{border-width:0 0 1px;border-radius:0}:host([appearance=filled-darker]) select{background:var(--colorNeutralBackground3,var(--spui-ui-surface-sunken,#f0f0f0));border-color:transparent}:host([appearance=filled-lighter]) select{background:var(--colorNeutralBackground2,var(--spui-ui-surface-hover,#f5f5f5));border-color:transparent}slot[name=icon]{position:absolute;inset-inline-end:8px;top:50%;display:grid;place-items:center;width:20px;height:20px;transform:translateY(-50%);color:var(--spui-ui-muted,var(--spui-ui-text,#424242));pointer-events:none}slot[name=icon]::slotted(*){width:20px;height:20px}select:focus-visible{border-width:2px;border-color:var(--spui-focus-ring,var(--colorStrokeFocus2,#005a9e));outline:0}:host([appearance=underline]) select:focus-visible{border-width:0 0 2px}select[aria-invalid]:not([aria-invalid=false]){border-color:var(--spui-input-invalid-border,var(--spui-ui-danger,var(--colorPaletteRedBorder2,#c50f1f)))}select:disabled{opacity:.55}@media(forced-colors:active){select{border-color:FieldText}slot[name=icon]{color:ButtonText}select:focus-visible{border-color:Highlight;border-width:2px;outline:0}select[aria-invalid]:not([aria-invalid=false]){border-color:CanvasText}select[aria-invalid]:not([aria-invalid=false]):focus-visible{border-color:Highlight}:host([appearance=underline]) select:focus-visible{border-width:0 0 2px}}`;
285
+ style.textContent += `slot[name=select]{display:block}slot[name=select]::slotted(select){box-sizing:border-box;width:100%;min-height:var(--spui-control-height,36px);padding-inline:var(--spui-control-padding-inline,12px);padding-inline-end:36px;appearance:none;border:1px solid var(--spui-control-border,var(--spui-ui-stroke,var(--colorNeutralStroke1,#8a8886)));border-radius:var(--spui-control-radius,4px);background:var(--spui-control-background,var(--spui-ui-surface,#fff));color:var(--spui-ui-text,inherit);font:inherit}:host([size=small]) slot[name=select]::slotted(select){min-height:24px;font-size:var(--spui-text-font-size-small,12px)}:host([size=large]) slot[name=select]::slotted(select){min-height:40px;font-size:var(--spui-text-font-size-large,18px)}:host([appearance=underline]) slot[name=select]::slotted(select){border-width:0 0 1px;border-radius:0}:host([appearance=filled-darker]) slot[name=select]::slotted(select){background:var(--colorNeutralBackground3,var(--spui-ui-surface-sunken,#f0f0f0));border-color:transparent}:host([appearance=filled-lighter]) slot[name=select]::slotted(select){background:var(--colorNeutralBackground2,var(--spui-ui-surface-hover,#f5f5f5));border-color:transparent}slot[name=select]::slotted(select:focus-visible){border-width:2px!important;border-color:var(--spui-focus-ring,var(--colorStrokeFocus2,#005a9e))!important;outline:0!important;outline-offset:0;box-shadow:none!important}:host([appearance=underline]) slot[name=select]::slotted(select:focus-visible){border-width:0 0 2px!important}slot[name=select]::slotted(select[aria-invalid]:not([aria-invalid=false])){border-color:var(--spui-input-invalid-border,var(--spui-ui-danger,var(--colorPaletteRedBorder2,#c50f1f)))!important}slot[name=select]::slotted(select:disabled){opacity:.55}@media(forced-colors:active){slot[name=select]::slotted(select){border-color:FieldText}slot[name=select]::slotted(select:focus-visible){border-color:Highlight!important;border-width:2px!important;outline:0!important}slot[name=select]::slotted(select[aria-invalid]:not([aria-invalid=false])){border-color:CanvasText!important}slot[name=select]::slotted(select[aria-invalid]:not([aria-invalid=false]):focus-visible){border-color:Highlight!important}:host([appearance=underline]) slot[name=select]::slotted(select:focus-visible){border-width:0 0 2px!important}}`;
286
286
  shadow.replaceChildren(style, label, control);
287
287
  this.syncOptions();
288
288
  }
@@ -1,4 +1,7 @@
1
- export declare const base = ":host{font:inherit;font-family:var(--spui-font-family,inherit);color:var(--spui-ui-text,var(--spui-semantic-body-text,var(--colorNeutralForeground1,#323130)));color-scheme:var(--spui-color-scheme,light dark)}button,input,textarea{font:inherit;font-family:var(--spui-font-family,inherit)}button:focus-visible,input:focus-visible,textarea:focus-visible{outline:2px solid var(--spui-focus-ring,var(--spui-semantic-focus-border,var(--colorStrokeFocus2,#005a9e)));outline-offset:2px}";
1
+ /** Resolve the normal text direction for a BCP 47 language tag; callers can still honor an explicit dir. */
2
+ export declare function getDirectionFromLanguage(language?: string | null): "ltr" | "rtl" | undefined;
3
+ export declare const directionByLanguage: string;
4
+ export declare const base: string;
2
5
  export type NativeFormControl = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
3
6
  export type NativeFormControlDefaults = {
4
7
  control: NativeFormControl;
@@ -1,4 +1,17 @@
1
- export const base = `:host{font:inherit;font-family:var(--spui-font-family,inherit);color:var(--spui-ui-text,var(--spui-semantic-body-text,var(--colorNeutralForeground1,#323130)));color-scheme:var(--spui-color-scheme,light dark)}button,input,textarea{font:inherit;font-family:var(--spui-font-family,inherit)}button:focus-visible,input:focus-visible,textarea:focus-visible{outline:2px solid var(--spui-focus-ring,var(--spui-semantic-focus-border,var(--colorStrokeFocus2,#005a9e)));outline-offset:2px}`;
1
+ const rtlLanguages = new Set(["ar", "arc", "bal", "bcc", "bgn", "bqi", "ckb", "dv", "fa", "glk", "he", "khw", "ks", "lrc", "mzn", "nqo", "ps", "rhg", "sd", "ug", "ur", "yi"]);
2
+ const rtlScripts = new Set(["arab", "armi", "avst", "hebr", "mand", "mani", "mend", "nkoo", "phli", "prti", "rohg", "samr", "syrc", "thaa"]);
3
+ /** Resolve the normal text direction for a BCP 47 language tag; callers can still honor an explicit dir. */
4
+ export function getDirectionFromLanguage(language) {
5
+ const tags = language?.trim().replaceAll("_", "-").split("-").filter(Boolean);
6
+ if (!tags?.length)
7
+ return undefined;
8
+ const script = tags.slice(1).find(tag => /^[a-z]{4}$/i.test(tag));
9
+ return script ? rtlScripts.has(script.toLowerCase()) ? "rtl" : "ltr" : rtlLanguages.has(tags[0].toLowerCase()) ? "rtl" : "ltr";
10
+ }
11
+ const rtlLanguageTags = ["ar", "arc", "bal", "bcc", "bgn", "bqi", "ckb", "dv", "fa", "glk", "he", "khw", "ks", "lrc", "mzn", "nqo", "ps", "rhg", "sd", "ug", "ur", "yi", "az-Arab", "ku-Arab", "ms-Arab", "pa-Arab", "uz-Arab", "kk-Arab", "ky-Arab", "tt-Arab"];
12
+ const ltrScriptTags = ["ar-Latn", "fa-Latn", "he-Latn", "ks-Deva", "sd-Deva", "ur-Latn", "ug-Latn", "yi-Latn"];
13
+ export const directionByLanguage = `${rtlLanguageTags.map(tag => `:host(:lang(${tag}):not([dir]))`).join(",")}{direction:rtl}${ltrScriptTags.map(tag => `:host(:lang(${tag}):not([dir]))`).join(",")}{direction:ltr}`;
14
+ export const base = `:host{font:inherit;font-family:var(--spui-font-family,inherit);color:var(--spui-ui-text,var(--spui-semantic-body-text,var(--colorNeutralForeground1,#323130)));color-scheme:var(--spui-color-scheme,light dark)}${directionByLanguage}button,input,textarea{font:inherit;font-family:var(--spui-font-family,inherit)}button:focus-visible,input:focus-visible,textarea:focus-visible{outline:2px solid var(--spui-focus-ring,var(--spui-semantic-focus-border,var(--colorStrokeFocus2,#005a9e)));outline-offset:2px}`;
2
15
  const accessibleNameSyncStates = new WeakMap();
3
16
  function stopHostAccessibleNameObserver(host, control) {
4
17
  const hostStates = accessibleNameSyncStates.get(host);
@@ -0,0 +1 @@
1
+ export declare function defineSharePointFieldBasicControls(): void;
@@ -0,0 +1,14 @@
1
+ import { defineButton } from "./button.js";
2
+ import { defineInput } from "./input.js";
3
+ import { defineTextArea } from "./textarea.js";
4
+ import { defineSelect } from "./select.js";
5
+ import { defineRadioGroup } from "./radio-group.js";
6
+ import { defineCheckbox } from "./toggle.js";
7
+ export function defineSharePointFieldBasicControls() {
8
+ defineButton();
9
+ defineInput();
10
+ defineTextArea();
11
+ defineSelect();
12
+ defineRadioGroup();
13
+ defineCheckbox();
14
+ }
@@ -0,0 +1,29 @@
1
+ import { type SharePointFieldMetadata, type SharePointFieldValue } from "../sharepoint-fields.js";
2
+ export type SharePointFieldOption = {
3
+ value: string | number;
4
+ label: string;
5
+ disabled?: boolean;
6
+ };
7
+ export type SharePointFieldValueChangeDetail = {
8
+ field: SharePointFieldMetadata;
9
+ value: SharePointFieldValue;
10
+ };
11
+ export type SharePointFieldElement = HTMLElement & {
12
+ field: SharePointFieldMetadata | undefined;
13
+ value: SharePointFieldValue | undefined;
14
+ defaultValue: SharePointFieldValue | undefined;
15
+ options: readonly SharePointFieldOption[];
16
+ disabled: boolean;
17
+ onValueChange?: (value: SharePointFieldValue, field: SharePointFieldMetadata) => void;
18
+ renderUnsupported?: (field: SharePointFieldMetadata, reason: string) => Node | null;
19
+ };
20
+ declare global {
21
+ interface HTMLElementTagNameMap {
22
+ "spui-sharepoint-field": SharePointFieldElement;
23
+ }
24
+ interface HTMLElementEventMap {
25
+ "spui-field-value-change": CustomEvent<SharePointFieldValueChangeDetail>;
26
+ }
27
+ }
28
+ /** Render SharePoint field metadata as a framework-free, theme-aware form control. */
29
+ export declare function defineSharePointField(): void;
@@ -0,0 +1,460 @@
1
+ import { getSharePointFieldAdapter, } from "../sharepoint-fields.js";
2
+ import { createFormField } from "./field-composition.js";
3
+ import { defineSharePointFieldBasicControls } from "./sharepoint-field-basic-controls.js";
4
+ const textValue = (value) => {
5
+ if (value == null)
6
+ return "";
7
+ if (value instanceof Date) {
8
+ const pad = (part) => String(part).padStart(2, "0");
9
+ return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}${value.getHours() || value.getMinutes() ? `T${pad(value.getHours())}:${pad(value.getMinutes())}` : ""}`;
10
+ }
11
+ if (typeof value === "object" && "url" in value)
12
+ return value.url;
13
+ return String(value);
14
+ };
15
+ function makeOption(tag, option) {
16
+ const element = document.createElement(tag);
17
+ element.setAttribute("value", String(option.value));
18
+ element.setAttribute(tag === "option" ? "value" : "label", tag === "option" ? String(option.value) : option.label);
19
+ if (tag === "option")
20
+ element.textContent = option.label;
21
+ else
22
+ element.textContent = option.label;
23
+ if (option.disabled)
24
+ element.setAttribute("disabled", "");
25
+ return element;
26
+ }
27
+ async function loadFieldControl(adapter) {
28
+ switch (adapter.control) {
29
+ case "input":
30
+ case "read-only":
31
+ case "textarea":
32
+ case "radio-group":
33
+ case "checkbox":
34
+ case "multi-choice":
35
+ case "select":
36
+ if (adapter.control === "select" && adapter.allowFillIn && !customElements.get("spui-combobox")) {
37
+ await import(/* webpackChunkName: "spui-sharepoint-field-freeform-choice" */ "./combobox.js").then(module => module.defineCombobox());
38
+ }
39
+ return;
40
+ case "rich-text-editor":
41
+ if (!customElements.get("spui-rich-text-editor"))
42
+ (await import(/* webpackChunkName: "spui-sharepoint-field-rich-text" */ "./rich-text-editor.js")).defineRichTextEditor();
43
+ break;
44
+ case "date-picker":
45
+ if (!customElements.get("spui-date-picker"))
46
+ (await import(/* webpackChunkName: "spui-sharepoint-field-date" */ "./date-picker.js")).defineDatePicker();
47
+ break;
48
+ case "combobox":
49
+ if (!customElements.get("spui-combobox"))
50
+ (await import(/* webpackChunkName: "spui-sharepoint-field-lookup" */ "./combobox.js")).defineCombobox();
51
+ break;
52
+ }
53
+ }
54
+ /** Render SharePoint field metadata as a framework-free, theme-aware form control. */
55
+ export function defineSharePointField() {
56
+ if (customElements.get("spui-sharepoint-field"))
57
+ return;
58
+ defineSharePointFieldBasicControls();
59
+ class SpuiSharePointField extends HTMLElement {
60
+ static observedAttributes = ["field", "value", "disabled", "hint", "validation-message", "empty-option-label", "add-choice-label"];
61
+ fieldValue;
62
+ currentValue;
63
+ initialValue;
64
+ currentOptions = [];
65
+ hasProvidedOptions = false;
66
+ controlled = false;
67
+ renderVersion = 0;
68
+ valueChangeCallback;
69
+ unsupportedRenderer;
70
+ get field() { return this.fieldValue; }
71
+ set field(value) { this.fieldValue = value; if (this.isConnected)
72
+ this.render(); }
73
+ get value() { return this.currentValue; }
74
+ set value(value) {
75
+ this.controlled = true;
76
+ this.currentValue = value;
77
+ this.syncControlValue();
78
+ }
79
+ get defaultValue() { return this.initialValue; }
80
+ set defaultValue(value) {
81
+ this.initialValue = value;
82
+ if (!this.controlled)
83
+ this.currentValue = value;
84
+ if (this.isConnected)
85
+ this.render();
86
+ }
87
+ get options() { return this.currentOptions; }
88
+ set options(value) { this.currentOptions = [...(value ?? [])]; this.hasProvidedOptions = true; if (this.isConnected)
89
+ this.render(); }
90
+ get disabled() { return this.hasAttribute("disabled"); }
91
+ set disabled(value) { this.toggleAttribute("disabled", Boolean(value)); }
92
+ get onValueChange() { return this.valueChangeCallback; }
93
+ set onValueChange(value) { this.valueChangeCallback = value; }
94
+ get renderUnsupported() { return this.unsupportedRenderer; }
95
+ set renderUnsupported(value) { this.unsupportedRenderer = value; }
96
+ attributeChangedCallback(name, _oldValue, value) {
97
+ if (!this.isConnected)
98
+ return;
99
+ if (name === "field") {
100
+ try {
101
+ this.fieldValue = value ? JSON.parse(value) : undefined;
102
+ }
103
+ catch {
104
+ this.fieldValue = undefined;
105
+ }
106
+ this.render();
107
+ }
108
+ else if (name === "value") {
109
+ if (!this.controlled) {
110
+ this.currentValue = value;
111
+ this.initialValue = value;
112
+ }
113
+ this.syncControlValue();
114
+ }
115
+ else
116
+ this.render();
117
+ }
118
+ connectedCallback() {
119
+ if (!this.fieldValue) {
120
+ const encoded = this.getAttribute("field");
121
+ if (encoded) {
122
+ try {
123
+ this.fieldValue = JSON.parse(encoded);
124
+ }
125
+ catch { /* Keep a useful fallback below. */ }
126
+ }
127
+ }
128
+ if (this.currentValue === undefined && this.hasAttribute("value"))
129
+ this.currentValue = this.getAttribute("value");
130
+ if (this.initialValue === undefined && this.hasAttribute("value"))
131
+ this.initialValue = this.getAttribute("value");
132
+ this.render();
133
+ }
134
+ async render() {
135
+ const renderVersion = ++this.renderVersion;
136
+ const field = this.fieldValue;
137
+ if (!field) {
138
+ this.hidden = false;
139
+ this.removeAttribute("aria-busy");
140
+ this.replaceChildren();
141
+ return;
142
+ }
143
+ const adapter = getSharePointFieldAdapter(field);
144
+ this.setAttribute("aria-busy", "true");
145
+ try {
146
+ await loadFieldControl(adapter);
147
+ if (!this.isConnected || renderVersion !== this.renderVersion)
148
+ return;
149
+ this.removeAttribute("aria-busy");
150
+ const options = this.hasProvidedOptions ? this.currentOptions : (adapter.choices ?? []).map(value => ({ value, label: value }));
151
+ const label = adapter.label || adapter.name || adapter.fieldType;
152
+ const value = this.currentValue;
153
+ const valueText = textValue(value);
154
+ const defaultValueText = textValue(this.initialValue);
155
+ const name = adapter.name || undefined;
156
+ const disabled = this.hasAttribute("disabled");
157
+ const controlDisabled = disabled || (adapter.readOnly && !["input", "textarea", "rich-text-editor"].includes(adapter.control));
158
+ let control;
159
+ const setBasics = (element) => {
160
+ if (name)
161
+ element.setAttribute("name", name);
162
+ if (adapter.required)
163
+ element.setAttribute("required", "");
164
+ if (controlDisabled)
165
+ element.setAttribute("disabled", "");
166
+ return element;
167
+ };
168
+ const selectOptions = (element, tag) => {
169
+ if (tag === "option") {
170
+ const empty = document.createElement("option");
171
+ empty.value = "";
172
+ empty.textContent = this.getAttribute("empty-option-label") ?? "Select an option";
173
+ empty.disabled = adapter.required;
174
+ element.append(empty, ...options.map(option => makeOption(tag, option)));
175
+ }
176
+ else
177
+ element.append(...options.map(option => makeOption(tag, option)));
178
+ };
179
+ switch (adapter.control) {
180
+ case "input": {
181
+ const input = setBasics(document.createElement("spui-input"));
182
+ input.setAttribute("type", adapter.inputType ?? "text");
183
+ if (this.initialValue !== undefined)
184
+ input.setAttribute("value", defaultValueText);
185
+ if (adapter.min !== undefined)
186
+ input.setAttribute("min", String(adapter.min));
187
+ if (adapter.max !== undefined)
188
+ input.setAttribute("max", String(adapter.max));
189
+ if (adapter.step !== undefined)
190
+ input.setAttribute("step", String(adapter.step));
191
+ if (adapter.maxLength !== undefined)
192
+ input.setAttribute("maxlength", String(adapter.maxLength));
193
+ if (adapter.readOnly)
194
+ input.setAttribute("read-only", "");
195
+ input.value = valueText;
196
+ control = input;
197
+ break;
198
+ }
199
+ case "textarea": {
200
+ const textarea = setBasics(document.createElement("spui-textarea"));
201
+ if (this.initialValue !== undefined)
202
+ textarea.setAttribute("value", defaultValueText);
203
+ if (adapter.maxLength !== undefined)
204
+ textarea.setAttribute("maxlength", String(adapter.maxLength));
205
+ if (adapter.readOnly)
206
+ textarea.setAttribute("read-only", "");
207
+ textarea.value = valueText;
208
+ control = textarea;
209
+ break;
210
+ }
211
+ case "rich-text-editor": {
212
+ const editor = setBasics(document.createElement("spui-rich-text-editor"));
213
+ if (this.initialValue !== undefined)
214
+ editor.setAttribute("value", defaultValueText);
215
+ editor.setAttribute("content-mode", adapter.richText?.contentMode ?? "rich");
216
+ if (adapter.richText?.allowHyperlink === false)
217
+ editor.setAttribute("allow-hyperlink", "false");
218
+ if (adapter.readOnly)
219
+ editor.setAttribute("read-only", "");
220
+ editor.value = valueText;
221
+ control = editor;
222
+ break;
223
+ }
224
+ case "select": {
225
+ if (adapter.allowFillIn) {
226
+ const combo = setBasics(document.createElement("spui-combobox"));
227
+ combo.setAttribute("allow-freeform", "");
228
+ combo.setAttribute("placeholder", this.getAttribute("empty-option-label") ?? "Select an option");
229
+ selectOptions(combo, "spui-combobox-option");
230
+ combo.value = valueText;
231
+ control = combo;
232
+ }
233
+ else {
234
+ const select = setBasics(document.createElement("spui-select"));
235
+ selectOptions(select, "option");
236
+ if (this.initialValue !== undefined)
237
+ select.setAttribute("default-value", defaultValueText);
238
+ select.value = valueText;
239
+ control = select;
240
+ }
241
+ break;
242
+ }
243
+ case "radio-group": {
244
+ const radio = setBasics(document.createElement("spui-radio-group"));
245
+ selectOptions(radio, "spui-radio-option");
246
+ if (this.initialValue !== undefined)
247
+ radio.setAttribute("default-value", defaultValueText);
248
+ radio.value = valueText;
249
+ control = radio;
250
+ break;
251
+ }
252
+ case "checkbox": {
253
+ const checkbox = setBasics(document.createElement("spui-checkbox"));
254
+ checkbox.setAttribute("label", label);
255
+ if (this.initialValue !== undefined)
256
+ checkbox.defaultChecked = this.initialValue === true || this.initialValue === "true";
257
+ checkbox.checked = value === true || value === "true";
258
+ control = checkbox;
259
+ break;
260
+ }
261
+ case "multi-choice": {
262
+ const group = document.createElement("fieldset");
263
+ group.style.minWidth = "0";
264
+ group.style.margin = "0";
265
+ group.style.padding = "0";
266
+ group.style.border = "0";
267
+ group.style.display = "grid";
268
+ group.style.gap = "var(--spui-control-gap,6px)";
269
+ const legend = document.createElement("legend");
270
+ legend.textContent = label;
271
+ group.append(legend);
272
+ const selected = Array.isArray(value) ? value.map(String) : [];
273
+ const visibleOptions = [...options];
274
+ const initialSelected = Array.isArray(this.initialValue) ? this.initialValue.map(String) : [];
275
+ for (const selectedValue of [...selected, ...initialSelected])
276
+ if (!visibleOptions.some(option => String(option.value) === selectedValue))
277
+ visibleOptions.push({ value: selectedValue, label: selectedValue });
278
+ for (const option of visibleOptions) {
279
+ const checkbox = document.createElement("spui-checkbox");
280
+ checkbox.setAttribute("label", option.label);
281
+ checkbox.setAttribute("value", String(option.value));
282
+ if (option.disabled || controlDisabled)
283
+ checkbox.setAttribute("disabled", "");
284
+ if (Array.isArray(this.initialValue) && this.initialValue.map(String).includes(String(option.value)))
285
+ checkbox.setAttribute("default-checked", "");
286
+ checkbox.checked = selected.includes(String(option.value));
287
+ group.append(checkbox);
288
+ }
289
+ if (adapter.allowFillIn) {
290
+ const fillIn = document.createElement("spui-input");
291
+ fillIn.setAttribute("type", "text");
292
+ fillIn.setAttribute("aria-label", this.getAttribute("add-choice-label") ?? "Add another choice");
293
+ const add = document.createElement("spui-button");
294
+ add.setAttribute("appearance", "secondary");
295
+ add.textContent = this.getAttribute("add-choice-label") ?? "Add another choice";
296
+ if (controlDisabled)
297
+ add.setAttribute("disabled", "");
298
+ const addFillIn = () => {
299
+ const choice = fillIn.value?.trim() ?? "";
300
+ if (!choice || selected.includes(choice))
301
+ return;
302
+ this.acceptValue([...selected, choice]);
303
+ fillIn.value = "";
304
+ };
305
+ fillIn.addEventListener("keydown", event => { if (event instanceof KeyboardEvent && event.key === "Enter") {
306
+ event.preventDefault();
307
+ addFillIn();
308
+ } });
309
+ add.addEventListener("click", addFillIn);
310
+ const fillInRow = document.createElement("div");
311
+ fillInRow.append(fillIn, add);
312
+ group.append(fillInRow);
313
+ fillInRow.style.display = "flex";
314
+ fillInRow.style.alignItems = "end";
315
+ fillInRow.style.gap = "var(--spui-control-gap,8px)";
316
+ }
317
+ control = group;
318
+ break;
319
+ }
320
+ case "date-picker": {
321
+ const datePicker = setBasics(document.createElement("spui-date-picker"));
322
+ if (this.initialValue !== undefined)
323
+ datePicker.setAttribute("value", textValue(this.initialValue));
324
+ datePicker.value = valueText;
325
+ control = datePicker;
326
+ break;
327
+ }
328
+ case "combobox": {
329
+ const combo = setBasics(document.createElement("spui-combobox"));
330
+ if (adapter.multiple)
331
+ combo.setAttribute("multiselect", "");
332
+ combo.setAttribute("placeholder", this.getAttribute("empty-option-label") ?? "Select an option");
333
+ selectOptions(combo, "spui-combobox-option");
334
+ if (adapter.multiple) {
335
+ if (Array.isArray(this.initialValue))
336
+ combo.setAttribute("default-selected-options", JSON.stringify(this.initialValue.map(String)));
337
+ combo.selectedOptions = Array.isArray(value) ? value.map(String) : [];
338
+ }
339
+ else {
340
+ if (this.initialValue !== undefined) {
341
+ combo.setAttribute("selected-value", defaultValueText);
342
+ combo.setAttribute("value", options.find(option => String(option.value) === defaultValueText)?.label ?? defaultValueText);
343
+ }
344
+ combo.selectedValue = valueText;
345
+ combo.value = options.find(option => String(option.value) === valueText)?.label ?? valueText;
346
+ }
347
+ control = combo;
348
+ break;
349
+ }
350
+ case "read-only": {
351
+ const readonly = setBasics(document.createElement("spui-input"));
352
+ readonly.setAttribute("type", "text");
353
+ readonly.setAttribute("read-only", "");
354
+ readonly.value = valueText;
355
+ control = readonly;
356
+ break;
357
+ }
358
+ case "unsupported": {
359
+ const custom = this.renderUnsupported?.(field, adapter.unsupportedReason ?? "No renderer is registered for this SharePoint field type.");
360
+ const fallback = custom ?? Object.assign(document.createElement("div"), { textContent: `This SharePoint field type needs a custom renderer (${adapter.fieldType}).` });
361
+ if (!custom && fallback instanceof HTMLElement)
362
+ fallback.setAttribute("role", "note");
363
+ control = fallback;
364
+ break;
365
+ }
366
+ }
367
+ const wrapper = createFormField(control, {
368
+ id: this.id ? `${this.id}-field` : undefined,
369
+ className: this.getAttribute("class") ?? undefined,
370
+ label: adapter.control === "multi-choice" ? undefined : label,
371
+ hint: this.getAttribute("hint") ?? adapter.description,
372
+ validationMessage: this.getAttribute("validation-message") ?? undefined,
373
+ required: adapter.required,
374
+ disabled,
375
+ });
376
+ wrapper.dataset.sharePointField = adapter.name || adapter.fieldType;
377
+ this.hidden = adapter.hidden;
378
+ this.replaceChildren(wrapper);
379
+ }
380
+ catch {
381
+ if (!this.isConnected || renderVersion !== this.renderVersion)
382
+ return;
383
+ this.removeAttribute("aria-busy");
384
+ const error = document.createElement("div");
385
+ error.setAttribute("role", "alert");
386
+ error.textContent = "The field control could not be loaded.";
387
+ this.replaceChildren(error);
388
+ }
389
+ }
390
+ syncControlValue() {
391
+ const wrapper = this.querySelector(".spui-form-field");
392
+ const first = wrapper?.firstElementChild;
393
+ const control = (first?.localName === "fieldset" ? first : first?.nextElementSibling);
394
+ if (!control)
395
+ return;
396
+ if (control.localName === "fieldset") {
397
+ const selected = new Set(Array.isArray(this.currentValue) ? this.currentValue.map(String) : []);
398
+ control.querySelectorAll("spui-checkbox").forEach(checkbox => { checkbox.checked = selected.has(checkbox.getAttribute("value") ?? ""); });
399
+ }
400
+ else if (control.localName === "spui-checkbox")
401
+ control.checked = this.currentValue === true || this.currentValue === "true";
402
+ else if (control.localName === "spui-combobox" && control.hasAttribute("multiselect"))
403
+ control.selectedOptions = Array.isArray(this.currentValue) ? this.currentValue.map(String) : [];
404
+ else if ("value" in control)
405
+ control.value = textValue(this.currentValue);
406
+ }
407
+ acceptValue(value) {
408
+ this.currentValue = value;
409
+ const field = this.fieldValue;
410
+ if (!field)
411
+ return;
412
+ this.onValueChange?.(value, field);
413
+ this.dispatchEvent(new CustomEvent("spui-field-value-change", { detail: { value, field }, bubbles: true, composed: true }));
414
+ }
415
+ constructor() {
416
+ super();
417
+ for (const key of ["field", "value", "defaultValue", "options", "disabled", "onValueChange", "renderUnsupported"]) {
418
+ if (!Object.prototype.hasOwnProperty.call(this, key))
419
+ continue;
420
+ const pending = this[key];
421
+ delete this[key];
422
+ this[key] = pending;
423
+ }
424
+ this.addEventListener("spui-input", event => {
425
+ const target = event.target;
426
+ if ((target?.localName === "spui-combobox" && !target.hasAttribute("allow-freeform")) || (target?.localName === "spui-input" && target.closest("fieldset")))
427
+ return;
428
+ const value = event.detail?.value;
429
+ if (value !== undefined)
430
+ this.acceptValue(value);
431
+ });
432
+ this.addEventListener("spui-change", event => {
433
+ const detail = event.detail;
434
+ if (!detail)
435
+ return;
436
+ if (detail.checked !== undefined) {
437
+ if (this.fieldValue && getSharePointFieldAdapter(this.fieldValue).control === "multi-choice") {
438
+ const checkbox = event.target;
439
+ const choice = checkbox.getAttribute("value") ?? "";
440
+ const selected = new Set(Array.isArray(this.currentValue) ? this.currentValue.map(String) : []);
441
+ if (detail.checked === true)
442
+ selected.add(choice);
443
+ else
444
+ selected.delete(choice);
445
+ this.acceptValue([...selected]);
446
+ }
447
+ else
448
+ this.acceptValue(detail.checked === true);
449
+ }
450
+ else if (detail.selectedOptions)
451
+ this.acceptValue(detail.selectedOptions);
452
+ else if (detail.selectedValue !== undefined)
453
+ this.acceptValue(detail.selectedValue || null);
454
+ else if (detail.value !== undefined)
455
+ this.acceptValue(detail.value || null);
456
+ });
457
+ }
458
+ }
459
+ customElements.define("spui-sharepoint-field", SpuiSharePointField);
460
+ }
@@ -488,9 +488,9 @@ export function defineSpinButton() {
488
488
  buttons.append(incrementSlot, decrementSlot);
489
489
  control.append(inputSlot, buttons);
490
490
  const style = document.createElement("style");
491
- style.textContent = `${base}:host{display:grid;gap:var(--spui-control-gap,6px);width:100%}label{font-size:14px;font-weight:600;color:var(--spui-ui-muted,var(--spui-semantic-body-text,inherit))}[part~=control-group]{box-sizing:border-box;display:grid;grid-template-columns:minmax(0,1fr) 24px;grid-template-rows:1fr 1fr;min-height:var(--spui-control-height,32px);column-gap:4px;position:relative;isolation:isolate;border:1px solid var(--spui-control-border,var(--spui-ui-stroke,var(--spui-semantic-input-border,var(--colorNeutralStroke1,#8a8886))));border-radius:var(--spui-control-radius,4px);background:var(--spui-control-background,var(--spui-ui-surface,var(--spui-semantic-input-background,var(--colorNeutralBackground1,#fff))));color:var(--spui-ui-text,var(--spui-semantic-body-text,inherit))}[part~=control-group]:focus-within{outline:2px solid var(--spui-focus-ring,var(--colorStrokeFocus2,#005a9e));outline-offset:2px}:host([size=small]) [part~=control-group]{min-height:24px}:host([appearance=underline]) [part~=control-group]{border-width:0 0 1px;border-radius:0}:host([appearance=filled-darker]) [part~=control-group]{background:var(--colorNeutralBackground3,var(--spui-ui-surface-sunken,#f0f0f0));border-color:transparent}:host([appearance=filled-lighter]) [part~=control-group]{background:var(--colorNeutralBackground2,var(--spui-ui-surface-hover,#f5f5f5));border-color:transparent}input{grid-column:1;grid-row:1/3;min-width:0;width:100%;padding-inline:var(--spui-control-padding-inline,10px);border:0;border-radius:inherit;background:transparent;color:inherit;font:inherit;outline:none}input::-webkit-inner-spin-button,input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}span[part=buttons]{grid-column:2;grid-row:1/3;display:grid;grid-template-rows:1fr 1fr;min-height:0}button{display:grid;place-items:center;min-width:24px;padding:0;border:0;background:transparent;color:inherit;cursor:pointer}button:hover:not(:disabled){background:var(--spui-ui-surface-hover,#f5f5f5)}button:disabled{opacity:.42;cursor:not-allowed}button svg{width:12px;height:12px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}input:disabled{opacity:.55}input:read-only{cursor:default}@media(forced-colors:active){[part~=control-group]{border-color:FieldText}button:focus-visible,input:focus-visible{outline:1px solid Highlight}}`;
491
+ style.textContent = `${base}:host{display:grid;gap:var(--spui-control-gap,6px);width:100%}label{font-size:14px;font-weight:600;color:var(--spui-ui-muted,var(--spui-semantic-body-text,inherit))}[part~=control-group]{box-sizing:border-box;display:grid;grid-template-columns:minmax(0,1fr) 24px;grid-template-rows:1fr 1fr;min-height:var(--spui-control-height,32px);column-gap:4px;position:relative;isolation:isolate;border:1px solid var(--spui-control-border,var(--spui-ui-stroke,var(--spui-semantic-input-border,var(--colorNeutralStroke1,#8a8886))));border-radius:var(--spui-control-radius,4px);background:var(--spui-control-background,var(--spui-ui-surface,var(--spui-semantic-input-background,var(--colorNeutralBackground1,#fff))));color:var(--spui-ui-text,var(--spui-semantic-body-text,inherit))}[part~=control-group]:focus-within{border-width:2px;border-color:var(--spui-focus-ring,var(--spui-ui-accent,#0078d4));outline:0;box-shadow:none}[part~=control-group]:has(input[aria-invalid]:not([aria-invalid=false])){border-color:var(--spui-input-invalid-border,var(--spui-ui-danger,var(--colorPaletteRedBorder2,#c50f1f)))}:host([size=small]) [part~=control-group]{min-height:24px}:host([appearance=underline]) [part~=control-group]{border-width:0 0 1px;border-radius:0}:host([appearance=underline]) [part~=control-group]:focus-within{border-width:0 0 2px}:host([appearance=filled-darker]) [part~=control-group]{background:var(--colorNeutralBackground3,var(--spui-ui-surface-sunken,#f0f0f0));border-color:transparent}:host([appearance=filled-lighter]) [part~=control-group]{background:var(--colorNeutralBackground2,var(--spui-ui-surface-hover,#f5f5f5));border-color:transparent}input{grid-column:1;grid-row:1/3;min-width:0;width:100%;padding-inline:var(--spui-control-padding-inline,10px);border:0;border-radius:inherit;background:transparent;color:inherit;font:inherit;outline:none}input:focus-visible{outline:0}input::-webkit-inner-spin-button,input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}span[part=buttons]{grid-column:2;grid-row:1/3;display:grid;grid-template-rows:1fr 1fr;min-height:0}button{display:grid;place-items:center;min-width:24px;padding:0;border:0;background:transparent;color:inherit;cursor:pointer}button:hover:not(:disabled){background:var(--spui-ui-surface-hover,#f5f5f5)}button:disabled{opacity:.42;cursor:not-allowed}button svg{width:12px;height:12px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}input:disabled{opacity:.55}input:read-only{cursor:default}@media(forced-colors:active){[part~=control-group]{border-color:FieldText}[part~=control-group]:focus-within{border-width:2px;border-color:Highlight;outline:0;box-shadow:none}[part~=control-group]:has(input[aria-invalid]:not([aria-invalid=false])){border-color:CanvasText}[part~=control-group]:has(input[aria-invalid]:not([aria-invalid=false])):focus-within{border-color:Highlight}:host([appearance=underline]) [part~=control-group]:focus-within{border-width:0 0 2px}button:focus-visible{outline:1px solid Highlight}input:focus-visible{outline:0}}`;
492
492
  style.textContent += "[part~=control-group]{grid-template-rows:24px 24px;min-height:50px}:host([size=small]) [part~=control-group]{min-height:50px}span[part=buttons]{grid-template-rows:24px 24px;min-height:48px}button{min-height:24px}";
493
- style.textContent += "slot[name=input]{display:contents}slot[name=input]::slotted(input){grid-column:1;grid-row:1/3;min-width:0;width:100%;padding-inline:var(--spui-control-padding-inline,10px);border:0;border-radius:inherit;background:transparent;color:inherit;font:inherit;outline:none}slot[name=incrementButton],slot[name=decrementButton]{display:contents}slot[name=incrementButton]::slotted(button),slot[name=decrementButton]::slotted(button){display:grid;place-items:center;min-width:24px;min-height:24px;padding:0;border:0;background:transparent;color:inherit;cursor:pointer}slot[name=incrementButton]::slotted(button:disabled),slot[name=decrementButton]::slotted(button:disabled){opacity:.42;cursor:not-allowed}";
493
+ style.textContent += "slot[name=input]{display:contents}slot[name=input]::slotted(input){grid-column:1;grid-row:1/3;min-width:0;width:100%;padding-inline:var(--spui-control-padding-inline,10px);border:0!important;border-radius:inherit;background:transparent;color:inherit;font:inherit;outline:0!important;box-shadow:none!important}slot[name=input]::slotted(input:focus-visible){border:0!important;outline:0!important;outline-offset:0;box-shadow:none!important}slot[name=incrementButton],slot[name=decrementButton]{display:contents}slot[name=incrementButton]::slotted(button),slot[name=decrementButton]::slotted(button){display:grid;place-items:center;min-width:24px;min-height:24px;padding:0;border:0;background:transparent;color:inherit;cursor:pointer}slot[name=incrementButton]::slotted(button:disabled),slot[name=decrementButton]::slotted(button:disabled){opacity:.42;cursor:not-allowed}";
494
494
  shadow.replaceChildren(style, label, control);
495
495
  this.syncSlots();
496
496
  }