@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.
- package/AGENTS.md +211 -0
- package/LICENSE +21 -0
- package/README.md +897 -2
- package/components.json +5811 -0
- package/dist/appearance.d.ts +6 -0
- package/dist/appearance.js +217 -0
- package/dist/elements/accordion.d.ts +9 -0
- package/dist/elements/accordion.js +338 -0
- package/dist/elements/alert.d.ts +2 -0
- package/dist/elements/alert.js +62 -0
- package/dist/elements/avatar-group-primitives.d.ts +3 -0
- package/dist/elements/avatar-group-primitives.js +189 -0
- package/dist/elements/avatar-group.d.ts +2 -0
- package/dist/elements/avatar-group.js +110 -0
- package/dist/elements/avatar.d.ts +4 -0
- package/dist/elements/avatar.js +251 -0
- package/dist/elements/badge.d.ts +8 -0
- package/dist/elements/badge.js +182 -0
- package/dist/elements/breadcrumb.d.ts +4 -0
- package/dist/elements/breadcrumb.js +233 -0
- package/dist/elements/button-group.d.ts +1 -0
- package/dist/elements/button-group.js +20 -0
- package/dist/elements/button-variants.d.ts +6 -0
- package/dist/elements/button-variants.js +339 -0
- package/dist/elements/button.d.ts +5 -0
- package/dist/elements/button.js +233 -0
- package/dist/elements/card-primitives.d.ts +3 -0
- package/dist/elements/card-primitives.js +83 -0
- package/dist/elements/card.d.ts +5 -0
- package/dist/elements/card.js +213 -0
- package/dist/elements/carousel.d.ts +92 -0
- package/dist/elements/carousel.js +1112 -0
- package/dist/elements/color-picker.d.ts +24 -0
- package/dist/elements/color-picker.js +1004 -0
- package/dist/elements/combobox.d.ts +1 -0
- package/dist/elements/combobox.js +1012 -0
- package/dist/elements/data-grid-model.d.ts +117 -0
- package/dist/elements/data-grid-model.js +294 -0
- package/dist/elements/data-grid.d.ts +29 -0
- package/dist/elements/data-grid.js +1567 -0
- package/dist/elements/date-picker.d.ts +1 -0
- package/dist/elements/date-picker.js +486 -0
- package/dist/elements/dialog.d.ts +23 -0
- package/dist/elements/dialog.js +409 -0
- package/dist/elements/divider.d.ts +2 -0
- package/dist/elements/divider.js +66 -0
- package/dist/elements/drawer.d.ts +3 -0
- package/dist/elements/drawer.js +308 -0
- package/dist/elements/field-composition.d.ts +26 -0
- package/dist/elements/field-composition.js +133 -0
- package/dist/elements/field.d.ts +1 -0
- package/dist/elements/field.js +67 -0
- package/dist/elements/file-upload.d.ts +6 -0
- package/dist/elements/file-upload.js +211 -0
- package/dist/elements/floating-position.d.ts +26 -0
- package/dist/elements/floating-position.js +39 -0
- package/dist/elements/image.d.ts +28 -0
- package/dist/elements/image.js +38 -0
- package/dist/elements/info.d.ts +9 -0
- package/dist/elements/info.js +102 -0
- package/dist/elements/input.d.ts +5 -0
- package/dist/elements/input.js +255 -0
- package/dist/elements/label.d.ts +15 -0
- package/dist/elements/label.js +56 -0
- package/dist/elements/link.d.ts +1 -0
- package/dist/elements/link.js +92 -0
- package/dist/elements/list.d.ts +19 -0
- package/dist/elements/list.js +297 -0
- package/dist/elements/menu.d.ts +33 -0
- package/dist/elements/menu.js +1103 -0
- package/dist/elements/message-bar.d.ts +10 -0
- package/dist/elements/message-bar.js +299 -0
- package/dist/elements/nav.d.ts +32 -0
- package/dist/elements/nav.js +791 -0
- package/dist/elements/overflow.d.ts +9 -0
- package/dist/elements/overflow.js +218 -0
- package/dist/elements/persona.d.ts +3 -0
- package/dist/elements/persona.js +125 -0
- package/dist/elements/popover.d.ts +77 -0
- package/dist/elements/popover.js +840 -0
- package/dist/elements/progress-bar.d.ts +4 -0
- package/dist/elements/progress-bar.js +82 -0
- package/dist/elements/radio-group.d.ts +1 -0
- package/dist/elements/radio-group.js +303 -0
- package/dist/elements/rating-display.d.ts +2 -0
- package/dist/elements/rating-display.js +125 -0
- package/dist/elements/rating.d.ts +2 -0
- package/dist/elements/rating.js +206 -0
- package/dist/elements/rich-text-editor.d.ts +18 -0
- package/dist/elements/rich-text-editor.js +508 -0
- package/dist/elements/search.d.ts +3 -0
- package/dist/elements/search.js +207 -0
- package/dist/elements/select.d.ts +6 -0
- package/dist/elements/select.js +291 -0
- package/dist/elements/shared.d.ts +19 -0
- package/dist/elements/shared.js +396 -0
- package/dist/elements/skeleton.d.ts +6 -0
- package/dist/elements/skeleton.js +91 -0
- package/dist/elements/slider.d.ts +1 -0
- package/dist/elements/slider.js +248 -0
- package/dist/elements/spin-button.d.ts +7 -0
- package/dist/elements/spin-button.js +499 -0
- package/dist/elements/spinner.d.ts +4 -0
- package/dist/elements/spinner.js +124 -0
- package/dist/elements/swatch-picker.d.ts +5 -0
- package/dist/elements/swatch-picker.js +363 -0
- package/dist/elements/table.d.ts +21 -0
- package/dist/elements/table.js +261 -0
- package/dist/elements/tabs.d.ts +1 -0
- package/dist/elements/tabs.js +276 -0
- package/dist/elements/tag-picker.d.ts +29 -0
- package/dist/elements/tag-picker.js +697 -0
- package/dist/elements/tags.d.ts +21 -0
- package/dist/elements/tags.js +421 -0
- package/dist/elements/teaching-popover.d.ts +32 -0
- package/dist/elements/teaching-popover.js +412 -0
- package/dist/elements/text.d.ts +9 -0
- package/dist/elements/text.js +75 -0
- package/dist/elements/textarea.d.ts +1 -0
- package/dist/elements/textarea.js +182 -0
- package/dist/elements/toast.d.ts +25 -0
- package/dist/elements/toast.js +337 -0
- package/dist/elements/toaster.d.ts +6 -0
- package/dist/elements/toaster.js +356 -0
- package/dist/elements/toggle.d.ts +2 -0
- package/dist/elements/toggle.js +246 -0
- package/dist/elements/toolbar.d.ts +12 -0
- package/dist/elements/toolbar.js +381 -0
- package/dist/elements/tooltip.d.ts +76 -0
- package/dist/elements/tooltip.js +510 -0
- package/dist/elements/tree-model.d.ts +19 -0
- package/dist/elements/tree-model.js +34 -0
- package/dist/elements/tree.d.ts +5 -0
- package/dist/elements/tree.js +273 -0
- package/dist/elements/view-card.d.ts +2 -0
- package/dist/elements/view-card.js +78 -0
- package/dist/elements/virtual-list.d.ts +41 -0
- package/dist/elements/virtual-list.js +369 -0
- package/dist/elements.d.ts +72 -0
- package/dist/elements.js +207 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +66 -0
- package/dist/react.d.ts +4546 -0
- package/dist/react.js +6837 -0
- package/dist/theme.d.ts +169 -0
- package/dist/theme.js +167 -0
- package/dist/virtualizer-hooks.d.ts +80 -0
- package/dist/virtualizer-hooks.js +253 -0
- package/package.json +87 -4
- package/scripts/generate-token-index.mjs +43 -0
- package/scripts/sync-component-catalog-signatures.mjs +55 -0
- package/scripts/validate-component-catalog.mjs +44 -0
- package/skills/sharepoint-ui/SKILL.md +12 -0
- package/skills/sharepoint-ui/references/choosing-components.md +20 -0
- package/skills/sharepoint-ui/references/sharepoint-patterns.md +17 -0
- package/tokens.json +3777 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { base } from "./shared.js";
|
|
2
|
+
const themeWatches = new WeakMap();
|
|
3
|
+
const namedColorTokens = {
|
|
4
|
+
"dark-red": { token: "DarkRed", fallback: "#d69ca5" }, cranberry: { token: "Cranberry", fallback: "#eeacb2" }, red: { token: "Red", fallback: "#f1bbbc" },
|
|
5
|
+
pumpkin: { token: "Pumpkin", fallback: "#efc4ad" }, peach: { token: "Peach", fallback: "#ffddb3" }, marigold: { token: "Marigold", fallback: "#f9e2ae" },
|
|
6
|
+
gold: { token: "Gold", fallback: "#ecdfa5" }, brass: { token: "Brass", fallback: "#e0cea2" }, brown: { token: "Brown", fallback: "#ddc3b0" },
|
|
7
|
+
forest: { token: "Forest", fallback: "#bdd99b" }, seafoam: { token: "Seafoam", fallback: "#a8f0cd" }, "dark-green": { token: "DarkGreen", fallback: "#9ad29a" },
|
|
8
|
+
"light-teal": { token: "LightTeal", fallback: "#a6e9ed" }, teal: { token: "Teal", fallback: "#9bd9db" }, steel: { token: "Steel", fallback: "#94c8d4" },
|
|
9
|
+
blue: { token: "Blue", fallback: "#a9d3f2" }, "royal-blue": { token: "RoyalBlue", fallback: "#9abfdc" }, cornflower: { token: "Cornflower", fallback: "#c8d1fa" },
|
|
10
|
+
navy: { token: "Navy", fallback: "#a3b2e8" }, lavender: { token: "Lavender", fallback: "#d2ccf8" }, purple: { token: "Purple", fallback: "#c6b1de" },
|
|
11
|
+
grape: { token: "Grape", fallback: "#d9a7e0" }, lilac: { token: "Lilac", fallback: "#e6bfed" }, pink: { token: "Pink", fallback: "#f7c0e3" },
|
|
12
|
+
magenta: { token: "Magenta", fallback: "#eca5d1" }, plum: { token: "Plum", fallback: "#d696c0" }, beige: { token: "Beige", fallback: "#d7d4d4" },
|
|
13
|
+
mink: { token: "Mink", fallback: "#cecccb" }, platinum: { token: "Platinum", fallback: "#cdd6d8" }, anchor: { token: "Anchor", fallback: "#bcc3c7" }
|
|
14
|
+
};
|
|
15
|
+
/** Small, theme-aware people and entity marker. Images are optional; name/initials are the fast fallback. */
|
|
16
|
+
export function defineAvatar() {
|
|
17
|
+
if (customElements.get("spui-avatar"))
|
|
18
|
+
return;
|
|
19
|
+
class SpuiAvatar extends HTMLElement {
|
|
20
|
+
static observedAttributes = ["name", "image", "initials", "size", "shape", "color", "id-for-color", "decorative"];
|
|
21
|
+
imageSource = "";
|
|
22
|
+
themeRoot;
|
|
23
|
+
image = document.createElement("img");
|
|
24
|
+
initials = document.createElement("span");
|
|
25
|
+
fallback = document.createElement("span");
|
|
26
|
+
surface = document.createElement("span");
|
|
27
|
+
connectedCallback() {
|
|
28
|
+
if (!this.shadowRoot)
|
|
29
|
+
this.render();
|
|
30
|
+
this.sync();
|
|
31
|
+
this.watchTheme();
|
|
32
|
+
}
|
|
33
|
+
disconnectedCallback() {
|
|
34
|
+
this.unwatchTheme();
|
|
35
|
+
}
|
|
36
|
+
attributeChangedCallback() {
|
|
37
|
+
this.sync();
|
|
38
|
+
if (this.isConnected) {
|
|
39
|
+
this.unwatchTheme();
|
|
40
|
+
this.watchTheme();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
refreshThemeColor() {
|
|
44
|
+
const color = this.getAttribute("color");
|
|
45
|
+
const named = namedColorTokens[color];
|
|
46
|
+
if (named) {
|
|
47
|
+
this.dataset.namedColor = "";
|
|
48
|
+
this.style.setProperty("--spui-avatar-colorful-background", `var(--colorPalette${named.token}Background2, color-mix(in srgb, ${named.fallback} 18%, var(--spui-ui-surface, #fff)))`);
|
|
49
|
+
this.style.setProperty("--spui-avatar-colorful-foreground", `var(--colorPalette${named.token}Foreground2, var(--spui-ui-text, var(--spui-semantic-body-text, #242424)))`);
|
|
50
|
+
this.style.setProperty("--spui-avatar-active-color", `var(--colorPalette${named.token}BorderActive, var(--spui-ui-accent, #78716c))`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
delete this.dataset.namedColor;
|
|
54
|
+
this.style.removeProperty("--spui-avatar-active-color");
|
|
55
|
+
if (color !== "colorful")
|
|
56
|
+
return;
|
|
57
|
+
const key = this.getAttribute("name") || this.getAttribute("id-for-color") || "sharepoint-ui";
|
|
58
|
+
const hash = hashString(key);
|
|
59
|
+
const rawColor = getComputedStyle(this).getPropertyValue("--spui-palette-theme-primary").trim()
|
|
60
|
+
|| getComputedStyle(this).getPropertyValue("--spui-ui-accent").trim()
|
|
61
|
+
|| "#78716c";
|
|
62
|
+
const rgb = parseColor(rawColor);
|
|
63
|
+
const shade = 0.18 + (hash % 5) * 0.1;
|
|
64
|
+
const derived = rgb?.map(channel => Math.round(channel * (1 - shade)));
|
|
65
|
+
const derivedColor = derived ? `#${derived.map(channel => channel.toString(16).padStart(2, "0")).join("")}` : rawColor;
|
|
66
|
+
this.style.setProperty("--spui-avatar-colorful-background", derivedColor);
|
|
67
|
+
this.style.setProperty("--spui-avatar-colorful-foreground", derived ? (relativeLuminance(derived) > 0.179 ? "#000000" : "#ffffff") : "var(--spui-semantic-primary-button-text, #ffffff)");
|
|
68
|
+
}
|
|
69
|
+
render() {
|
|
70
|
+
const shadow = this.attachShadow({ mode: "open" });
|
|
71
|
+
this.surface.className = "surface";
|
|
72
|
+
this.surface.part.add("surface");
|
|
73
|
+
this.image.className = "image";
|
|
74
|
+
this.image.part.add("image");
|
|
75
|
+
this.image.alt = "";
|
|
76
|
+
this.image.loading = "lazy";
|
|
77
|
+
this.image.decoding = "async";
|
|
78
|
+
this.image.addEventListener("load", () => { this.dataset.imageState = "loaded"; });
|
|
79
|
+
this.image.addEventListener("error", () => { this.dataset.imageState = "error"; });
|
|
80
|
+
this.initials.className = "initials";
|
|
81
|
+
this.initials.part.add("initials");
|
|
82
|
+
this.fallback.className = "fallback";
|
|
83
|
+
this.fallback.part.add("fallback");
|
|
84
|
+
this.fallback.setAttribute("aria-hidden", "true");
|
|
85
|
+
this.fallback.innerHTML = '<svg viewBox="0 0 24 24" focusable="false"><path d="M12 12a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9Zm0 2c-4.4 0-8 2.2-8 5v2h16v-2c0-2.8-3.6-5-8-5Z"/></svg>';
|
|
86
|
+
this.surface.append(this.image, this.initials, this.fallback);
|
|
87
|
+
const style = document.createElement("style");
|
|
88
|
+
style.textContent = `${base}:host{--spui-avatar-size:32px;display:inline-grid;flex:none;width:var(--spui-avatar-size);height:var(--spui-avatar-size);vertical-align:middle;line-height:1}:host([size]){width:var(--spui-avatar-size);height:var(--spui-avatar-size)}.surface{position:relative;box-sizing:border-box;display:grid;place-items:center;width:100%;height:100%;overflow:hidden;border:1px solid var(--spui-avatar-border,var(--spui-ui-stroke,var(--spui-semantic-body-divider,#d1d1d1)));border-radius:var(--spui-avatar-radius,50%);background:var(--spui-avatar-background,var(--spui-semantic-body-subtext,var(--colorNeutralBackground3,#f0f0f0)));color:var(--spui-avatar-foreground,var(--spui-semantic-body-text,var(--colorNeutralForeground1,#323130)));font:600 calc(var(--spui-avatar-size) * .36)/1 var(--spui-font-family,inherit);user-select:none}.image{position:absolute;inset:0;z-index:1;width:100%;height:100%;object-fit:cover}.initials{z-index:0;max-width:100%;overflow:hidden;padding:0 2px;text-overflow:clip;white-space:nowrap}.fallback{z-index:0;display:grid;place-items:center;width:62%;height:62%;color:currentColor}.initials:not(:empty)~.fallback{display:none}.fallback svg{display:block;width:100%;height:100%;fill:currentColor}:host([data-image-state=loading]) .image,:host([data-image-state=error]) .image{visibility:hidden}:host([data-image-state=loaded]) .initials,:host([data-image-state=loaded]) .fallback{visibility:hidden}:host([shape=square]) .surface{border-radius:var(--spui-avatar-radius,var(--spui-control-radius,6px))}:host([color=brand]) .surface{background:var(--spui-avatar-brand-background,var(--spui-semantic-primary-button-background,var(--colorBrandBackground,var(--spui-avatar-active-color,var(--spui-ui-accent,#78716c)))));color:var(--spui-avatar-brand-foreground,var(--spui-semantic-primary-button-text,var(--colorNeutralForegroundOnBrand,#fff)))}:host([color=colorful]) .surface,:host([data-named-color]) .surface{background:var(--spui-avatar-colorful-background,var(--spui-semantic-primary-button-background,var(--colorBrandBackground,var(--spui-avatar-active-color,var(--spui-ui-accent,#78716c)))));color:var(--spui-avatar-colorful-foreground,var(--spui-semantic-primary-button-text,#fff))}:host([active=active]:not([active-appearance])) .surface,:host([active=active][active-appearance=ring]) .surface{box-shadow:0 0 0 2px var(--spui-ui-surface,#fff),0 0 0 4px var(--spui-avatar-active-color,var(--spui-ui-accent,#78716c))}:host([active=active][active-appearance=shadow]) .surface{box-shadow:var(--spui-avatar-active-shadow,var(--spui-card-shadow,0 2px 8px rgb(0 0 0 / 24%)))}:host([active=active][active-appearance=ring-shadow]) .surface{box-shadow:0 0 0 2px var(--spui-ui-surface,#fff),0 0 0 4px var(--spui-avatar-active-color,var(--spui-ui-accent,#78716c)),var(--spui-avatar-active-shadow,var(--spui-card-shadow,0 2px 8px rgb(0 0 0 / 24%)))}:host([active=inactive]){opacity:.56}:host([data-custom-icon=true]) .initials,:host([data-custom-icon=true]) .fallback{display:none}:host([data-custom-icon=true]) .icon{z-index:0;display:grid;place-items:center;width:70%;height:70%;color:inherit}:host([data-custom-badge=true]) .badge{position:absolute;z-index:2;inset-block-end:0;inset-inline-end:0;display:grid;place-items:center;min-width:var(--spui-avatar-badge-size,10px);min-height:var(--spui-avatar-badge-size,10px);border:2px solid var(--spui-avatar-badge-border,var(--spui-ui-surface,#fff));border-radius:50%;background:var(--spui-avatar-badge-background,var(--spui-semantic-success-background,#13a10e));color:var(--spui-avatar-badge-foreground,#fff);font:600 9px/1 var(--spui-font-family,inherit)}:host(:not([data-custom-badge=true])) .badge{display:none}@media(forced-colors:active){.surface{border-color:CanvasText;background:Canvas;color:CanvasText;forced-color-adjust:none}:host([data-named-color]) .surface{border-color:CanvasText;background:Canvas;color:CanvasText}.image{forced-color-adjust:auto}:host([active=active]) .surface{outline:2px solid Highlight;outline-offset:2px;box-shadow:none}}`;
|
|
89
|
+
style.textContent += `.surface{background:var(--spui-avatar-background,var(--spui-palette-neutral-light,var(--colorNeutralBackground3,#f0f0f0)));font-size:clamp(10px,calc(var(--spui-avatar-size) * .36),48px)}`;
|
|
90
|
+
style.textContent += `@media(forced-colors:active){.surface{border-color:CanvasText;background:Canvas;color:CanvasText}}`;
|
|
91
|
+
const icon = document.createElement("span");
|
|
92
|
+
icon.className = "icon";
|
|
93
|
+
icon.part.add("icon");
|
|
94
|
+
icon.append(document.createElement("slot"));
|
|
95
|
+
icon.firstElementChild.setAttribute("name", "icon");
|
|
96
|
+
const badge = document.createElement("span");
|
|
97
|
+
badge.className = "badge";
|
|
98
|
+
badge.part.add("badge");
|
|
99
|
+
badge.append(document.createElement("slot"));
|
|
100
|
+
badge.firstElementChild.setAttribute("name", "badge");
|
|
101
|
+
const iconSlot = icon.firstElementChild;
|
|
102
|
+
const badgeSlot = badge.firstElementChild;
|
|
103
|
+
iconSlot.addEventListener("slotchange", () => this.toggleAttribute("data-custom-icon", iconSlot.assignedNodes({ flatten: true }).length > 0));
|
|
104
|
+
badgeSlot.addEventListener("slotchange", () => this.toggleAttribute("data-custom-badge", badgeSlot.assignedNodes({ flatten: true }).length > 0));
|
|
105
|
+
this.surface.append(icon, badge);
|
|
106
|
+
shadow.append(style, this.surface);
|
|
107
|
+
}
|
|
108
|
+
sync() {
|
|
109
|
+
if (!this.shadowRoot)
|
|
110
|
+
return;
|
|
111
|
+
const size = this.getAttribute("size")?.trim() || "32px";
|
|
112
|
+
const normalizedSize = /^\d+(?:\.\d+)?$/.test(size) ? `${size}px` : size;
|
|
113
|
+
this.style.setProperty("--spui-avatar-size", CSS.supports("width", normalizedSize) ? normalizedSize : "32px");
|
|
114
|
+
const shape = this.getAttribute("shape") === "square" ? "square" : "circular";
|
|
115
|
+
this.dataset.shape = shape;
|
|
116
|
+
if (this.getAttribute("shape") !== shape)
|
|
117
|
+
this.setAttribute("shape", shape);
|
|
118
|
+
const color = this.getAttribute("color");
|
|
119
|
+
if (!color || (!["neutral", "brand", "colorful"].includes(color) && !Object.hasOwn(namedColorTokens, color)))
|
|
120
|
+
this.setAttribute("color", "neutral");
|
|
121
|
+
const name = this.getAttribute("name")?.trim() ?? "";
|
|
122
|
+
const locale = this.closest("[lang]")?.getAttribute("lang") || this.ownerDocument.documentElement.lang || undefined;
|
|
123
|
+
const initials = this.getAttribute("initials")?.trim() || getInitials(name, Number.parseFloat(this.style.getPropertyValue("--spui-avatar-size")), locale, getComputedStyle(this).direction === "rtl");
|
|
124
|
+
this.initials.textContent = initials;
|
|
125
|
+
const source = this.getAttribute("image")?.trim() ?? "";
|
|
126
|
+
if (source !== this.imageSource) {
|
|
127
|
+
this.imageSource = source;
|
|
128
|
+
if (source) {
|
|
129
|
+
this.setAttribute("data-image-state", "loading");
|
|
130
|
+
this.image.src = source;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
this.image.removeAttribute("src");
|
|
134
|
+
this.setAttribute("data-image-state", "none");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else if (source && !this.dataset.imageState)
|
|
138
|
+
this.setAttribute("data-image-state", "loading");
|
|
139
|
+
const decorative = this.hasAttribute("decorative");
|
|
140
|
+
const hasExplicitName = (this.hasAttribute("aria-label") && this.dataset.autoLabel === undefined) || this.hasAttribute("aria-labelledby");
|
|
141
|
+
const accessibleName = name || initials;
|
|
142
|
+
if (decorative || (!hasExplicitName && !accessibleName)) {
|
|
143
|
+
if (this.dataset.autoLabel !== undefined) {
|
|
144
|
+
this.removeAttribute("aria-label");
|
|
145
|
+
delete this.dataset.autoLabel;
|
|
146
|
+
}
|
|
147
|
+
if (!this.hasAttribute("aria-hidden")) {
|
|
148
|
+
this.setAttribute("aria-hidden", "true");
|
|
149
|
+
this.dataset.autoHidden = "";
|
|
150
|
+
}
|
|
151
|
+
if (!this.hasAttribute("role")) {
|
|
152
|
+
this.setAttribute("role", "presentation");
|
|
153
|
+
this.dataset.autoRole = "";
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
if (this.dataset.autoHidden !== undefined) {
|
|
158
|
+
this.removeAttribute("aria-hidden");
|
|
159
|
+
delete this.dataset.autoHidden;
|
|
160
|
+
}
|
|
161
|
+
if (this.dataset.autoRole !== undefined) {
|
|
162
|
+
this.removeAttribute("role");
|
|
163
|
+
delete this.dataset.autoRole;
|
|
164
|
+
}
|
|
165
|
+
if (!this.hasAttribute("role")) {
|
|
166
|
+
this.setAttribute("role", "img");
|
|
167
|
+
this.dataset.autoRole = "";
|
|
168
|
+
}
|
|
169
|
+
if (!this.hasAttribute("aria-labelledby") && (!this.hasAttribute("aria-label") || this.dataset.autoLabel !== undefined)) {
|
|
170
|
+
this.setAttribute("aria-label", accessibleName);
|
|
171
|
+
this.dataset.autoLabel = "";
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
this.refreshThemeColor();
|
|
175
|
+
}
|
|
176
|
+
watchTheme() {
|
|
177
|
+
if (this.getAttribute("color") !== "colorful" || !this.isConnected)
|
|
178
|
+
return;
|
|
179
|
+
const root = this.closest("[data-spui-appearance]");
|
|
180
|
+
if (!root)
|
|
181
|
+
return;
|
|
182
|
+
this.unwatchTheme();
|
|
183
|
+
this.themeRoot = root;
|
|
184
|
+
let watch = themeWatches.get(root);
|
|
185
|
+
if (!watch) {
|
|
186
|
+
const instances = new Set();
|
|
187
|
+
const observer = new MutationObserver(() => instances.forEach(avatar => avatar.refreshThemeColor()));
|
|
188
|
+
observer.observe(root, { attributes: true, attributeFilter: ["style"] });
|
|
189
|
+
watch = { instances, observer };
|
|
190
|
+
themeWatches.set(root, watch);
|
|
191
|
+
}
|
|
192
|
+
watch.instances.add(this);
|
|
193
|
+
}
|
|
194
|
+
unwatchTheme() {
|
|
195
|
+
if (!this.themeRoot)
|
|
196
|
+
return;
|
|
197
|
+
const watch = themeWatches.get(this.themeRoot);
|
|
198
|
+
watch?.instances.delete(this);
|
|
199
|
+
if (watch && watch.instances.size === 0) {
|
|
200
|
+
watch.observer.disconnect();
|
|
201
|
+
themeWatches.delete(this.themeRoot);
|
|
202
|
+
}
|
|
203
|
+
this.themeRoot = undefined;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
customElements.define("spui-avatar", SpuiAvatar);
|
|
207
|
+
}
|
|
208
|
+
function getInitials(name, size, locale, rtl = false) {
|
|
209
|
+
const words = name.replace(/\([^)]*\)|\[[^\]]*\]|\{[^}]*\}/g, " ").trim().split(/\s+/).filter(Boolean);
|
|
210
|
+
if (!words.length)
|
|
211
|
+
return "";
|
|
212
|
+
const selectedWords = size <= 20 ? [words[0]] : words.length === 2 ? words : words.length === 3 ? [words[0], words[2]] : [words[0]];
|
|
213
|
+
let initials = selectedWords.map(word => firstGrapheme(word, locale)).join("");
|
|
214
|
+
try {
|
|
215
|
+
initials = initials.toLocaleUpperCase(locale);
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
initials = initials.toUpperCase();
|
|
219
|
+
}
|
|
220
|
+
const characters = Array.from(initials);
|
|
221
|
+
return rtl && characters.length > 1 ? characters.reverse().join("") : initials;
|
|
222
|
+
}
|
|
223
|
+
function firstGrapheme(value, locale) {
|
|
224
|
+
const Segmenter = Intl.Segmenter;
|
|
225
|
+
if (Segmenter) {
|
|
226
|
+
try {
|
|
227
|
+
return [...new Segmenter(locale, { granularity: "grapheme" }).segment(value)][0]?.segment ?? "";
|
|
228
|
+
}
|
|
229
|
+
catch { /* Fall through for unsupported locale tags or older engines. */ }
|
|
230
|
+
}
|
|
231
|
+
return Array.from(value.normalize("NFC"))[0] ?? "";
|
|
232
|
+
}
|
|
233
|
+
function hashString(value) {
|
|
234
|
+
let hash = 2166136261;
|
|
235
|
+
for (const character of value)
|
|
236
|
+
hash = Math.imul(hash ^ character.codePointAt(0), 16777619);
|
|
237
|
+
return hash >>> 0;
|
|
238
|
+
}
|
|
239
|
+
function parseColor(value) {
|
|
240
|
+
const hex = /^#([\da-f]{3}|[\da-f]{6})$/i.exec(value);
|
|
241
|
+
if (hex) {
|
|
242
|
+
const expanded = hex[1].length === 3 ? [...hex[1]].map(character => character + character).join("") : hex[1];
|
|
243
|
+
return [0, 2, 4].map(index => parseInt(expanded.slice(index, index + 2), 16));
|
|
244
|
+
}
|
|
245
|
+
const rgb = /^rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)/i.exec(value);
|
|
246
|
+
return rgb ? [Number(rgb[1]), Number(rgb[2]), Number(rgb[3])] : null;
|
|
247
|
+
}
|
|
248
|
+
function relativeLuminance([red, green, blue]) {
|
|
249
|
+
const linear = (channel) => { const value = channel / 255; return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4; };
|
|
250
|
+
return linear(red) * 0.2126 + linear(green) * 0.7152 + linear(blue) * 0.0722;
|
|
251
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type BadgeAppearance = "filled" | "ghost" | "outline" | "tint";
|
|
2
|
+
export type BadgeColor = "brand" | "danger" | "important" | "informative" | "severe" | "subtle" | "success" | "warning";
|
|
3
|
+
export type BadgeShape = "circular" | "rounded" | "square";
|
|
4
|
+
export type BadgeSize = "tiny" | "extra-small" | "small" | "medium" | "large" | "extra-large";
|
|
5
|
+
export type PresenceBadgeStatus = "busy" | "out-of-office" | "away" | "available" | "offline" | "do-not-disturb" | "unknown" | "blocked";
|
|
6
|
+
export declare function defineBadge(): void;
|
|
7
|
+
export declare function defineCounterBadge(): void;
|
|
8
|
+
export declare function definePresenceBadge(): void;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { base } from "./shared.js";
|
|
2
|
+
const badgeStyle = `${base}
|
|
3
|
+
:host{--badge-size:20px;--badge-pad:6px;--badge-status:var(--spui-ui-accent,var(--colorBrandBackground,#0078d4));--badge-filled-background:var(--spui-ui-accent,var(--colorBrandBackground,#0078d4));--badge-filled-foreground:var(--spui-ui-accent-text,#fff);--badge-filled-border:var(--colorTransparentStroke,transparent);--badge-tint-background:color-mix(in srgb,var(--badge-status) 12%,var(--spui-ui-surface,#fff));--badge-tint-foreground:var(--badge-status);--badge-tint-border:var(--badge-outline-border);--badge-outline-border:var(--badge-status);display:inline-flex;align-items:center;justify-content:center;gap:4px;box-sizing:border-box;min-width:var(--badge-size);min-height:var(--badge-size);padding-inline:var(--badge-pad);border:1px solid var(--badge-outline-border);border-radius:999px;background:var(--badge-filled-background);color:var(--badge-filled-foreground);font:600 12px/1 var(--spui-font-family,inherit);white-space:nowrap;vertical-align:middle}
|
|
4
|
+
:host([color=success]){--badge-status:var(--spui-ui-success,#107c10);--badge-color:var(--spui-ui-success-surface,#dff6dd);--badge-foreground:var(--badge-status);--badge-tint:var(--spui-ui-success-surface,#dff6dd)}
|
|
5
|
+
:host([color=warning]),:host([color=important]){--badge-status:var(--spui-ui-warning,#835b00);--badge-color:var(--spui-ui-warning-surface,#fff4ce);--badge-foreground:var(--badge-status);--badge-tint:var(--spui-ui-warning-surface,#fff4ce)}
|
|
6
|
+
:host([color=danger]),:host([color=severe]){--badge-status:var(--spui-ui-danger,#b10e1c);--badge-color:var(--spui-ui-danger-surface,#fde7e9);--badge-foreground:var(--badge-status);--badge-tint:var(--spui-ui-danger-surface,#fde7e9)}
|
|
7
|
+
:host([color=informative]){--badge-status:var(--spui-ui-accent,#0078d4);--badge-color:var(--spui-ui-accent,var(--colorBrandBackground,#0078d4));--badge-tint:color-mix(in srgb,var(--badge-status) 12%,var(--spui-ui-surface,#fff))}
|
|
8
|
+
:host([color=subtle]){--badge-status:var(--spui-ui-muted,#616161);--badge-color:color-mix(in srgb,var(--spui-ui-stroke,#d1d1d1) 25%,var(--spui-ui-surface,#fff));--badge-foreground:var(--spui-ui-text,#242424);--badge-tint:var(--badge-color)}
|
|
9
|
+
:host([color=brand]){--badge-filled-background:var(--colorBrandBackground,var(--spui-ui-accent,#0078d4));--badge-filled-foreground:var(--colorNeutralForegroundOnBrand,var(--spui-ui-accent-text,#fff));--badge-status:var(--colorBrandForeground1,var(--spui-ui-accent,#0078d4));--badge-tint-background:var(--colorBrandBackground2,color-mix(in srgb,var(--badge-status) 12%,var(--spui-ui-surface,#fff)));--badge-tint-foreground:var(--colorBrandForeground2,var(--badge-status));--badge-tint-border:var(--colorBrandStroke2,var(--badge-status));--badge-outline-border:var(--badge-status)}
|
|
10
|
+
:host([color=danger]){--badge-filled-background:var(--colorPaletteRedBackground3,color-mix(in srgb,var(--spui-ui-danger,#b10e1c) 85%,black));--badge-filled-foreground:var(--colorNeutralForegroundOnBrand,#fff);--badge-status:var(--colorPaletteRedForeground3,var(--spui-ui-danger,#b10e1c));--badge-tint-background:var(--colorPaletteRedBackground1,var(--spui-ui-danger-surface,#fde7e9));--badge-tint-foreground:var(--colorPaletteRedForeground1,var(--spui-ui-danger,#b10e1c));--badge-tint-border:var(--colorPaletteRedBorder1,var(--badge-status));--badge-outline-border:var(--colorPaletteRedBorder2,var(--badge-status))}
|
|
11
|
+
:host([color=severe]){--badge-filled-background:var(--colorPaletteDarkOrangeBackground3,color-mix(in srgb,var(--spui-ui-warning,#835b00) 88%,black));--badge-filled-foreground:var(--colorNeutralForegroundOnBrand,#fff);--badge-status:var(--colorPaletteDarkOrangeForeground3,var(--spui-ui-warning,#835b00));--badge-tint-background:var(--colorPaletteDarkOrangeBackground1,var(--spui-ui-warning-surface,#fff4ce));--badge-tint-foreground:var(--colorPaletteDarkOrangeForeground1,var(--spui-ui-warning,#835b00));--badge-tint-border:var(--colorPaletteDarkOrangeBorder1,var(--badge-status));--badge-outline-border:var(--badge-status)}
|
|
12
|
+
:host([color=success]){--badge-filled-background:var(--colorPaletteGreenBackground3,color-mix(in srgb,var(--spui-ui-success,#107c10) 85%,black));--badge-filled-foreground:var(--colorNeutralForegroundOnBrand,#fff);--badge-status:var(--colorPaletteGreenForeground3,var(--spui-ui-success,#107c10));--badge-tint-background:var(--colorPaletteGreenBackground1,var(--spui-ui-success-surface,#dff6dd));--badge-tint-foreground:var(--colorPaletteGreenForeground1,var(--spui-ui-success,#107c10));--badge-tint-border:var(--colorPaletteGreenBorder1,var(--badge-status));--badge-outline-border:var(--colorPaletteGreenBorder2,var(--badge-status))}
|
|
13
|
+
:host([color=warning]){--badge-filled-background:var(--colorPaletteYellowBackground3,color-mix(in srgb,var(--spui-ui-warning,#835b00) 82%,black));--badge-filled-foreground:var(--colorNeutralForeground1Static,#242424);--badge-status:var(--colorPaletteYellowForeground2,var(--spui-ui-warning,#835b00));--badge-tint-background:var(--colorPaletteYellowBackground1,var(--spui-ui-warning-surface,#fff4ce));--badge-tint-foreground:var(--colorPaletteYellowForeground1,var(--spui-ui-warning,#835b00));--badge-tint-border:var(--colorPaletteYellowBorder1,var(--badge-status));--badge-outline-border:var(--badge-status)}
|
|
14
|
+
:host([color=important]){--badge-filled-background:var(--colorNeutralForeground1,#242424);--badge-filled-foreground:var(--colorNeutralBackground1,#fff);--badge-status:var(--colorNeutralForeground1,#242424);--badge-tint-background:var(--colorNeutralForeground3,#616161);--badge-tint-foreground:var(--colorNeutralBackground1,#fff);--badge-tint-border:var(--colorTransparentStroke,transparent);--badge-outline-border:var(--colorNeutralStrokeAccessible,#616161)}
|
|
15
|
+
:host([color=informative]){--badge-filled-background:var(--colorNeutralBackground5,#e0e0e0);--badge-filled-foreground:var(--colorNeutralForeground3,#616161);--badge-status:var(--colorNeutralForeground3,#616161);--badge-tint-background:var(--colorNeutralBackground4,#f0f0f0);--badge-tint-foreground:var(--colorNeutralForeground3,#616161);--badge-tint-border:var(--colorNeutralStroke2,#d1d1d1);--badge-outline-border:var(--colorNeutralStroke2,#d1d1d1)}
|
|
16
|
+
:host([color=subtle]){--badge-filled-background:var(--colorNeutralBackground1,var(--spui-ui-surface,#fff));--badge-filled-foreground:var(--colorNeutralForeground1,var(--spui-ui-text,#242424));--badge-status:var(--colorNeutralForegroundStaticInverted,#fff);--badge-tint-background:var(--colorNeutralBackground1,var(--spui-ui-surface,#fff));--badge-tint-foreground:var(--colorNeutralForeground3,var(--spui-ui-muted,#616161));--badge-tint-border:var(--colorNeutralStroke2,var(--spui-ui-stroke,#d1d1d1));--badge-outline-border:var(--badge-status)}
|
|
17
|
+
:host([color=brand]){--badge-outline-foreground:var(--colorBrandForeground1,var(--spui-ui-accent,#0078d4))}:host([color=danger]){--badge-outline-foreground:var(--colorPaletteRedForeground3,var(--spui-ui-danger,#b10e1c))}:host([color=important]){--badge-outline-foreground:var(--colorNeutralForeground3,#616161)}:host([color=informative]){--badge-outline-foreground:var(--colorNeutralForeground3,#616161)}:host([color=severe]){--badge-outline-foreground:var(--colorPaletteDarkOrangeForeground3,var(--spui-ui-warning,#835b00))}:host([color=subtle]){--badge-outline-foreground:var(--colorNeutralForegroundStaticInverted,#fff)}:host([color=success]){--badge-outline-foreground:var(--colorPaletteGreenForeground3,var(--spui-ui-success,#107c10))}:host([color=warning]){--badge-outline-foreground:var(--colorPaletteYellowForeground2,var(--spui-ui-warning,#835b00))}
|
|
18
|
+
:host([appearance=ghost]){border-color:transparent;background:transparent;color:var(--badge-status)}
|
|
19
|
+
:host([appearance=outline]){background:transparent;color:var(--badge-outline-foreground,var(--badge-status))}
|
|
20
|
+
:host([appearance=filled]){border-color:var(--badge-filled-border)}:host([appearance=ghost]){border-color:transparent}:host([appearance=outline]){border-color:var(--badge-outline-border)}:host([appearance=tint]){border-color:var(--badge-tint-border);background:var(--badge-tint-background);color:var(--badge-tint-foreground)}
|
|
21
|
+
:host([shape=rounded]){border-radius:6px}:host([shape=square]){border-radius:2px}:host([shape=circular]){border-radius:999px}
|
|
22
|
+
:host([size=tiny]){--badge-size:6px;--badge-pad:0;width:6px;min-width:0;min-height:0;height:6px;padding:0;font-size:4px}:host([size=extra-small]){--badge-size:10px;--badge-pad:0;width:10px;min-width:0;min-height:0;height:10px;padding:0;font-size:6px}:host([size=small]){--badge-size:16px;--badge-pad:4px;font-size:10px}:host([size=large]){--badge-size:24px;--badge-pad:7px;font-size:14px}:host([size=extra-large]){--badge-size:32px;--badge-pad:9px;font-size:16px}
|
|
23
|
+
.content{display:inline-flex;align-items:center;justify-content:center;order:1}.icon{display:inline-flex;align-items:center;justify-content:center;order:0;line-height:0}slot[name=icon]{display:contents}
|
|
24
|
+
:host([icon-position=after]) .icon{order:2}:host([icon-position=after]) .content{order:1}
|
|
25
|
+
:host([dot]){width:var(--badge-size);min-width:var(--badge-size);padding:0}:host([dot]) .content,:host([dot]) .icon{display:none}:host([hidden]){display:none}
|
|
26
|
+
:host([kind=presence]){position:relative;width:var(--badge-size);min-width:var(--badge-size);padding:0;border-width:1px;background:var(--badge-color)}
|
|
27
|
+
:host([kind=presence]){background:var(--colorNeutralBackground1,var(--spui-ui-surface,#fff));border-color:var(--colorTransparentStroke,transparent);color:var(--badge-status)}:host([kind=presence][status=available]){--badge-status:var(--colorPaletteLightGreenForeground3,var(--spui-ui-success,#13a10e))}:host([kind=presence][status=away]){--badge-status:var(--colorPaletteMarigoldBackground3,var(--spui-ui-warning,#835b00))}:host([kind=presence][status=busy]),:host([kind=presence][status=do-not-disturb]),:host([kind=presence][status=blocked]){--badge-status:var(--colorPaletteRedBackground3,var(--spui-ui-danger,#c50f1f))}:host([kind=presence][status=out-of-office]){--badge-status:var(--colorPaletteBerryForeground3,var(--spui-ui-danger,#c50f1f))}:host([kind=presence][status=offline]),:host([kind=presence][status=unknown]){--badge-status:var(--colorNeutralForeground3,var(--spui-ui-muted,#616161))}
|
|
28
|
+
:host([kind=presence][status=available]){--badge-color:var(--spui-ui-success-surface,#dff6dd);--badge-foreground:var(--spui-ui-success,#107c10)}
|
|
29
|
+
:host([kind=presence][status=away]){--badge-color:var(--spui-ui-warning-surface,#fff4ce);--badge-foreground:var(--spui-ui-warning,#835b00)}
|
|
30
|
+
:host([kind=presence][status=busy]),:host([kind=presence][status=do-not-disturb]),:host([kind=presence][status=blocked]),:host([kind=presence][status=out-of-office]){--badge-color:var(--spui-ui-danger-surface,#fde7e9);--badge-foreground:var(--spui-ui-danger,#b10e1c)}
|
|
31
|
+
:host([kind=presence][status=offline]),:host([kind=presence][status=unknown]){--badge-color:color-mix(in srgb,var(--spui-ui-stroke,#d1d1d1) 25%,var(--spui-ui-surface,#fff));--badge-foreground:var(--spui-ui-muted,#616161)}
|
|
32
|
+
.icon svg{display:block;width:100%;height:100%}:host([kind=presence]) .icon{width:68%;height:68%}
|
|
33
|
+
@media(forced-colors:active){:host{border-color:CanvasText}:host([appearance=ghost]),:host([appearance=tint]){color:CanvasText}:host([kind=presence]){background:Canvas;color:CanvasText;border-color:CanvasText}}
|
|
34
|
+
@media(prefers-reduced-motion:reduce){:host{transition:none}}`;
|
|
35
|
+
function presenceIcon(status, outOfOffice) {
|
|
36
|
+
const ns = "http://www.w3.org/2000/svg";
|
|
37
|
+
const svg = document.createElementNS(ns, "svg");
|
|
38
|
+
svg.setAttribute("viewBox", "0 0 16 16");
|
|
39
|
+
svg.setAttribute("aria-hidden", "true");
|
|
40
|
+
svg.setAttribute("focusable", "false");
|
|
41
|
+
const shape = (name, attributes) => {
|
|
42
|
+
const element = document.createElementNS(ns, name);
|
|
43
|
+
for (const [key, value] of Object.entries(attributes))
|
|
44
|
+
element.setAttribute(key, value);
|
|
45
|
+
svg.append(element);
|
|
46
|
+
};
|
|
47
|
+
const stroke = { fill: "none", stroke: "currentColor", "stroke-width": "1.6", "stroke-linecap": "round", "stroke-linejoin": "round" };
|
|
48
|
+
if (status === "available")
|
|
49
|
+
shape("path", { ...stroke, d: "M3.4 8.2 6.5 11.3 12.8 4.9" });
|
|
50
|
+
else if (status === "away") {
|
|
51
|
+
shape("circle", { ...stroke, cx: "8", cy: "8", r: "5.4" });
|
|
52
|
+
shape("path", { ...stroke, d: "M8 4.8V8l2.3 1.5" });
|
|
53
|
+
}
|
|
54
|
+
else if (status === "busy")
|
|
55
|
+
shape("circle", { cx: "8", cy: "8", r: "4.7", fill: "currentColor" });
|
|
56
|
+
else if (status === "do-not-disturb") {
|
|
57
|
+
shape("circle", { ...stroke, cx: "8", cy: "8", r: "5.4" });
|
|
58
|
+
shape("path", { ...stroke, d: "M5.2 8h5.6" });
|
|
59
|
+
}
|
|
60
|
+
else if (status === "offline")
|
|
61
|
+
shape("circle", { ...stroke, cx: "8", cy: "8", r: "5.1" });
|
|
62
|
+
else if (status === "blocked") {
|
|
63
|
+
shape("circle", { ...stroke, cx: "8", cy: "8", r: "5.2" });
|
|
64
|
+
shape("path", { ...stroke, d: "m4.3 11.7 7.4-7.4" });
|
|
65
|
+
}
|
|
66
|
+
else if (status === "out-of-office") {
|
|
67
|
+
shape("path", { ...stroke, d: "M3.5 13.2V4.5l4.5-2 4.5 2v8.7M2.5 13.2h11M6 6.2h.1M9.9 6.2h.1M7.2 13.1V9.8h1.6v3.3" });
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
shape("path", { ...stroke, d: "M5.8 5.7a2.3 2.3 0 1 1 3.8 1.7c-1 .8-1.6 1.1-1.6 2.3" });
|
|
71
|
+
shape("circle", { cx: "8", cy: "12.3", r: ".5", fill: "currentColor" });
|
|
72
|
+
}
|
|
73
|
+
if (outOfOffice && status !== "out-of-office") {
|
|
74
|
+
shape("circle", { cx: "12.1", cy: "12.1", r: "3.25", fill: "var(--spui-ui-surface,#fff)", stroke: "var(--spui-ui-danger,#b10e1c)", "stroke-width": ".75" });
|
|
75
|
+
shape("path", { d: "M10.7 13.4v-2.7h2.8v2.7m-1.9-2.7v.9m1-.9v.9", fill: "none", stroke: "var(--spui-ui-danger,#b10e1c)", "stroke-width": ".55", "stroke-linecap": "round", "stroke-linejoin": "round" });
|
|
76
|
+
}
|
|
77
|
+
return svg;
|
|
78
|
+
}
|
|
79
|
+
function renderBadge(host, shadow, options) {
|
|
80
|
+
const style = document.createElement("style");
|
|
81
|
+
style.textContent = badgeStyle;
|
|
82
|
+
const content = document.createElement("span");
|
|
83
|
+
content.className = "content";
|
|
84
|
+
content.part.add("content");
|
|
85
|
+
const defaultSlot = document.createElement("slot");
|
|
86
|
+
if (options.content !== undefined)
|
|
87
|
+
content.textContent = options.content;
|
|
88
|
+
else
|
|
89
|
+
content.append(defaultSlot);
|
|
90
|
+
const icon = document.createElement("span");
|
|
91
|
+
icon.className = "icon";
|
|
92
|
+
icon.part.add("icon");
|
|
93
|
+
const iconSlot = document.createElement("slot");
|
|
94
|
+
iconSlot.name = "icon";
|
|
95
|
+
if (options.icon && !host.querySelector('[slot="icon"]'))
|
|
96
|
+
icon.append(options.icon);
|
|
97
|
+
else
|
|
98
|
+
icon.append(iconSlot);
|
|
99
|
+
shadow.replaceChildren(style, icon, content);
|
|
100
|
+
if (options.presence) {
|
|
101
|
+
const status = host.getAttribute("status") ?? "available";
|
|
102
|
+
if (host.getAttribute("role") !== "img")
|
|
103
|
+
host.setAttribute("role", "img");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export function defineBadge() {
|
|
107
|
+
if (customElements.get("spui-badge"))
|
|
108
|
+
return;
|
|
109
|
+
class SpuiBadge extends HTMLElement {
|
|
110
|
+
static observedAttributes = ["appearance", "color", "icon-position", "shape", "size"];
|
|
111
|
+
connectedCallback() { if (!this.shadowRoot)
|
|
112
|
+
this.render(); }
|
|
113
|
+
attributeChangedCallback() { if (this.isConnected)
|
|
114
|
+
this.render(); }
|
|
115
|
+
render() { renderBadge(this, this.shadowRoot ?? this.attachShadow({ mode: "open" }), {}); }
|
|
116
|
+
}
|
|
117
|
+
customElements.define("spui-badge", SpuiBadge);
|
|
118
|
+
}
|
|
119
|
+
export function defineCounterBadge() {
|
|
120
|
+
if (customElements.get("spui-counter-badge"))
|
|
121
|
+
return;
|
|
122
|
+
class SpuiCounterBadge extends HTMLElement {
|
|
123
|
+
static observedAttributes = ["appearance", "aria-label", "color", "count", "dot", "overflow-count", "shape", "show-zero", "size"];
|
|
124
|
+
generatedLabel;
|
|
125
|
+
customLabel = false;
|
|
126
|
+
connectedCallback() { this.render(); }
|
|
127
|
+
attributeChangedCallback(name, _oldValue, newValue) {
|
|
128
|
+
if (name === "aria-label")
|
|
129
|
+
this.customLabel = newValue !== null && newValue !== this.generatedLabel;
|
|
130
|
+
if (this.isConnected)
|
|
131
|
+
this.render();
|
|
132
|
+
}
|
|
133
|
+
render() {
|
|
134
|
+
const parsedCount = Number(this.getAttribute("count") ?? 0);
|
|
135
|
+
const parsedMax = Number(this.getAttribute("overflow-count") ?? 99);
|
|
136
|
+
const count = Number.isFinite(parsedCount) ? Math.max(0, parsedCount) : 0;
|
|
137
|
+
const max = Number.isFinite(parsedMax) ? Math.max(0, parsedMax) : 99;
|
|
138
|
+
const dot = this.hasAttribute("dot");
|
|
139
|
+
const visible = dot || count > 0 || this.hasAttribute("show-zero");
|
|
140
|
+
this.toggleAttribute("hidden", !visible);
|
|
141
|
+
if (!dot && !this.customLabel) {
|
|
142
|
+
const label = `${count > max ? `${max}+` : count}`;
|
|
143
|
+
this.generatedLabel = label;
|
|
144
|
+
if (this.getAttribute("aria-label") !== label)
|
|
145
|
+
this.setAttribute("aria-label", label);
|
|
146
|
+
}
|
|
147
|
+
this.setAttribute("kind", "counter");
|
|
148
|
+
const content = dot ? undefined : String(count > max ? `${max}+` : count);
|
|
149
|
+
renderBadge(this, this.shadowRoot ?? this.attachShadow({ mode: "open" }), { content });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
customElements.define("spui-counter-badge", SpuiCounterBadge);
|
|
153
|
+
}
|
|
154
|
+
export function definePresenceBadge() {
|
|
155
|
+
if (customElements.get("spui-presence-badge"))
|
|
156
|
+
return;
|
|
157
|
+
class SpuiPresenceBadge extends HTMLElement {
|
|
158
|
+
static observedAttributes = ["out-of-office", "size", "status", "aria-label"];
|
|
159
|
+
generatedLabel;
|
|
160
|
+
customLabel = false;
|
|
161
|
+
connectedCallback() { this.render(); }
|
|
162
|
+
attributeChangedCallback(name, _oldValue, newValue) {
|
|
163
|
+
if (name === "aria-label")
|
|
164
|
+
this.customLabel = newValue !== null && newValue !== this.generatedLabel;
|
|
165
|
+
if (this.isConnected)
|
|
166
|
+
this.render();
|
|
167
|
+
}
|
|
168
|
+
render() {
|
|
169
|
+
this.setAttribute("kind", "presence");
|
|
170
|
+
const status = this.getAttribute("status") ?? "available";
|
|
171
|
+
if (!this.customLabel) {
|
|
172
|
+
const status = this.getAttribute("status") ?? "available";
|
|
173
|
+
const label = `${status.replaceAll("-", " ")}${this.hasAttribute("out-of-office") && status !== "out-of-office" ? ", out of office" : ""}`;
|
|
174
|
+
this.generatedLabel = label;
|
|
175
|
+
if (this.getAttribute("aria-label") !== label)
|
|
176
|
+
this.setAttribute("aria-label", label);
|
|
177
|
+
}
|
|
178
|
+
renderBadge(this, this.shadowRoot ?? this.attachShadow({ mode: "open" }), { icon: presenceIcon(status, this.hasAttribute("out-of-office")), presence: true });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
customElements.define("spui-presence-badge", SpuiPresenceBadge);
|
|
182
|
+
}
|