@nysds/nys-avatar 1.18.0 → 1.18.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.
- package/dist/nys-avatar.js +102 -93
- package/dist/nys-avatar.js.map +1 -1
- package/package.json +2 -2
package/dist/nys-avatar.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { LitElement as g, unsafeCSS as
|
|
2
|
-
import { property as
|
|
3
|
-
import { ifDefined as
|
|
1
|
+
import { LitElement as g, unsafeCSS as b, html as l } from "lit";
|
|
2
|
+
import { property as s, state as m } from "lit/decorators.js";
|
|
3
|
+
import { ifDefined as c } from "lit/directives/if-defined.js";
|
|
4
4
|
/*!
|
|
5
5
|
* █▄ █ █ █ █▀▀▀█ █▀▀▄ █▀▀▀█
|
|
6
6
|
* █ █ █ █▄▄▄█ ▀▀▀▄▄ █ █ ▀▀▀▄▄
|
|
7
7
|
* █ ▀█ █ █▄▄▄█ █▄▄▀ █▄▄▄█
|
|
8
8
|
*
|
|
9
|
-
* Avatar Component v1.18.
|
|
9
|
+
* Avatar Component v1.18.3
|
|
10
10
|
* Part of the New York State Design System
|
|
11
11
|
* Repository: https://github.com/its-hcd/nysds
|
|
12
12
|
* License: MIT
|
|
13
13
|
*/
|
|
14
|
-
const x = ":host{--_nys-avatar-border-radius: var(--nys-radius-round, 1776px);--_nys-avatar-size: var(--nys-avatar-size, var(--nys-font-size-6xl, 36px));--_nys-avatar-shape: var(--nys-radius-round, 1776px);--_nys-avatar-border-color: var(--nys-color-ink-reverse, #ffffff);--_nys-avatar-border-size: var(--nys-border-width-sm, 1px);--_nys-avatar-width: var(--nys-font-size-6xl, 36px);--_nys-avatar-color: var(--nys-color-theme, #154973);--_nys-avatar-background-color: var(--nys-color-theme-weaker, #eff6fb);--_nys-avatar-outline-color: var(--nys-color-focus, #004dd1);--_nys-avatar-outline-width: var(--nys-border-width-md, 2px);--_nys-avatar-outline-offset: var(--nys-space-2px, 2px)}.nys-avatar{display:inline-block}.nys-avatar__component{display:flex;justify-content:center;align-items:center;border-radius:var(--_nys-avatar-border-radius);width:var(--_nys-avatar-size);height:var(--_nys-avatar-size);font-size:var(--_nys-avatar-size);overflow:hidden;box-sizing:border-box;color:var(--_nys-avatar-color);background-color:var(--_nys-avatar-background-color);border:var(--_nys-avatar-border-size) solid var(--_nys-avatar-border-color);outline-offset:var(--_nys-avatar-outline-offset);transition:all .15s ease-in-out;-webkit-user-select:none;user-select:none}:host([interactive]) .nys-avatar__component:
|
|
15
|
-
var
|
|
16
|
-
for (var
|
|
17
|
-
(
|
|
18
|
-
return
|
|
14
|
+
const x = ":host{--_nys-avatar-border-radius: var(--nys-radius-round, 1776px);--_nys-avatar-size: var(--nys-avatar-size, var(--nys-font-size-6xl, 36px));--_nys-avatar-shape: var(--nys-radius-round, 1776px);--_nys-avatar-border-color: var(--nys-color-ink-reverse, #ffffff);--_nys-avatar-border-size: var(--nys-border-width-sm, 1px);--_nys-avatar-width: var(--nys-font-size-6xl, 36px);--_nys-avatar-color: var(--nys-color-theme, #154973);--_nys-avatar-background-color: var(--nys-color-theme-weaker, #eff6fb);--_nys-avatar-outline-color: var(--nys-color-focus, #004dd1);--_nys-avatar-outline-width: var(--nys-border-width-md, 2px);--_nys-avatar-outline-offset: var(--nys-space-2px, 2px)}.nys-avatar{display:inline-block}.nys-avatar__component{display:flex;justify-content:center;align-items:center;border-radius:var(--_nys-avatar-border-radius);width:var(--_nys-avatar-size);height:var(--_nys-avatar-size);font-size:var(--_nys-avatar-size);overflow:hidden;box-sizing:border-box;color:var(--_nys-avatar-color);background-color:var(--_nys-avatar-background-color);border:var(--_nys-avatar-border-size) solid var(--_nys-avatar-border-color);outline-offset:var(--_nys-avatar-outline-offset);transition:all .15s ease-in-out;-webkit-user-select:none;user-select:none}:host([interactive]:not([disabled])) button.nys-avatar__component{appearance:none;border:var(--_nys-avatar-border-size) solid var(--_nys-avatar-border-color);padding:0;cursor:pointer}:host([interactive]:not([disabled])) button.nys-avatar__component:hover{--_nys-avatar-background-color: var(--nys-color-theme-mid, #457aa5);--_nys-avatar-color: var(--nys-color-text-reverse, #ffffff)}:host([interactive]:not([disabled])) button.nys-avatar__component:active{--_nys-avatar-background-color: var(--nys-color-theme-strong, #0e324f);--_nys-avatar-color: var(--nys-color-text-reverse, #ffffff)}:host([interactive]:not([disabled])) button.nys-avatar__component:focus-visible{outline:solid var(--_nys-avatar-outline-width) var(--_nys-avatar-outline-color);outline-offset:var(--_nys-avatar-outline-offset)}:host([disabled]) .nys-avatar__component{--_nys-avatar-color: var(--nys-color-text-disabled, #bec0c1);--_nys-avatar-background-color: var(--nys-color-neutral-10, #f6f6f6);cursor:not-allowed}:host([disabled]) .nys-avatar__component:focus-within{outline:solid var(--_nys-avatar-outline-width) var(--_nys-avatar-outline-color)}div[part=nys-avatar__icon]{display:flex;align-items:center;justify-content:center}.nys-avatar__initials{display:flex;align-items:center;justify-content:center;width:100%;height:100%;font-size:calc(var(--_nys-avatar-width) * .5);font-weight:700;text-transform:uppercase}.nys-avatar__image{width:100%;height:100%;object-fit:cover}.nys-avatar__icon{width:100%;height:100%;fill:currentcolor}";
|
|
15
|
+
var $ = Object.defineProperty, r = (h, t, o, n) => {
|
|
16
|
+
for (var e = void 0, i = h.length - 1, v; i >= 0; i--)
|
|
17
|
+
(v = h[i]) && (e = v(t, o, e) || e);
|
|
18
|
+
return e && $(t, o, e), e;
|
|
19
19
|
};
|
|
20
20
|
let w = 0;
|
|
21
|
-
const
|
|
21
|
+
const y = class y extends g {
|
|
22
22
|
constructor() {
|
|
23
23
|
super(...arguments), this.id = "", this.ariaLabel = "", this.image = "", this.initials = "", this.icon = "", this.color = "", this.interactive = !1, this.disabled = !1, this.lazy = !1, this._slotHasContent = !1;
|
|
24
24
|
}
|
|
@@ -30,21 +30,29 @@ const d = class d extends g {
|
|
|
30
30
|
super.connectedCallback(), this.id || (this.id = `nys-avatar-${Date.now()}-${w++}`);
|
|
31
31
|
}
|
|
32
32
|
async _handleSlotChange() {
|
|
33
|
-
const
|
|
34
|
-
if (!
|
|
33
|
+
const t = this.shadowRoot?.querySelector("slot");
|
|
34
|
+
if (!t) {
|
|
35
35
|
this._slotHasContent = !1;
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
await Promise.resolve();
|
|
39
|
-
const
|
|
40
|
-
(
|
|
39
|
+
const o = t.assignedNodes({ flatten: !0 }).filter(
|
|
40
|
+
(n) => n.nodeType === Node.ELEMENT_NODE || n.nodeType === Node.TEXT_NODE && n.textContent?.trim()
|
|
41
41
|
);
|
|
42
|
-
this._slotHasContent =
|
|
42
|
+
this._slotHasContent = o.length > 0;
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
45
|
* Functions
|
|
46
46
|
* --------------------------------------------------------------------------
|
|
47
47
|
*/
|
|
48
|
+
get _cleanAriaLabel() {
|
|
49
|
+
return (this.ariaLabel ?? "").replace(/[\s\u00a0]+/g, " ").trim();
|
|
50
|
+
}
|
|
51
|
+
_colorStyle() {
|
|
52
|
+
if (!this.color) return "";
|
|
53
|
+
const t = this.getContrastForeground() ?? "";
|
|
54
|
+
return `--_nys-avatar-background-color: ${this.color}; --_nys-avatar-color: ${t}; color: ${t}`;
|
|
55
|
+
}
|
|
48
56
|
/**
|
|
49
57
|
* Computes the appropriate foreground color (icon or initials)
|
|
50
58
|
* based on the avatar's background color for sufficient contrast.
|
|
@@ -52,93 +60,94 @@ const d = class d extends g {
|
|
|
52
60
|
* @returns CSS color string for foreground
|
|
53
61
|
*/
|
|
54
62
|
getContrastForeground() {
|
|
55
|
-
const
|
|
63
|
+
const t = "var(--nys-color-ink, #000)", o = "var(--nys-color-ink-reverse, #fff)", n = "var(--nys-color-text, #000)", e = "var(--nys-color-text-reverse, #fff)";
|
|
56
64
|
if (!this.color) return;
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
document.body.removeChild(
|
|
61
|
-
const
|
|
62
|
-
if (!
|
|
63
|
-
const _ = Number(
|
|
64
|
-
return this.initials?.length > 0 ? f ?
|
|
65
|
+
const i = document.createElement("div");
|
|
66
|
+
i.style.color = this.color, document.body.appendChild(i);
|
|
67
|
+
const v = getComputedStyle(i).color;
|
|
68
|
+
document.body.removeChild(i);
|
|
69
|
+
const d = v.match(/\d+/g);
|
|
70
|
+
if (!d) return;
|
|
71
|
+
const _ = Number(d[0]), u = Number(d[1]), p = Number(d[2]), f = (0.299 * _ + 0.587 * u + 0.114 * p) / 255 < 0.5;
|
|
72
|
+
return this.initials?.length > 0 ? f ? e : n : f ? o : t;
|
|
65
73
|
}
|
|
66
74
|
render() {
|
|
75
|
+
const t = this._cleanAriaLabel, o = this._colorStyle(), n = this.image?.length > 0 ? l`<img
|
|
76
|
+
part="nys-avatar__image"
|
|
77
|
+
class="nys-avatar__image"
|
|
78
|
+
src=${this.image}
|
|
79
|
+
alt=${t || ""}
|
|
80
|
+
loading=${this.lazy ? "lazy" : "eager"}
|
|
81
|
+
/>` : this.initials?.length > 0 ? l`<span
|
|
82
|
+
part="nys-avatar__initials"
|
|
83
|
+
class="nys-avatar__initials"
|
|
84
|
+
aria-hidden="true"
|
|
85
|
+
>${this.initials}</span
|
|
86
|
+
>` : l`<div part="nys-avatar__icon">
|
|
87
|
+
<slot @slotchange=${this._handleSlotChange}></slot>
|
|
88
|
+
${this._slotHasContent ? null : l`<nys-icon
|
|
89
|
+
label="nys-avatar__icon"
|
|
90
|
+
name=${this.icon?.length > 0 ? this.icon : "account_circle"}
|
|
91
|
+
></nys-icon>`}
|
|
92
|
+
</div>`, e = this.interactive ? l`<button
|
|
93
|
+
part="nys-avatar"
|
|
94
|
+
class="nys-avatar__component"
|
|
95
|
+
style=${c(o || void 0)}
|
|
96
|
+
aria-label=${c(t || void 0)}
|
|
97
|
+
?disabled=${this.disabled}
|
|
98
|
+
>
|
|
99
|
+
${n}
|
|
100
|
+
</button>` : l`<div
|
|
101
|
+
part="nys-avatar"
|
|
102
|
+
class="nys-avatar__component"
|
|
103
|
+
style=${c(o || void 0)}
|
|
104
|
+
role=${c(this.image ? void 0 : t ? "img" : void 0)}
|
|
105
|
+
aria-label=${c(this.image ? void 0 : t || void 0)}
|
|
106
|
+
aria-hidden=${c(this.image || t ? void 0 : "true")}
|
|
107
|
+
>
|
|
108
|
+
${n}
|
|
109
|
+
</div>`;
|
|
67
110
|
return l`
|
|
68
111
|
<div class="nys-avatar">
|
|
69
|
-
<div class="nys-avatar__content">
|
|
70
|
-
<div
|
|
71
|
-
part="nys-avatar"
|
|
72
|
-
class="nys-avatar__component"
|
|
73
|
-
style=${this.color ? `--_nys-avatar-background-color: ${this.color}; color: ${this.getContrastForeground()}` : ""}
|
|
74
|
-
role=${y(
|
|
75
|
-
this.interactive ? "button" : this.image ? void 0 : "img"
|
|
76
|
-
)}
|
|
77
|
-
aria-label=${y(
|
|
78
|
-
this.image ? void 0 : this.ariaLabel ? this.ariaLabel : "avatar"
|
|
79
|
-
)}
|
|
80
|
-
tabindex=${y(
|
|
81
|
-
this.interactive && !this.disabled ? 0 : void 0
|
|
82
|
-
)}
|
|
83
|
-
>
|
|
84
|
-
${this.image?.length > 0 ? l`<img
|
|
85
|
-
part="nys-avatar__image"
|
|
86
|
-
class="nys-avatar__image"
|
|
87
|
-
src=${this.image}
|
|
88
|
-
alt=${this.ariaLabel || "avatar"}
|
|
89
|
-
loading=${this.lazy ? "lazy" : "eager"}
|
|
90
|
-
/>` : this.initials?.length > 0 ? l`<span
|
|
91
|
-
part="nys-avatar__initials"
|
|
92
|
-
class="nys-avatar__initials"
|
|
93
|
-
aria-hidden="true"
|
|
94
|
-
>${this.initials}</span
|
|
95
|
-
>` : l`<div part="nys-avatar__icon">
|
|
96
|
-
<slot @slotchange=${this._handleSlotChange}></slot>
|
|
97
|
-
${this._slotHasContent ? null : l`<nys-icon
|
|
98
|
-
label="nys-avatar__icon"
|
|
99
|
-
name=${this.icon?.length > 0 ? this.icon : "account_circle"}
|
|
100
|
-
></nys-icon>`}
|
|
101
|
-
</div>`}
|
|
102
|
-
</div>
|
|
103
|
-
</div>
|
|
112
|
+
<div class="nys-avatar__content">${e}</div>
|
|
104
113
|
</div>
|
|
105
114
|
`;
|
|
106
115
|
}
|
|
107
116
|
};
|
|
108
|
-
|
|
109
|
-
let
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
],
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
],
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
],
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
],
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
],
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
],
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
],
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
],
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
],
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
],
|
|
140
|
-
customElements.get("nys-avatar") || customElements.define("nys-avatar",
|
|
117
|
+
y.styles = b(x);
|
|
118
|
+
let a = y;
|
|
119
|
+
r([
|
|
120
|
+
s({ type: String, reflect: !0 })
|
|
121
|
+
], a.prototype, "id");
|
|
122
|
+
r([
|
|
123
|
+
s({ type: String })
|
|
124
|
+
], a.prototype, "ariaLabel");
|
|
125
|
+
r([
|
|
126
|
+
s({ type: String })
|
|
127
|
+
], a.prototype, "image");
|
|
128
|
+
r([
|
|
129
|
+
s({ type: String })
|
|
130
|
+
], a.prototype, "initials");
|
|
131
|
+
r([
|
|
132
|
+
s({ type: String })
|
|
133
|
+
], a.prototype, "icon");
|
|
134
|
+
r([
|
|
135
|
+
s({ type: String })
|
|
136
|
+
], a.prototype, "color");
|
|
137
|
+
r([
|
|
138
|
+
s({ type: Boolean, reflect: !0 })
|
|
139
|
+
], a.prototype, "interactive");
|
|
140
|
+
r([
|
|
141
|
+
s({ type: Boolean, reflect: !0 })
|
|
142
|
+
], a.prototype, "disabled");
|
|
143
|
+
r([
|
|
144
|
+
s({ type: Boolean, reflect: !0 })
|
|
145
|
+
], a.prototype, "lazy");
|
|
146
|
+
r([
|
|
147
|
+
m()
|
|
148
|
+
], a.prototype, "_slotHasContent");
|
|
149
|
+
customElements.get("nys-avatar") || customElements.define("nys-avatar", a);
|
|
141
150
|
export {
|
|
142
|
-
|
|
151
|
+
a as NysAvatar
|
|
143
152
|
};
|
|
144
153
|
//# sourceMappingURL=nys-avatar.js.map
|
package/dist/nys-avatar.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nys-avatar.js","sources":["../src/nys-avatar.ts"],"sourcesContent":["import { LitElement, html, unsafeCSS } from \"lit\";\nimport { property, state } from \"lit/decorators.js\";\nimport { ifDefined } from \"lit/directives/if-defined.js\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-avatar.scss?inline\";\n\nlet avatarIdCounter = 0;\n\n/**\n * Displays a user representation as image, initials, or icon with automatic fallback chain.\n *\n * Priority: `image` > `initials` > `icon` > default icon. Set `color` to customize background;\n * foreground auto-adjusts for contrast. Use `interactive` for clickable avatars (e.g., profile menus).\n *\n * @summary User avatar with image, initials, or icon fallback and contrast-aware colors.\n * @element nys-avatar\n *\n * @slot - Custom icon content. Overrides default icon when no image or initials.\n *\n * @example Image avatar\n * ```html\n * <nys-avatar image=\"/path/to/photo.jpg\" ariaLabel=\"Jane Smith\"></nys-avatar>\n * ```\n *\n * @example Initials avatar with custom color\n * ```html\n * <nys-avatar initials=\"JS\" color=\"var(--nys-color-theme)\" ariaLabel=\"Jane Smith\"></nys-avatar>\n * ```\n */\n\nexport class NysAvatar extends LitElement {\n static styles = unsafeCSS(styles);\n\n /** Unique identifier. Auto-generated if not provided. */\n @property({ type: String, reflect: true }) id = \"\";\n\n /** Accessible label for screen readers. Required when no image `alt` is available. */\n @property({ type: String }) ariaLabel = \"\";\n\n /** Image URL. Takes priority over initials and icon. */\n @property({ type: String }) image = \"\";\n\n /** 1-2 character initials. Used when no image is provided. */\n @property({ type: String }) initials = \"\";\n\n /** Custom icon name. Falls back to `account_circle` if not set. */\n @property({ type: String }) icon = \"\";\n\n /** Background color. Foreground auto-adjusts for contrast. Accepts CSS values or variables. */\n @property({ type: String }) color = \"\";\n\n /** Makes avatar clickable with button role and focus ring. */\n @property({ type: Boolean, reflect: true }) interactive = false;\n\n /** Prevents interaction when `interactive` is true. */\n @property({ type: Boolean, reflect: true }) disabled = false;\n\n /** Enables lazy loading for the image. */\n @property({ type: Boolean, reflect: true }) lazy = false;\n @state() private _slotHasContent = false;\n\n /**\n * Lifecycle methods\n * --------------------------------------------------------------------------\n */\n\n connectedCallback() {\n super.connectedCallback();\n if (!this.id) {\n this.id = `nys-avatar-${Date.now()}-${avatarIdCounter++}`;\n }\n }\n\n private async _handleSlotChange() {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>(\"slot\");\n if (!slot) {\n this._slotHasContent = false;\n return;\n }\n\n await Promise.resolve();\n\n const assignedNodes = slot\n .assignedNodes({ flatten: true })\n .filter(\n (node) =>\n node.nodeType === Node.ELEMENT_NODE ||\n (node.nodeType === Node.TEXT_NODE && node.textContent?.trim()),\n );\n\n this._slotHasContent = assignedNodes.length > 0;\n }\n\n /**\n * Functions\n * --------------------------------------------------------------------------\n */\n\n /**\n * Computes the appropriate foreground color (icon or initials)\n * based on the avatar's background color for sufficient contrast.\n *\n * @returns CSS color string for foreground\n */\n private getContrastForeground() {\n // Default NYSDS CSS vars for foreground.\n // Contrast must return =>\n // IF icon: \"--nys-color-ink-reverse\" or \"--nys-color-ink\"\n // If initials: \"--nys-color-text-reverse\" or \"--nys-color-text\"\n const fgIconDark = \"var(--nys-color-ink, #000)\";\n const fgIconLight = \"var(--nys-color-ink-reverse, #fff)\";\n const fgInitialDark = \"var(--nys-color-text, #000)\";\n const fgInitialLight = \"var(--nys-color-text-reverse, #fff)\";\n\n if (!this.color) return;\n\n // Create a temporary element to compute luminance (this is in case user pass in \"var(--nys-color-stuff)\")\n const div = document.createElement(\"div\");\n div.style.color = this.color;\n document.body.appendChild(div);\n const computedColor = getComputedStyle(div).color;\n document.body.removeChild(div);\n\n // Parse RGB\n const match = computedColor.match(/\\d+/g);\n if (!match) return;\n\n const r = Number(match[0]);\n const g = Number(match[1]);\n const b = Number(match[2]);\n\n // Calculate relative luminance\n const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n\n const isDark = luminance < 0.5;\n if (this.initials?.length > 0) {\n return isDark ? fgInitialLight : fgInitialDark;\n } else {\n return isDark ? fgIconLight : fgIconDark;\n }\n }\n\n render() {\n return html`\n <div class=\"nys-avatar\">\n <div class=\"nys-avatar__content\">\n <div\n part=\"nys-avatar\"\n class=\"nys-avatar__component\"\n style=${this.color\n ? `--_nys-avatar-background-color: ${this.color}; color: ${this.getContrastForeground()}`\n : \"\"}\n role=${ifDefined(\n this.interactive ? \"button\" : this.image ? undefined : \"img\",\n )}\n aria-label=${ifDefined(\n this.image\n ? undefined\n : this.ariaLabel\n ? this.ariaLabel\n : \"avatar\",\n )}\n tabindex=${ifDefined(\n this.interactive && !this.disabled ? 0 : undefined,\n )}\n >\n ${this.image?.length > 0\n ? html`<img\n part=\"nys-avatar__image\"\n class=\"nys-avatar__image\"\n src=${this.image}\n alt=${this.ariaLabel || \"avatar\"}\n loading=${this.lazy ? \"lazy\" : \"eager\"}\n />`\n : this.initials?.length > 0\n ? html`<span\n part=\"nys-avatar__initials\"\n class=\"nys-avatar__initials\"\n aria-hidden=\"true\"\n >${this.initials}</span\n >`\n : html`<div part=\"nys-avatar__icon\">\n <slot @slotchange=${this._handleSlotChange}></slot>\n ${!this._slotHasContent\n ? html`<nys-icon\n label=\"nys-avatar__icon\"\n name=${this.icon?.length > 0\n ? this.icon\n : \"account_circle\"}\n ></nys-icon>`\n : null}\n </div>`}\n </div>\n </div>\n </div>\n `;\n }\n}\n\nif (!customElements.get(\"nys-avatar\")) {\n customElements.define(\"nys-avatar\", NysAvatar);\n}\n"],"names":["avatarIdCounter","_NysAvatar","LitElement","slot","assignedNodes","node","fgIconDark","fgIconLight","fgInitialDark","fgInitialLight","div","computedColor","match","r","g","b","isDark","html","ifDefined","unsafeCSS","styles","NysAvatar","__decorateClass","property","state"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nys-avatar.js","sources":["../src/nys-avatar.ts"],"sourcesContent":["import { LitElement, html, unsafeCSS } from \"lit\";\nimport { property, state } from \"lit/decorators.js\";\nimport { ifDefined } from \"lit/directives/if-defined.js\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-avatar.scss?inline\";\n\nlet avatarIdCounter = 0;\n\n/**\n * Displays a user representation as image, initials, or icon with automatic fallback chain.\n *\n * Priority: `image` > `initials` > `icon` > default icon. Set `color` to customize background;\n * foreground auto-adjusts for contrast. Use `interactive` for clickable avatars (e.g., profile menus).\n *\n * @summary User avatar with image, initials, or icon fallback and contrast-aware colors.\n * @element nys-avatar\n *\n * @slot - Custom icon content. Overrides default icon when no image or initials.\n *\n * @example Image avatar\n * ```html\n * <nys-avatar image=\"/path/to/photo.jpg\" ariaLabel=\"Jane Smith\"></nys-avatar>\n * ```\n *\n * @example Initials avatar with custom color\n * ```html\n * <nys-avatar initials=\"JS\" color=\"var(--nys-color-theme)\" ariaLabel=\"Jane Smith\"></nys-avatar>\n * ```\n */\n\nexport class NysAvatar extends LitElement {\n static styles = unsafeCSS(styles);\n\n /** Unique identifier. Auto-generated if not provided. */\n @property({ type: String, reflect: true }) id = \"\";\n\n /** Accessible label for screen readers. Required when no image `alt` is available. */\n @property({ type: String }) ariaLabel = \"\";\n\n /** Image URL. Takes priority over initials and icon. */\n @property({ type: String }) image = \"\";\n\n /** 1-2 character initials. Used when no image is provided. */\n @property({ type: String }) initials = \"\";\n\n /** Custom icon name. Falls back to `account_circle` if not set. */\n @property({ type: String }) icon = \"\";\n\n /** Background color. Foreground auto-adjusts for contrast. Accepts CSS values or variables. */\n @property({ type: String }) color = \"\";\n\n /** Makes avatar clickable with button role and focus ring. */\n @property({ type: Boolean, reflect: true }) interactive = false;\n\n /** Prevents interaction when `interactive` is true. */\n @property({ type: Boolean, reflect: true }) disabled = false;\n\n /** Enables lazy loading for the image. */\n @property({ type: Boolean, reflect: true }) lazy = false;\n @state() private _slotHasContent = false;\n\n /**\n * Lifecycle methods\n * --------------------------------------------------------------------------\n */\n\n connectedCallback() {\n super.connectedCallback();\n if (!this.id) {\n this.id = `nys-avatar-${Date.now()}-${avatarIdCounter++}`;\n }\n }\n\n private async _handleSlotChange() {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>(\"slot\");\n if (!slot) {\n this._slotHasContent = false;\n return;\n }\n\n await Promise.resolve();\n\n const assignedNodes = slot\n .assignedNodes({ flatten: true })\n .filter(\n (node) =>\n node.nodeType === Node.ELEMENT_NODE ||\n (node.nodeType === Node.TEXT_NODE && node.textContent?.trim()),\n );\n\n this._slotHasContent = assignedNodes.length > 0;\n }\n\n /**\n * Functions\n * --------------------------------------------------------------------------\n */\n\n private get _cleanAriaLabel(): string {\n return (this.ariaLabel ?? \"\").replace(/[\\s\\u00a0]+/g, \" \").trim();\n }\n\n private _colorStyle(): string {\n if (!this.color) return \"\";\n const fg = this.getContrastForeground() ?? \"\";\n return `--_nys-avatar-background-color: ${this.color}; --_nys-avatar-color: ${fg}; color: ${fg}`;\n }\n\n /**\n * Computes the appropriate foreground color (icon or initials)\n * based on the avatar's background color for sufficient contrast.\n *\n * @returns CSS color string for foreground\n */\n private getContrastForeground() {\n // Default NYSDS CSS vars for foreground.\n // Contrast must return =>\n // IF icon: \"--nys-color-ink-reverse\" or \"--nys-color-ink\"\n // If initials: \"--nys-color-text-reverse\" or \"--nys-color-text\"\n const fgIconDark = \"var(--nys-color-ink, #000)\";\n const fgIconLight = \"var(--nys-color-ink-reverse, #fff)\";\n const fgInitialDark = \"var(--nys-color-text, #000)\";\n const fgInitialLight = \"var(--nys-color-text-reverse, #fff)\";\n\n if (!this.color) return;\n\n // Create a temporary element to compute luminance (this is in case user pass in \"var(--nys-color-stuff)\")\n const div = document.createElement(\"div\");\n div.style.color = this.color;\n document.body.appendChild(div);\n const computedColor = getComputedStyle(div).color;\n document.body.removeChild(div);\n\n // Parse RGB\n const match = computedColor.match(/\\d+/g);\n if (!match) return;\n\n const r = Number(match[0]);\n const g = Number(match[1]);\n const b = Number(match[2]);\n\n // Calculate relative luminance\n const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n\n const isDark = luminance < 0.5;\n if (this.initials?.length > 0) {\n return isDark ? fgInitialLight : fgInitialDark;\n } else {\n return isDark ? fgIconLight : fgIconDark;\n }\n }\n\n render() {\n const label = this._cleanAriaLabel;\n const colorStyle = this._colorStyle();\n\n const avatarContent =\n this.image?.length > 0\n ? html`<img\n part=\"nys-avatar__image\"\n class=\"nys-avatar__image\"\n src=${this.image}\n alt=${label || \"\"}\n loading=${this.lazy ? \"lazy\" : \"eager\"}\n />`\n : this.initials?.length > 0\n ? html`<span\n part=\"nys-avatar__initials\"\n class=\"nys-avatar__initials\"\n aria-hidden=\"true\"\n >${this.initials}</span\n >`\n : html`<div part=\"nys-avatar__icon\">\n <slot @slotchange=${this._handleSlotChange}></slot>\n ${!this._slotHasContent\n ? html`<nys-icon\n label=\"nys-avatar__icon\"\n name=${this.icon?.length > 0 ? this.icon : \"account_circle\"}\n ></nys-icon>`\n : null}\n </div>`;\n\n const container = this.interactive\n ? html`<button\n part=\"nys-avatar\"\n class=\"nys-avatar__component\"\n style=${ifDefined(colorStyle || undefined)}\n aria-label=${ifDefined(label || undefined)}\n ?disabled=${this.disabled}\n >\n ${avatarContent}\n </button>`\n : html`<div\n part=\"nys-avatar\"\n class=\"nys-avatar__component\"\n style=${ifDefined(colorStyle || undefined)}\n role=${ifDefined(this.image ? undefined : label ? \"img\" : undefined)}\n aria-label=${ifDefined(this.image ? undefined : label || undefined)}\n aria-hidden=${ifDefined(this.image || label ? undefined : \"true\")}\n >\n ${avatarContent}\n </div>`;\n\n return html`\n <div class=\"nys-avatar\">\n <div class=\"nys-avatar__content\">${container}</div>\n </div>\n `;\n }\n}\n\nif (!customElements.get(\"nys-avatar\")) {\n customElements.define(\"nys-avatar\", NysAvatar);\n}\n"],"names":["avatarIdCounter","_NysAvatar","LitElement","slot","assignedNodes","node","fg","fgIconDark","fgIconLight","fgInitialDark","fgInitialLight","div","computedColor","match","r","g","b","isDark","label","colorStyle","avatarContent","html","container","ifDefined","unsafeCSS","styles","NysAvatar","__decorateClass","property","state"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nysds/nys-avatar",
|
|
3
|
-
"version": "1.18.
|
|
3
|
+
"version": "1.18.3",
|
|
4
4
|
"description": "The Avatar component from the NYS Design System.",
|
|
5
5
|
"module": "dist/nys-avatar.js",
|
|
6
6
|
"exports": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"lit-analyze": "lit-analyzer '**/*.ts'"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@nysds/nys-icon": "^1.18.
|
|
25
|
+
"@nysds/nys-icon": "^1.18.3"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"lit": "^3.3.1",
|