@lablup/ui-common 0.1.0-alpha.17 → 0.1.0-alpha.19

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.
@@ -0,0 +1 @@
1
+ .select{display:inline-block;position:relative}.select--full-width{width:100%;display:block}.select--labelled{gap:var(--token-paddingXS);flex-direction:column;display:flex}.select__label{font-size:var(--token-fontSizeSM);color:var(--token-colorTextSecondary);font-weight:500}.select__trigger{justify-content:space-between;align-items:center;gap:var(--token-paddingSM);width:100%;min-height:var(--token-controlHeight);padding:var(--token-paddingXS) var(--token-padding);font-size:var(--token-fontSize);font-family:var(--token-fontFamily);color:var(--token-colorText);background-color:var(--token-colorBgContainer);border:1px solid var(--token-colorBorder);border-radius:var(--token-borderRadius);cursor:pointer;transition:all var(--token-motionDurationFast);text-align:left;display:flex}.select__trigger:hover:not(:disabled){border-color:var(--token-colorPrimary)}.select__trigger:focus{border-color:color-mix(in srgb, var(--token-colorPrimary) 70%, var(--token-colorText));box-shadow:0 0 0 2px var(--token-colorPrimaryBg);outline:none}.select--open .select__trigger{border-color:var(--token-colorPrimary);box-shadow:0 0 0 2px var(--token-colorPrimaryBg);border-bottom-right-radius:0;border-bottom-left-radius:0}.select--disabled .select__trigger{cursor:not-allowed;opacity:.6;background-color:var(--token-colorFillTertiary)}.select--small .select__trigger{min-height:var(--token-controlHeightSM);padding:var(--token-paddingXS) var(--token-paddingSM);font-size:var(--token-fontSizeSM)}.select--medium .select__trigger{min-height:var(--token-controlHeight);padding:var(--token-paddingXS) var(--token-padding);font-size:var(--token-fontSize)}.select--large .select__trigger{min-height:var(--token-controlHeightLG);padding:var(--token-paddingSM) var(--token-padding);font-size:var(--token-fontSize)}.select__trigger{min-width:120px}.select__value{align-items:center;gap:var(--token-paddingSM);flex:1;min-width:0;display:flex;overflow:hidden}.select__value-icon{color:var(--token-colorTextSecondary);flex-shrink:0;justify-content:center;align-items:center;display:flex}.select__value-label{text-overflow:ellipsis;white-space:nowrap;font-weight:500;overflow:hidden}.select__placeholder{color:var(--token-colorTextTertiary)}.select__chevron{color:var(--token-colorTextSecondary);transition:transform var(--token-motionDurationFast);flex-shrink:0;justify-content:center;align-items:center;display:flex}.select__chevron--open{transform:rotate(180deg)}.select__dropdown{z-index:var(--token-zIndexPopover);background:var(--token-colorBgElevated);border:1px solid var(--token-colorPrimary);border-radius:0 0 var(--token-borderRadius) var(--token-borderRadius);box-shadow:var(--token-boxShadowSecondary);border-top:none;flex-direction:column;max-height:240px;animation:.15s ease-out selectDropdownOpen;display:flex;position:absolute;top:100%;left:0;right:0;overflow:hidden}.select__dropdown--portal{border:1px solid var(--token-colorBorder);border-radius:var(--token-borderRadius);z-index:var(--token-zIndexTooltip);position:fixed}.select__search{padding:var(--token-paddingSM);background:var(--token-colorBgElevated);border-bottom:1px solid var(--token-colorBorderSecondary);flex-shrink:0}.select__search-input{box-sizing:border-box;width:100%;padding:var(--token-paddingXS) var(--token-paddingSM);font-family:var(--token-fontFamily);font-size:var(--token-fontSize);color:var(--token-colorText);background:var(--token-colorBgContainer);border:1px solid var(--token-colorBorder);border-radius:var(--token-borderRadiusSM);transition:border-color var(--token-motionDurationFast), box-shadow var(--token-motionDurationFast)}.select__search-input::placeholder{color:var(--token-colorTextPlaceholder)}.select__search-input:focus-visible{border-color:color-mix(in srgb, var(--token-colorPrimary) 70%, var(--token-colorText));box-shadow:0 0 0 2px var(--token-colorPrimaryBg);outline:none}.select__options{min-height:0;overflow-y:auto}@keyframes selectDropdownOpen{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.select__options::-webkit-scrollbar{width:var(--token-scrollbarSize);height:var(--token-scrollbarSize)}.select__options::-webkit-scrollbar-track{background:0 0}.select__options::-webkit-scrollbar-thumb{background-color:var(--token-colorBorder);border-radius:var(--token-scrollbarRadius)}.select__options::-webkit-scrollbar-thumb:hover{background-color:var(--token-colorTextTertiary)}.select__option{align-items:center;gap:var(--token-paddingSM);padding:var(--token-paddingSM) var(--token-padding);font-size:var(--token-fontSize);color:var(--token-colorText);cursor:pointer;transition:background-color var(--token-motionDurationFast);-webkit-user-select:none;user-select:none;display:flex}.select__option--disabled{color:var(--token-colorTextDisabled);cursor:not-allowed}.select__option:first-child{border-radius:0}.select__option:last-child{border-radius:0 0 var(--token-borderRadius) var(--token-borderRadius)}.select__option:hover:not(.select__option--disabled),.select__option--focused:not(.select__option--disabled){background-color:var(--token-colorFillSecondary)}.select__option--selected{background-color:var(--token-colorPrimaryBg)}.select__option--selected:hover,.select__option--selected.select__option--focused{background-color:var(--token-colorPrimaryBgHover)}.select__option-icon{width:20px;height:20px;color:var(--token-colorTextSecondary);flex-shrink:0;justify-content:center;align-items:center;display:flex}.select__option--selected .select__option-icon{color:var(--token-colorPrimary)}.select__option-content{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.select__option-label{font-weight:500}.select__option--selected .select__option-label{color:var(--token-colorPrimary)}.select__option-description{font-size:var(--token-fontSizeSM);color:var(--token-colorTextTertiary)}.select__option-check{color:var(--token-colorPrimary);flex-shrink:0;justify-content:center;align-items:center;display:flex}.select__empty{padding:var(--token-padding) var(--token-paddingMD);color:var(--token-colorTextTertiary);text-align:center;font-style:italic}.select--large .select__option{padding:var(--token-paddingSM) var(--token-paddingMD);font-size:var(--token-fontSize)}.select--large .select__option-icon{width:24px;height:24px}.select--large .select__option-description{font-size:var(--token-fontSizeSM)}
@@ -0,0 +1,38 @@
1
+ import { ReactNode } from 'react';
2
+ export interface SelectOption<T extends string = string> {
3
+ value: T;
4
+ label: string;
5
+ description?: string;
6
+ icon?: ReactNode;
7
+ /** Keep an unavailable choice visible while preventing its selection. */
8
+ disabled?: boolean;
9
+ }
10
+ export interface SelectProps<T extends string = string> {
11
+ value: T;
12
+ onChange: (value: T) => void;
13
+ onBlur?: () => void;
14
+ options: SelectOption<T>[];
15
+ /**
16
+ * Field label rendered above the trigger, the way `TextInput` renders its
17
+ * own. Names the trigger and the listbox through `aria-labelledby`, so a
18
+ * caller that supplies it does not also need `aria-label`.
19
+ */
20
+ label?: ReactNode;
21
+ placeholder?: string;
22
+ disabled?: boolean;
23
+ size?: "small" | "default" | "medium" | "large";
24
+ fullWidth?: boolean;
25
+ className?: string;
26
+ searchable?: boolean;
27
+ searchPlaceholder?: string;
28
+ "aria-label"?: string;
29
+ "aria-describedby"?: string;
30
+ invalid?: boolean;
31
+ /**
32
+ * Shown in place of the list when a search matches nothing. English default,
33
+ * because this package cannot resolve a consumer's locale key: a product that
34
+ * localises passes its own words.
35
+ */
36
+ noOptionsLabel?: string;
37
+ }
38
+ export declare function Select<T extends string = string>({ value, onChange, onBlur, options, label, placeholder, disabled, size, fullWidth, className, searchable, searchPlaceholder, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, invalid, noOptionsLabel, }: SelectProps<T>): import("react").JSX.Element;
@@ -0,0 +1,313 @@
1
+ /* empty css */
2
+ import { useCallback as e, useEffect as t, useId as n, useMemo as r, useRef as i, useState as a } from "react";
3
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
4
+ import { createPortal as c } from "react-dom";
5
+ //#region src/components/Select/Select.tsx
6
+ function l({ value: l, onChange: u, onBlur: d, options: f, label: p, placeholder: ee = "Select...", disabled: m = !1, size: h = "default", fullWidth: g = !1, className: _ = "", searchable: v = !1, searchPlaceholder: y, "aria-label": b, "aria-describedby": x, invalid: S = !1, noOptionsLabel: C = "No options" }) {
7
+ let [w, T] = a(!1), [E, D] = a(-1), [O, k] = a(""), [A, te] = a({
8
+ top: 0,
9
+ left: 0,
10
+ width: 0
11
+ }), j = i(null), M = i(null), N = i(null), P = i(null), F = i([]), I = n(), L = n(), R = n(), z = p != null && p !== !1, B = f.find((e) => e.value === l), V = r(() => {
12
+ let e = O.trim().toLowerCase();
13
+ return !v || e.length === 0 ? f : f.filter((t) => [
14
+ t.label,
15
+ t.value,
16
+ t.description ?? ""
17
+ ].some((t) => t.toLowerCase().includes(e)));
18
+ }, [
19
+ f,
20
+ O,
21
+ v
22
+ ]), H = e((e, t) => {
23
+ for (let n = e; n >= 0 && n < V.length; n += t) {
24
+ let e = V[n];
25
+ if (e && !e.disabled) return n;
26
+ }
27
+ return -1;
28
+ }, [V]), U = (e) => `${L}-option-${String(e)}`, W = E >= 0 ? U(E) : void 0, G = e(() => {
29
+ if (!M.current) return;
30
+ let e = M.current.getBoundingClientRect();
31
+ te({
32
+ top: e.bottom + 4,
33
+ left: e.left,
34
+ width: e.width
35
+ });
36
+ }, []), K = e(() => {
37
+ if (m) return;
38
+ G(), T(!0);
39
+ let e = V.findIndex((e) => e.value === l && !e.disabled);
40
+ D(e === -1 ? H(0, 1) : e);
41
+ }, [
42
+ m,
43
+ V,
44
+ l,
45
+ G,
46
+ H
47
+ ]), q = e(() => {
48
+ T(!1), D(-1), k(""), M.current?.focus();
49
+ }, []), J = e((e, t) => {
50
+ t && (t.stopPropagation(), t.preventDefault()), !e.disabled && (u(e.value), q());
51
+ }, [u, q]);
52
+ t(() => {
53
+ let e = (e) => {
54
+ if (!w) return;
55
+ let t = e.target, n = j.current && !j.current.contains(t), r = N.current && !N.current.contains(t);
56
+ n && r && q();
57
+ }, t = (e) => {
58
+ e.key === "Escape" && w && q();
59
+ };
60
+ return w && (document.addEventListener("mousedown", e), document.addEventListener("keydown", t)), () => {
61
+ document.removeEventListener("mousedown", e), document.removeEventListener("keydown", t);
62
+ };
63
+ }, [w, q]), t(() => {
64
+ if (!w) return;
65
+ let e = () => {
66
+ G();
67
+ };
68
+ return window.addEventListener("resize", e), window.addEventListener("scroll", e, !0), () => {
69
+ window.removeEventListener("resize", e), window.removeEventListener("scroll", e, !0);
70
+ };
71
+ }, [w, G]), t(() => {
72
+ w && v && P.current?.focus();
73
+ }, [w, v]), t(() => {
74
+ if (!w || !v) return;
75
+ F.current = [];
76
+ let e = V.findIndex((e) => e.value === l && !e.disabled);
77
+ D(e >= 0 ? e : H(0, 1));
78
+ }, [
79
+ w,
80
+ O,
81
+ v,
82
+ l,
83
+ V,
84
+ H
85
+ ]);
86
+ let Y = e((e) => {
87
+ if (!m) switch (e.key) {
88
+ case "Enter":
89
+ case " ":
90
+ if (e.preventDefault(), w) {
91
+ let e = V[E];
92
+ e && J(e);
93
+ } else K();
94
+ break;
95
+ case "Escape":
96
+ e.preventDefault(), w && q();
97
+ break;
98
+ case "ArrowDown":
99
+ if (e.preventDefault(), !w) K();
100
+ else {
101
+ let e = H(E + 1, 1);
102
+ e >= 0 && D(e);
103
+ }
104
+ break;
105
+ case "ArrowUp":
106
+ if (e.preventDefault(), w) {
107
+ let e = H(E - 1, -1);
108
+ e >= 0 && D(e);
109
+ }
110
+ break;
111
+ case "Tab": w && q();
112
+ }
113
+ }, [
114
+ m,
115
+ w,
116
+ E,
117
+ V,
118
+ H,
119
+ K,
120
+ q,
121
+ J
122
+ ]), X = e((e) => {
123
+ switch (e.key) {
124
+ case "Enter": {
125
+ e.preventDefault();
126
+ let t = V[E];
127
+ t && J(t);
128
+ break;
129
+ }
130
+ case "Escape":
131
+ e.preventDefault(), e.stopPropagation(), q();
132
+ break;
133
+ case "ArrowDown":
134
+ e.preventDefault(), D((e) => {
135
+ let t = H(e + 1, 1);
136
+ return t >= 0 ? t : e;
137
+ });
138
+ break;
139
+ case "ArrowUp":
140
+ e.preventDefault(), D((e) => {
141
+ let t = H(e - 1, -1);
142
+ return t >= 0 ? t : e;
143
+ });
144
+ break;
145
+ case "Tab": q();
146
+ }
147
+ }, [
148
+ q,
149
+ E,
150
+ J,
151
+ V,
152
+ H
153
+ ]);
154
+ t(() => {
155
+ let e = F.current[E];
156
+ w && E >= 0 && e && e.scrollIntoView({
157
+ block: "nearest",
158
+ behavior: "smooth"
159
+ });
160
+ }, [w, E]);
161
+ let Z = h === "default" ? "" : `select--${h}`, ne = g ? "select--full-width" : "", re = z ? "select--labelled" : "", Q = z ? R : void 0, $ = y ?? b;
162
+ return /* @__PURE__ */ s("div", {
163
+ ref: j,
164
+ className: `select ${Z} ${ne} ${re} ${w ? "select--open" : ""} ${m ? "select--disabled" : ""} ${_}`,
165
+ children: [
166
+ z && /* @__PURE__ */ o("span", {
167
+ className: "select__label",
168
+ id: R,
169
+ children: p
170
+ }),
171
+ /* @__PURE__ */ s("button", {
172
+ ref: M,
173
+ type: "button",
174
+ className: "select__trigger",
175
+ onClick: () => {
176
+ w ? q() : K();
177
+ },
178
+ onKeyDown: Y,
179
+ onBlur: d,
180
+ disabled: m,
181
+ "aria-haspopup": "listbox",
182
+ "aria-expanded": w,
183
+ "aria-controls": w ? I : void 0,
184
+ "aria-activedescendant": w ? W : void 0,
185
+ "aria-label": b,
186
+ "aria-labelledby": Q,
187
+ "aria-describedby": x,
188
+ "aria-invalid": S || void 0,
189
+ children: [B ? /* @__PURE__ */ s("span", {
190
+ className: "select__value",
191
+ children: [B.icon && /* @__PURE__ */ o("span", {
192
+ className: "select__value-icon",
193
+ children: B.icon
194
+ }), /* @__PURE__ */ o("span", {
195
+ className: "select__value-label",
196
+ children: B.label
197
+ })]
198
+ }) : /* @__PURE__ */ o("span", {
199
+ className: "select__placeholder",
200
+ children: ee
201
+ }), /* @__PURE__ */ o("span", {
202
+ className: `select__chevron ${w ? "select__chevron--open" : ""}`,
203
+ children: /* @__PURE__ */ o("svg", {
204
+ width: "10",
205
+ height: "6",
206
+ viewBox: "0 0 10 6",
207
+ fill: "none",
208
+ xmlns: "http://www.w3.org/2000/svg",
209
+ children: /* @__PURE__ */ o("path", {
210
+ d: "M1 1L5 5L9 1",
211
+ stroke: "currentColor",
212
+ strokeWidth: "1.5",
213
+ strokeLinecap: "round",
214
+ strokeLinejoin: "round"
215
+ })
216
+ })
217
+ })]
218
+ }),
219
+ w && c(/* @__PURE__ */ s("div", {
220
+ ref: N,
221
+ className: "select__dropdown select__dropdown--portal",
222
+ style: {
223
+ position: "fixed",
224
+ top: `${String(A.top)}px`,
225
+ left: `${String(A.left)}px`,
226
+ width: `${String(A.width)}px`
227
+ },
228
+ children: [v && /* @__PURE__ */ o("div", {
229
+ className: "select__search",
230
+ children: /* @__PURE__ */ o("input", {
231
+ ref: P,
232
+ className: "select__search-input",
233
+ type: "search",
234
+ value: O,
235
+ onChange: (e) => k(e.currentTarget.value),
236
+ onKeyDown: X,
237
+ placeholder: y,
238
+ "aria-label": $,
239
+ "aria-labelledby": $ ? void 0 : Q,
240
+ "aria-controls": I,
241
+ "aria-activedescendant": W,
242
+ autoComplete: "off",
243
+ spellCheck: !1
244
+ })
245
+ }), /* @__PURE__ */ o("div", {
246
+ className: "select__options",
247
+ role: "listbox",
248
+ id: I,
249
+ "aria-label": b,
250
+ "aria-labelledby": Q,
251
+ children: V.length === 0 ? /* @__PURE__ */ o("div", {
252
+ className: "select__empty",
253
+ children: C
254
+ }) : V.map((e, t) => /* @__PURE__ */ s("div", {
255
+ id: U(t),
256
+ ref: (e) => {
257
+ F.current[t] = e;
258
+ },
259
+ className: `select__option ${e.value === l ? "select__option--selected" : ""} ${t === E ? "select__option--focused" : ""} ${e.disabled ? "select__option--disabled" : ""}`,
260
+ role: "option",
261
+ "aria-selected": e.value === l,
262
+ "aria-disabled": e.disabled || void 0,
263
+ "aria-label": e.description ? e.label + ". " + e.description : void 0,
264
+ onClick: (t) => {
265
+ J(e, t);
266
+ },
267
+ onMouseEnter: () => {
268
+ e.disabled || D(t);
269
+ },
270
+ children: [
271
+ e.icon && /* @__PURE__ */ o("span", {
272
+ className: "select__option-icon",
273
+ children: e.icon
274
+ }),
275
+ /* @__PURE__ */ s("div", {
276
+ className: "select__option-content",
277
+ children: [/* @__PURE__ */ o("span", {
278
+ className: "select__option-label",
279
+ children: e.label
280
+ }), e.description && /* @__PURE__ */ o("span", {
281
+ className: "select__option-description",
282
+ children: e.description
283
+ })]
284
+ }),
285
+ e.value === l && /* @__PURE__ */ o("span", {
286
+ className: "select__option-check",
287
+ children: /* @__PURE__ */ o("svg", {
288
+ width: "14",
289
+ height: "10",
290
+ viewBox: "0 0 14 10",
291
+ fill: "none",
292
+ xmlns: "http://www.w3.org/2000/svg",
293
+ children: /* @__PURE__ */ o("path", {
294
+ d: "M1 5L5 9L13 1",
295
+ stroke: "currentColor",
296
+ strokeWidth: "2",
297
+ strokeLinecap: "round",
298
+ strokeLinejoin: "round"
299
+ })
300
+ })
301
+ })
302
+ ]
303
+ }, e.value))
304
+ })]
305
+ }), document.body)
306
+ ]
307
+ });
308
+ }
309
+ //#endregion
310
+ export { l as Select };
311
+
312
+ //# sourceMappingURL=Select2.js.map
313
+ import "../../assets/components/Select/Select.css";
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Select2.js","names":[],"sources":["../../../src/components/Select/Select.tsx"],"sourcesContent":["/**\n * Select Component\n *\n * Single-select listbox with an optional search filter, icons, descriptions and\n * a disabled-but-visible option state. The listbox is portalled, so a select\n * near the bottom of a scrolling panel is not clipped by it.\n *\n * Every string it renders arrives as a prop with an English default, so the\n * component resolves no locale key of its own.\n */\n\nimport { useState, useRef, useEffect, useCallback, useId, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport \"./Select.css\";\n\nexport interface SelectOption<T extends string = string> {\n value: T;\n label: string;\n description?: string;\n icon?: ReactNode;\n /** Keep an unavailable choice visible while preventing its selection. */\n disabled?: boolean;\n}\n\nexport interface SelectProps<T extends string = string> {\n value: T;\n onChange: (value: T) => void;\n onBlur?: () => void;\n options: SelectOption<T>[];\n /**\n * Field label rendered above the trigger, the way `TextInput` renders its\n * own. Names the trigger and the listbox through `aria-labelledby`, so a\n * caller that supplies it does not also need `aria-label`.\n */\n label?: ReactNode;\n placeholder?: string;\n disabled?: boolean;\n size?: \"small\" | \"default\" | \"medium\" | \"large\";\n fullWidth?: boolean;\n className?: string;\n searchable?: boolean;\n searchPlaceholder?: string;\n \"aria-label\"?: string;\n \"aria-describedby\"?: string;\n invalid?: boolean;\n /**\n * Shown in place of the list when a search matches nothing. English default,\n * because this package cannot resolve a consumer's locale key: a product that\n * localises passes its own words.\n */\n noOptionsLabel?: string;\n}\n\nexport function Select<T extends string = string>({\n value,\n onChange,\n onBlur,\n options,\n label,\n placeholder = \"Select...\",\n disabled = false,\n size = \"default\",\n fullWidth = false,\n className = \"\",\n searchable = false,\n searchPlaceholder,\n \"aria-label\": ariaLabel,\n \"aria-describedby\": ariaDescribedBy,\n invalid = false,\n noOptionsLabel = \"No options\",\n}: SelectProps<T>) {\n const [isOpen, setIsOpen] = useState(false);\n const [focusedIndex, setFocusedIndex] = useState(-1);\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [dropdownPosition, setDropdownPosition] = useState({\n top: 0,\n left: 0,\n width: 0,\n });\n\n const containerRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const dropdownRef = useRef<HTMLDivElement>(null);\n const searchInputRef = useRef<HTMLInputElement>(null);\n const optionsRef = useRef<(HTMLDivElement | null)[]>([]);\n const listboxId = useId();\n const optionIdPrefix = useId();\n const labelId = useId();\n const hasLabel = label !== undefined && label !== null && label !== false;\n\n // Find selected option\n const selectedOption = options.find((opt) => opt.value === value);\n const visibleOptions = useMemo(() => {\n const normalizedQuery = searchQuery.trim().toLowerCase();\n if (!searchable || normalizedQuery.length === 0) return options;\n return options.filter((option) =>\n [option.label, option.value, option.description ?? \"\"].some((candidate) =>\n candidate.toLowerCase().includes(normalizedQuery),\n ),\n );\n }, [options, searchQuery, searchable]);\n\n const findEnabledIndex = useCallback(\n (start: number, step: 1 | -1): number => {\n for (\n let index = start;\n index >= 0 && index < visibleOptions.length;\n index += step\n ) {\n const option = visibleOptions[index];\n if (option && !option.disabled) return index;\n }\n return -1;\n },\n [visibleOptions],\n );\n\n // Generate unique option ID\n const getOptionId = (index: number) => `${optionIdPrefix}-option-${String(index)}`;\n\n // Get active descendant ID for screen readers\n const activeDescendantId = focusedIndex >= 0 ? getOptionId(focusedIndex) : undefined;\n\n // Calculate dropdown position\n const updateDropdownPosition = useCallback(() => {\n if (!triggerRef.current) return;\n\n const rect = triggerRef.current.getBoundingClientRect();\n setDropdownPosition({\n top: rect.bottom + 4,\n left: rect.left,\n width: rect.width, // Use trigger width as minimum, but allow expansion\n });\n }, []);\n\n // Open dropdown\n const openDropdown = useCallback(() => {\n if (disabled) return;\n updateDropdownPosition();\n setIsOpen(true);\n const selectedIndex = visibleOptions.findIndex(\n (opt) => opt.value === value && !opt.disabled,\n );\n setFocusedIndex(selectedIndex !== -1 ? selectedIndex : findEnabledIndex(0, 1));\n }, [disabled, visibleOptions, value, updateDropdownPosition, findEnabledIndex]);\n\n // Close dropdown\n const closeDropdown = useCallback(() => {\n setIsOpen(false);\n setFocusedIndex(-1);\n setSearchQuery(\"\");\n triggerRef.current?.focus();\n }, []);\n\n // Handle option selection\n const selectOption = useCallback(\n (option: SelectOption<T>, e?: React.MouseEvent) => {\n if (e) {\n e.stopPropagation();\n e.preventDefault();\n }\n if (option.disabled) return;\n onChange(option.value);\n closeDropdown();\n },\n [onChange, closeDropdown],\n );\n\n // Handle click outside to close\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (!isOpen) return;\n const target = event.target as Node;\n // Check if click is outside both the container and the dropdown (which is portaled)\n const isOutsideContainer =\n containerRef.current && !containerRef.current.contains(target);\n const isOutsideDropdown =\n dropdownRef.current && !dropdownRef.current.contains(target);\n if (isOutsideContainer && isOutsideDropdown) {\n closeDropdown();\n }\n };\n\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\" && isOpen) {\n closeDropdown();\n }\n };\n\n if (isOpen) {\n document.addEventListener(\"mousedown\", handleClickOutside);\n document.addEventListener(\"keydown\", handleEscape);\n }\n\n return () => {\n document.removeEventListener(\"mousedown\", handleClickOutside);\n document.removeEventListener(\"keydown\", handleEscape);\n };\n }, [isOpen, closeDropdown]);\n\n // Handle window resize/scroll to update dropdown position\n useEffect(() => {\n if (!isOpen) return;\n\n const handlePositionUpdate = () => {\n updateDropdownPosition();\n };\n\n window.addEventListener(\"resize\", handlePositionUpdate);\n window.addEventListener(\"scroll\", handlePositionUpdate, true);\n\n return () => {\n window.removeEventListener(\"resize\", handlePositionUpdate);\n window.removeEventListener(\"scroll\", handlePositionUpdate, true);\n };\n }, [isOpen, updateDropdownPosition]);\n\n useEffect(() => {\n if (isOpen && searchable) {\n searchInputRef.current?.focus();\n }\n }, [isOpen, searchable]);\n\n useEffect(() => {\n if (!isOpen || !searchable) return;\n optionsRef.current = [];\n const selectedIndex = visibleOptions.findIndex(\n (option) => option.value === value && !option.disabled,\n );\n setFocusedIndex(selectedIndex >= 0 ? selectedIndex : findEnabledIndex(0, 1));\n }, [isOpen, searchQuery, searchable, value, visibleOptions, findEnabledIndex]);\n\n // Keyboard navigation\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (disabled) return;\n\n switch (e.key) {\n case \"Enter\":\n case \" \":\n e.preventDefault();\n if (isOpen) {\n const focusedOption = visibleOptions[focusedIndex];\n if (focusedOption) {\n selectOption(focusedOption);\n }\n } else {\n openDropdown();\n }\n break;\n\n case \"Escape\":\n e.preventDefault();\n if (isOpen) {\n closeDropdown();\n }\n break;\n\n case \"ArrowDown\":\n e.preventDefault();\n if (!isOpen) {\n openDropdown();\n } else {\n const nextIndex = findEnabledIndex(focusedIndex + 1, 1);\n if (nextIndex >= 0) setFocusedIndex(nextIndex);\n }\n break;\n\n case \"ArrowUp\":\n e.preventDefault();\n if (isOpen) {\n const previousIndex = findEnabledIndex(focusedIndex - 1, -1);\n if (previousIndex >= 0) setFocusedIndex(previousIndex);\n }\n break;\n\n case \"Tab\":\n if (isOpen) {\n closeDropdown();\n }\n break;\n }\n },\n [\n disabled,\n isOpen,\n focusedIndex,\n visibleOptions,\n findEnabledIndex,\n openDropdown,\n closeDropdown,\n selectOption,\n ],\n );\n\n const handleSearchKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLInputElement>) => {\n switch (event.key) {\n case \"Enter\": {\n event.preventDefault();\n const focusedOption = visibleOptions[focusedIndex];\n if (focusedOption) selectOption(focusedOption);\n break;\n }\n case \"Escape\":\n event.preventDefault();\n event.stopPropagation();\n closeDropdown();\n break;\n case \"ArrowDown\":\n event.preventDefault();\n setFocusedIndex((current) => {\n const nextIndex = findEnabledIndex(current + 1, 1);\n return nextIndex >= 0 ? nextIndex : current;\n });\n break;\n case \"ArrowUp\":\n event.preventDefault();\n setFocusedIndex((current) => {\n const previousIndex = findEnabledIndex(current - 1, -1);\n return previousIndex >= 0 ? previousIndex : current;\n });\n break;\n case \"Tab\":\n closeDropdown();\n break;\n }\n },\n [closeDropdown, focusedIndex, selectOption, visibleOptions, findEnabledIndex],\n );\n\n // Scroll focused option into view\n useEffect(() => {\n const focusedElement = optionsRef.current[focusedIndex];\n if (isOpen && focusedIndex >= 0 && focusedElement) {\n focusedElement.scrollIntoView({\n block: \"nearest\",\n behavior: \"smooth\",\n });\n }\n }, [isOpen, focusedIndex]);\n\n const sizeClass = size !== \"default\" ? `select--${size}` : \"\";\n const widthClass = fullWidth ? \"select--full-width\" : \"\";\n const labelledClass = hasLabel ? \"select--labelled\" : \"\";\n // aria-labelledby wins over aria-label in the accessible-name calculation,\n // so a caller may pass either; the rendered label is preferred when present.\n const labelledBy = hasLabel ? labelId : undefined;\n const searchFieldLabel = searchPlaceholder ?? ariaLabel;\n\n return (\n <div\n ref={containerRef}\n className={`select ${sizeClass} ${widthClass} ${labelledClass} ${isOpen ? \"select--open\" : \"\"} ${disabled ? \"select--disabled\" : \"\"} ${className}`}\n >\n {hasLabel && (\n <span className=\"select__label\" id={labelId}>\n {label}\n </span>\n )}\n <button\n ref={triggerRef}\n type=\"button\"\n className=\"select__trigger\"\n onClick={() => {\n if (isOpen) {\n closeDropdown();\n } else {\n openDropdown();\n }\n }}\n onKeyDown={handleKeyDown}\n onBlur={onBlur}\n disabled={disabled}\n aria-haspopup=\"listbox\"\n aria-expanded={isOpen}\n aria-controls={isOpen ? listboxId : undefined}\n aria-activedescendant={isOpen ? activeDescendantId : undefined}\n aria-label={ariaLabel}\n aria-labelledby={labelledBy}\n aria-describedby={ariaDescribedBy}\n aria-invalid={invalid || undefined}\n >\n {selectedOption ? (\n <span className=\"select__value\">\n {selectedOption.icon && (\n <span className=\"select__value-icon\">{selectedOption.icon}</span>\n )}\n <span className=\"select__value-label\">{selectedOption.label}</span>\n </span>\n ) : (\n <span className=\"select__placeholder\">{placeholder}</span>\n )}\n <span className={`select__chevron ${isOpen ? \"select__chevron--open\" : \"\"}`}>\n <svg\n width=\"10\"\n height=\"6\"\n viewBox=\"0 0 10 6\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M1 1L5 5L9 1\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </span>\n </button>\n\n {isOpen &&\n createPortal(\n <div\n ref={dropdownRef}\n className=\"select__dropdown select__dropdown--portal\"\n style={{\n position: \"fixed\",\n top: `${String(dropdownPosition.top)}px`,\n left: `${String(dropdownPosition.left)}px`,\n width: `${String(dropdownPosition.width)}px`,\n }}\n >\n {searchable && (\n <div className=\"select__search\">\n <input\n ref={searchInputRef}\n className=\"select__search-input\"\n type=\"search\"\n value={searchQuery}\n onChange={(event) => setSearchQuery(event.currentTarget.value)}\n onKeyDown={handleSearchKeyDown}\n placeholder={searchPlaceholder}\n aria-label={searchFieldLabel}\n aria-labelledby={searchFieldLabel ? undefined : labelledBy}\n aria-controls={listboxId}\n aria-activedescendant={activeDescendantId}\n autoComplete=\"off\"\n spellCheck={false}\n />\n </div>\n )}\n <div\n className=\"select__options\"\n role=\"listbox\"\n id={listboxId}\n aria-label={ariaLabel}\n aria-labelledby={labelledBy}\n >\n {visibleOptions.length === 0 ? (\n <div className=\"select__empty\">{noOptionsLabel}</div>\n ) : (\n visibleOptions.map((option, index) => (\n <div\n key={option.value}\n id={getOptionId(index)}\n ref={(el) => {\n optionsRef.current[index] = el;\n }}\n className={`select__option ${\n option.value === value ? \"select__option--selected\" : \"\"\n } ${index === focusedIndex ? \"select__option--focused\" : \"\"} ${option.disabled ? \"select__option--disabled\" : \"\"}`}\n role=\"option\"\n aria-selected={option.value === value}\n aria-disabled={option.disabled || undefined}\n aria-label={\n option.description\n ? option.label + \". \" + option.description\n : undefined\n }\n onClick={(e) => {\n selectOption(option, e);\n }}\n onMouseEnter={() => {\n if (!option.disabled) setFocusedIndex(index);\n }}\n >\n {option.icon && (\n <span className=\"select__option-icon\">{option.icon}</span>\n )}\n <div className=\"select__option-content\">\n <span className=\"select__option-label\">{option.label}</span>\n {option.description && (\n <span className=\"select__option-description\">\n {option.description}\n </span>\n )}\n </div>\n {option.value === value && (\n <span className=\"select__option-check\">\n <svg\n width=\"14\"\n height=\"10\"\n viewBox=\"0 0 14 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M1 5L5 9L13 1\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </span>\n )}\n </div>\n ))\n )}\n </div>\n </div>,\n document.body,\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { Select } from './Select';
2
+ export type { SelectOption, SelectProps } from './Select';
@@ -0,0 +1,2 @@
1
+ import { Select as e } from "./Select2.js";
2
+ export { e as Select };
@@ -11,6 +11,16 @@ export interface SkeletonProps {
11
11
  testId?: string;
12
12
  /** Accessible label announced while the skeleton is visible. Default: "Loading" */
13
13
  loadingLabel?: string;
14
+ /**
15
+ * Render as decoration: `aria-hidden`, with no role, no `aria-busy` and no
16
+ * label.
17
+ *
18
+ * For a shape inside a composite that announces the wait itself. `role="status"`
19
+ * is an implicit polite live region, so a composite that fills its own region
20
+ * with named primitives mounts one live region per shape and announces the
21
+ * same wait once per shape.
22
+ */
23
+ decorative?: boolean;
14
24
  }
15
25
  /**
16
26
  * Skeleton provides a loading placeholder with shimmer animation.
@@ -21,5 +31,5 @@ export interface SkeletonProps {
21
31
  * - Multiple shape variants
22
32
  * - Accessible via aria-busy and aria-label
23
33
  */
24
- export declare function Skeleton({ width, height, variant, className, testId, loadingLabel, }: SkeletonProps): import("react").JSX.Element;
34
+ export declare function Skeleton({ width, height, variant, className, testId, loadingLabel, decorative, }: SkeletonProps): import("react").JSX.Element;
25
35
  export default Skeleton;
@@ -1,21 +1,23 @@
1
1
  /* empty css */
2
2
  import { jsx as e } from "react/jsx-runtime";
3
3
  //#region src/components/Skeleton/Skeleton.tsx
4
- function t({ width: t = "100%", height: n = "20px", variant: r = "rect", className: i = "", testId: a, loadingLabel: o = "Loading" }) {
5
- let s = [
4
+ function t({ width: t = "100%", height: n = "20px", variant: r = "rect", className: i = "", testId: a, loadingLabel: o = "Loading", decorative: s = !1 }) {
5
+ let c = [
6
6
  "skeleton",
7
7
  `skeleton--${r}`,
8
8
  i
9
9
  ].filter(Boolean).join(" ");
10
10
  return /* @__PURE__ */ e("div", {
11
- className: s,
11
+ className: c,
12
12
  style: {
13
13
  width: t,
14
14
  height: n
15
15
  },
16
- role: "status",
17
- "aria-busy": "true",
18
- "aria-label": o,
16
+ ...s ? { "aria-hidden": !0 } : {
17
+ role: "status",
18
+ "aria-busy": !0,
19
+ "aria-label": o
20
+ },
19
21
  "data-testid": a,
20
22
  children: /* @__PURE__ */ e("span", { className: "skeleton__shimmer" })
21
23
  });
@@ -1 +1 @@
1
- {"version":3,"file":"Skeleton2.js","names":[],"sources":["../../../src/components/Skeleton/Skeleton.tsx"],"sourcesContent":["/**\n * Skeleton Component\n *\n * Base skeleton component for loading states.\n * Provides a shimmer animation effect that respects prefers-reduced-motion.\n *\n * @example\n * // Basic usage\n * <Skeleton width=\"100%\" height=\"20px\" />\n *\n * @example\n * // Circle skeleton\n * <Skeleton width=\"40px\" height=\"40px\" variant=\"circle\" />\n */\n\nimport \"./Skeleton.css\";\n\nexport interface SkeletonProps {\n /** Width of the skeleton (CSS value) */\n width?: string;\n /** Height of the skeleton (CSS value) */\n height?: string;\n /** Visual variant */\n variant?: \"rect\" | \"circle\" | \"text\";\n /** Additional CSS class names */\n className?: string;\n /** Test ID for testing */\n testId?: string;\n /** Accessible label announced while the skeleton is visible. Default: \"Loading\" */\n loadingLabel?: string;\n}\n\n/**\n * Skeleton provides a loading placeholder with shimmer animation.\n *\n * Features:\n * - Shimmer effect using CSS animation\n * - Reduced motion preference support\n * - Multiple shape variants\n * - Accessible via aria-busy and aria-label\n */\nexport function Skeleton({\n width = \"100%\",\n height = \"20px\",\n variant = \"rect\",\n className = \"\",\n testId,\n loadingLabel = \"Loading\",\n}: SkeletonProps) {\n const classNames = [\"skeleton\", `skeleton--${variant}`, className]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n className={classNames}\n style={{ width, height }}\n role=\"status\"\n aria-busy=\"true\"\n aria-label={loadingLabel}\n data-testid={testId}\n >\n <span className=\"skeleton__shimmer\" />\n </div>\n );\n}\n\nexport default Skeleton;\n"],"mappings":";;;AAyCA,SAAgB,EAAS,EACvB,WAAQ,QACR,YAAS,QACT,aAAU,QACV,eAAY,IACZ,WACA,kBAAe,aACC;CAChB,IAAM,IAAa;EAAC;EAAY,aAAa;EAAW;CAAS,CAAC,CAC/D,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CAEX,OACE,kBAAC,OAAD;EACE,WAAW;EACX,OAAO;GAAE;GAAO;EAAO;EACvB,MAAK;EACL,aAAU;EACV,cAAY;EACZ,eAAa;EAEb,UAAA,kBAAC,QAAD,EAAM,WAAU,oBAAqB,CAAA;CAClC,CAAA;AAET"}
1
+ {"version":3,"file":"Skeleton2.js","names":[],"sources":["../../../src/components/Skeleton/Skeleton.tsx"],"sourcesContent":["/**\n * Skeleton Component\n *\n * Base skeleton component for loading states.\n * Provides a shimmer animation effect that respects prefers-reduced-motion.\n *\n * @example\n * // Basic usage\n * <Skeleton width=\"100%\" height=\"20px\" />\n *\n * @example\n * // Circle skeleton\n * <Skeleton width=\"40px\" height=\"40px\" variant=\"circle\" />\n */\n\nimport \"./Skeleton.css\";\n\nexport interface SkeletonProps {\n /** Width of the skeleton (CSS value) */\n width?: string;\n /** Height of the skeleton (CSS value) */\n height?: string;\n /** Visual variant */\n variant?: \"rect\" | \"circle\" | \"text\";\n /** Additional CSS class names */\n className?: string;\n /** Test ID for testing */\n testId?: string;\n /** Accessible label announced while the skeleton is visible. Default: \"Loading\" */\n loadingLabel?: string;\n /**\n * Render as decoration: `aria-hidden`, with no role, no `aria-busy` and no\n * label.\n *\n * For a shape inside a composite that announces the wait itself. `role=\"status\"`\n * is an implicit polite live region, so a composite that fills its own region\n * with named primitives mounts one live region per shape and announces the\n * same wait once per shape.\n */\n decorative?: boolean;\n}\n\n/**\n * Skeleton provides a loading placeholder with shimmer animation.\n *\n * Features:\n * - Shimmer effect using CSS animation\n * - Reduced motion preference support\n * - Multiple shape variants\n * - Accessible via aria-busy and aria-label\n */\nexport function Skeleton({\n width = \"100%\",\n height = \"20px\",\n variant = \"rect\",\n className = \"\",\n testId,\n loadingLabel = \"Loading\",\n decorative = false,\n}: SkeletonProps) {\n const classNames = [\"skeleton\", `skeleton--${variant}`, className]\n .filter(Boolean)\n .join(\" \");\n\n // A decorative shape is one of several inside a composite that already\n // announces the wait. It keeps its size and its shimmer and leaves the\n // accessibility tree, so a card built from seven of these is one live region\n // rather than seven.\n const announcement = decorative\n ? ({ \"aria-hidden\": true } as const)\n : ({\n role: \"status\",\n \"aria-busy\": true,\n \"aria-label\": loadingLabel,\n } as const);\n\n return (\n <div\n className={classNames}\n style={{ width, height }}\n {...announcement}\n data-testid={testId}\n >\n <span className=\"skeleton__shimmer\" />\n </div>\n );\n}\n\nexport default Skeleton;\n"],"mappings":";;;AAmDA,SAAgB,EAAS,EACvB,WAAQ,QACR,YAAS,QACT,aAAU,QACV,eAAY,IACZ,WACA,kBAAe,WACf,gBAAa,MACG;CAChB,IAAM,IAAa;EAAC;EAAY,aAAa;EAAW;CAAS,CAAC,CAC/D,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CAcX,OACE,kBAAC,OAAD;EACE,WAAW;EACX,OAAO;GAAE;GAAO;EAAO;EACvB,GAZiB,IAChB,EAAE,eAAe,GAAK,IACtB;GACC,MAAM;GACN,aAAa;GACb,cAAc;EAChB;EAOA,eAAa;EAEb,UAAA,kBAAC,QAAD,EAAM,WAAU,oBAAqB,CAAA;CAClC,CAAA;AAET"}
@@ -5,7 +5,7 @@ export interface SkeletonCardProps {
5
5
  className?: string;
6
6
  /** Test ID for testing */
7
7
  testId?: string;
8
- /** Accessible name for each skeleton inside. Defaults to "Loading". */
8
+ /** Accessible name announced for the placeholder as a whole. Defaults to "Loading". */
9
9
  loadingLabel?: string;
10
10
  }
11
11
  /**
@@ -13,21 +13,22 @@ function i({ variant: i = "default", className: a = "", testId: o, loadingLabel:
13
13
  "data-testid": o,
14
14
  role: "status",
15
15
  "aria-busy": "true",
16
+ "aria-label": s ?? "Loading",
16
17
  children: i === "stat" ? /* @__PURE__ */ n(t, { children: /* @__PURE__ */ r("div", {
17
18
  className: "skeleton-card__header",
18
19
  children: [/* @__PURE__ */ n(e, {
19
- loadingLabel: s,
20
+ decorative: !0,
20
21
  width: "48px",
21
22
  height: "48px",
22
23
  variant: "circle"
23
24
  }), /* @__PURE__ */ r("div", {
24
25
  className: "skeleton-card__header-text",
25
26
  children: [/* @__PURE__ */ n(e, {
26
- loadingLabel: s,
27
+ decorative: !0,
27
28
  width: "60%",
28
29
  height: "16px"
29
30
  }), /* @__PURE__ */ n(e, {
30
- loadingLabel: s,
31
+ decorative: !0,
31
32
  width: "40%",
32
33
  height: "24px"
33
34
  })]
@@ -36,7 +37,7 @@ function i({ variant: i = "default", className: a = "", testId: o, loadingLabel:
36
37
  /* @__PURE__ */ n("div", {
37
38
  className: "skeleton-card__header",
38
39
  children: /* @__PURE__ */ n(e, {
39
- loadingLabel: s,
40
+ decorative: !0,
40
41
  width: "70%",
41
42
  height: "20px"
42
43
  })
@@ -45,17 +46,17 @@ function i({ variant: i = "default", className: a = "", testId: o, loadingLabel:
45
46
  className: "skeleton-card__content",
46
47
  children: [
47
48
  /* @__PURE__ */ n(e, {
48
- loadingLabel: s,
49
+ decorative: !0,
49
50
  width: "100%",
50
51
  height: "16px"
51
52
  }),
52
53
  /* @__PURE__ */ n(e, {
53
- loadingLabel: s,
54
+ decorative: !0,
54
55
  width: "90%",
55
56
  height: "16px"
56
57
  }),
57
58
  /* @__PURE__ */ n(e, {
58
- loadingLabel: s,
59
+ decorative: !0,
59
60
  width: "80%",
60
61
  height: "16px"
61
62
  })
@@ -64,11 +65,11 @@ function i({ variant: i = "default", className: a = "", testId: o, loadingLabel:
64
65
  i !== "compact" && /* @__PURE__ */ r("div", {
65
66
  className: "skeleton-card__footer",
66
67
  children: [/* @__PURE__ */ n(e, {
67
- loadingLabel: s,
68
+ decorative: !0,
68
69
  width: "80px",
69
70
  height: "32px"
70
71
  }), /* @__PURE__ */ n(e, {
71
- loadingLabel: s,
72
+ decorative: !0,
72
73
  width: "80px",
73
74
  height: "32px"
74
75
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"SkeletonCard2.js","names":[],"sources":["../../../src/components/Skeleton/SkeletonCard.tsx"],"sourcesContent":["/**\n * SkeletonCard Component\n *\n * Skeleton placeholder for card components (model cards, stat cards, etc.).\n * Mimics the structure of a typical card with header, content, and footer areas.\n *\n * @example\n * // Basic usage\n * <SkeletonCard />\n *\n * @example\n * // Compact variant\n * <SkeletonCard variant=\"compact\" />\n */\n\nimport { Skeleton } from \"./Skeleton\";\nimport \"./SkeletonCard.css\";\n\nexport interface SkeletonCardProps {\n /** Card size variant */\n variant?: \"default\" | \"compact\" | \"stat\";\n /** Additional CSS class names */\n className?: string;\n /** Test ID for testing */\n testId?: string;\n /** Accessible name for each skeleton inside. Defaults to \"Loading\". */\n loadingLabel?: string;\n}\n\n/**\n * SkeletonCard provides a loading placeholder for card components.\n *\n * Variants:\n * - default: Full card with header, content, and footer\n * - compact: Smaller card with reduced padding\n * - stat: Statistics card layout\n */\nexport function SkeletonCard({\n variant = \"default\",\n className = \"\",\n testId,\n loadingLabel,\n}: SkeletonCardProps) {\n const classNames = [\"skeleton-card\", `skeleton-card--${variant}`, className]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div className={classNames} data-testid={testId} role=\"status\" aria-busy=\"true\">\n {variant === \"stat\" ? (\n <>\n <div className=\"skeleton-card__header\">\n <Skeleton\n loadingLabel={loadingLabel}\n width=\"48px\"\n height=\"48px\"\n variant=\"circle\"\n />\n <div className=\"skeleton-card__header-text\">\n <Skeleton loadingLabel={loadingLabel} width=\"60%\" height=\"16px\" />\n <Skeleton loadingLabel={loadingLabel} width=\"40%\" height=\"24px\" />\n </div>\n </div>\n </>\n ) : (\n <>\n <div className=\"skeleton-card__header\">\n <Skeleton loadingLabel={loadingLabel} width=\"70%\" height=\"20px\" />\n </div>\n <div className=\"skeleton-card__content\">\n <Skeleton loadingLabel={loadingLabel} width=\"100%\" height=\"16px\" />\n <Skeleton loadingLabel={loadingLabel} width=\"90%\" height=\"16px\" />\n <Skeleton loadingLabel={loadingLabel} width=\"80%\" height=\"16px\" />\n </div>\n {variant !== \"compact\" && (\n <div className=\"skeleton-card__footer\">\n <Skeleton loadingLabel={loadingLabel} width=\"80px\" height=\"32px\" />\n <Skeleton loadingLabel={loadingLabel} width=\"80px\" height=\"32px\" />\n </div>\n )}\n </>\n )}\n </div>\n );\n}\n\nexport default SkeletonCard;\n"],"mappings":";;;;AAqCA,SAAgB,EAAa,EAC3B,aAAU,WACV,eAAY,IACZ,WACA,mBACoB;CACpB,IAAM,IAAa;EAAC;EAAiB,kBAAkB;EAAW;CAAS,CAAC,CACzE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CAEX,OACE,kBAAC,OAAD;EAAK,WAAW;EAAY,eAAa;EAAQ,MAAK;EAAS,aAAU;EACtE,UAAA,MAAY,SACX,kBAAA,GAAA,EAAA,UACE,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,kBAAC,GAAD;IACgB;IACd,OAAM;IACN,QAAO;IACP,SAAQ;GACT,CAAA,GACD,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,GAAD;KAAwB;KAAc,OAAM;KAAM,QAAO;IAAQ,CAAA,GACjE,kBAAC,GAAD;KAAwB;KAAc,OAAM;KAAM,QAAO;IAAQ,CAAA,CAC9D;GACF,CAAA,CAAA;EACL,CAAA,EAAA,CAAA,IAEF,kBAAA,GAAA,EAAA,UAAA;GACE,kBAAC,OAAD;IAAK,WAAU;IACb,UAAA,kBAAC,GAAD;KAAwB;KAAc,OAAM;KAAM,QAAO;IAAQ,CAAA;GAC9D,CAAA;GACL,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,kBAAC,GAAD;MAAwB;MAAc,OAAM;MAAO,QAAO;KAAQ,CAAA;KAClE,kBAAC,GAAD;MAAwB;MAAc,OAAM;MAAM,QAAO;KAAQ,CAAA;KACjE,kBAAC,GAAD;MAAwB;MAAc,OAAM;MAAM,QAAO;KAAQ,CAAA;IAC9D;;GACJ,MAAY,aACX,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,GAAD;KAAwB;KAAc,OAAM;KAAO,QAAO;IAAQ,CAAA,GAClE,kBAAC,GAAD;KAAwB;KAAc,OAAM;KAAO,QAAO;IAAQ,CAAA,CAC/D;;EAEP,EAAA,CAAA;CAED,CAAA;AAET"}
1
+ {"version":3,"file":"SkeletonCard2.js","names":[],"sources":["../../../src/components/Skeleton/SkeletonCard.tsx"],"sourcesContent":["/**\n * SkeletonCard Component\n *\n * Skeleton placeholder for card components (model cards, stat cards, etc.).\n * Mimics the structure of a typical card with header, content, and footer areas.\n *\n * @example\n * // Basic usage\n * <SkeletonCard />\n *\n * @example\n * // Compact variant\n * <SkeletonCard variant=\"compact\" />\n */\n\nimport { Skeleton } from \"./Skeleton\";\nimport \"./SkeletonCard.css\";\n\nexport interface SkeletonCardProps {\n /** Card size variant */\n variant?: \"default\" | \"compact\" | \"stat\";\n /** Additional CSS class names */\n className?: string;\n /** Test ID for testing */\n testId?: string;\n /** Accessible name announced for the placeholder as a whole. Defaults to \"Loading\". */\n loadingLabel?: string;\n}\n\n/**\n * SkeletonCard provides a loading placeholder for card components.\n *\n * Variants:\n * - default: Full card with header, content, and footer\n * - compact: Smaller card with reduced padding\n * - stat: Statistics card layout\n */\nexport function SkeletonCard({\n variant = \"default\",\n className = \"\",\n testId,\n loadingLabel,\n}: SkeletonCardProps) {\n const classNames = [\"skeleton-card\", `skeleton-card--${variant}`, className]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n className={classNames}\n data-testid={testId}\n role=\"status\"\n aria-busy=\"true\"\n aria-label={loadingLabel ?? \"Loading\"}\n >\n {variant === \"stat\" ? (\n <>\n <div className=\"skeleton-card__header\">\n <Skeleton decorative width=\"48px\" height=\"48px\" variant=\"circle\" />\n <div className=\"skeleton-card__header-text\">\n <Skeleton decorative width=\"60%\" height=\"16px\" />\n <Skeleton decorative width=\"40%\" height=\"24px\" />\n </div>\n </div>\n </>\n ) : (\n <>\n <div className=\"skeleton-card__header\">\n <Skeleton decorative width=\"70%\" height=\"20px\" />\n </div>\n <div className=\"skeleton-card__content\">\n <Skeleton decorative width=\"100%\" height=\"16px\" />\n <Skeleton decorative width=\"90%\" height=\"16px\" />\n <Skeleton decorative width=\"80%\" height=\"16px\" />\n </div>\n {variant !== \"compact\" && (\n <div className=\"skeleton-card__footer\">\n <Skeleton decorative width=\"80px\" height=\"32px\" />\n <Skeleton decorative width=\"80px\" height=\"32px\" />\n </div>\n )}\n </>\n )}\n </div>\n );\n}\n\nexport default SkeletonCard;\n"],"mappings":";;;;AAqCA,SAAgB,EAAa,EAC3B,aAAU,WACV,eAAY,IACZ,WACA,mBACoB;CACpB,IAAM,IAAa;EAAC;EAAiB,kBAAkB;EAAW;CAAS,CAAC,CACzE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CAEX,OACE,kBAAC,OAAD;EACE,WAAW;EACX,eAAa;EACb,MAAK;EACL,aAAU;EACV,cAAY,KAAgB;EAE3B,UAAA,MAAY,SACX,kBAAA,GAAA,EAAA,UACE,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,kBAAC,GAAD;IAAU,YAAA;IAAW,OAAM;IAAO,QAAO;IAAO,SAAQ;GAAU,CAAA,GAClE,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,GAAD;KAAU,YAAA;KAAW,OAAM;KAAM,QAAO;IAAQ,CAAA,GAChD,kBAAC,GAAD;KAAU,YAAA;KAAW,OAAM;KAAM,QAAO;IAAQ,CAAA,CAC7C;GACF,CAAA,CAAA;EACL,CAAA,EAAA,CAAA,IAEF,kBAAA,GAAA,EAAA,UAAA;GACE,kBAAC,OAAD;IAAK,WAAU;IACb,UAAA,kBAAC,GAAD;KAAU,YAAA;KAAW,OAAM;KAAM,QAAO;IAAQ,CAAA;GAC7C,CAAA;GACL,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,kBAAC,GAAD;MAAU,YAAA;MAAW,OAAM;MAAO,QAAO;KAAQ,CAAA;KACjD,kBAAC,GAAD;MAAU,YAAA;MAAW,OAAM;MAAM,QAAO;KAAQ,CAAA;KAChD,kBAAC,GAAD;MAAU,YAAA;MAAW,OAAM;MAAM,QAAO;KAAQ,CAAA;IAC7C;;GACJ,MAAY,aACX,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,GAAD;KAAU,YAAA;KAAW,OAAM;KAAO,QAAO;IAAQ,CAAA,GACjD,kBAAC,GAAD;KAAU,YAAA;KAAW,OAAM;KAAO,QAAO;IAAQ,CAAA,CAC9C;;EAEP,EAAA,CAAA;CAED,CAAA;AAET"}
@@ -18,6 +18,7 @@ function r({ variant: r = "bar", height: i = "300px", className: a = "", testId:
18
18
  children: r === "pie" ? /* @__PURE__ */ n("div", {
19
19
  className: "skeleton-chart__pie",
20
20
  children: [/* @__PURE__ */ t(e, {
21
+ decorative: !0,
21
22
  width: "100%",
22
23
  height: "100%",
23
24
  variant: "circle"
@@ -26,10 +27,12 @@ function r({ variant: r = "bar", height: i = "300px", className: a = "", testId:
26
27
  children: Array.from({ length: 4 }).map((r, i) => /* @__PURE__ */ n("div", {
27
28
  className: "skeleton-chart__legend-item",
28
29
  children: [/* @__PURE__ */ t(e, {
30
+ decorative: !0,
29
31
  width: "16px",
30
32
  height: "16px",
31
33
  variant: "circle"
32
34
  }), /* @__PURE__ */ t(e, {
35
+ decorative: !0,
33
36
  width: "80px",
34
37
  height: "14px"
35
38
  })]
@@ -47,6 +50,7 @@ function r({ variant: r = "bar", height: i = "300px", className: a = "", testId:
47
50
  85,
48
51
  65
49
52
  ].map((n, r) => /* @__PURE__ */ t(e, {
53
+ decorative: !0,
50
54
  width: "100%",
51
55
  height: `${String(n)}%`,
52
56
  className: "skeleton-chart__bar"
@@ -54,6 +58,7 @@ function r({ variant: r = "bar", height: i = "300px", className: a = "", testId:
54
58
  }) : /* @__PURE__ */ t("div", {
55
59
  className: "skeleton-chart__line",
56
60
  children: /* @__PURE__ */ t(e, {
61
+ decorative: !0,
57
62
  width: "100%",
58
63
  height: "100%"
59
64
  })
@@ -1 +1 @@
1
- {"version":3,"file":"SkeletonChart2.js","names":[],"sources":["../../../src/components/Skeleton/SkeletonChart.tsx"],"sourcesContent":["/**\n * SkeletonChart Component\n *\n * Skeleton placeholder for chart components.\n * Shows a simplified chart-like shape with bars or circular elements.\n *\n * @example\n * // Bar chart skeleton\n * <SkeletonChart variant=\"bar\" />\n *\n * @example\n * // Pie chart skeleton\n * <SkeletonChart variant=\"pie\" />\n */\n\nimport { Skeleton } from \"./Skeleton\";\nimport \"./SkeletonChart.css\";\n\nexport interface SkeletonChartProps {\n /** Chart type variant */\n variant?: \"bar\" | \"line\" | \"pie\" | \"area\";\n /** Chart height */\n height?: string;\n /** Additional CSS class names */\n className?: string;\n /** Test ID for testing */\n testId?: string;\n /** Accessible label announced while the chart is loading. Default: \"Loading chart\" */\n loadingLabel?: string;\n}\n\n/**\n * SkeletonChart provides a loading placeholder for chart components.\n *\n * Variants:\n * - bar: Vertical bars of varying heights\n * - line: Line graph placeholder\n * - pie: Circular chart placeholder\n * - area: Area chart placeholder\n */\nexport function SkeletonChart({\n variant = \"bar\",\n height = \"300px\",\n className = \"\",\n testId,\n loadingLabel = \"Loading chart\",\n}: SkeletonChartProps) {\n const classNames = [\"skeleton-chart\", `skeleton-chart--${variant}`, className]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n className={classNames}\n style={{ height }}\n data-testid={testId}\n role=\"status\"\n aria-busy=\"true\"\n aria-label={loadingLabel}\n >\n {variant === \"pie\" ? (\n <div className=\"skeleton-chart__pie\">\n <Skeleton width=\"100%\" height=\"100%\" variant=\"circle\" />\n <div className=\"skeleton-chart__legend\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"skeleton-chart__legend-item\">\n <Skeleton width=\"16px\" height=\"16px\" variant=\"circle\" />\n <Skeleton width=\"80px\" height=\"14px\" />\n </div>\n ))}\n </div>\n </div>\n ) : variant === \"bar\" ? (\n <div className=\"skeleton-chart__bars\">\n {[60, 80, 45, 90, 70, 55, 85, 65].map((barHeight, i) => (\n <Skeleton\n key={i}\n width=\"100%\"\n height={`${String(barHeight)}%`}\n className=\"skeleton-chart__bar\"\n />\n ))}\n </div>\n ) : (\n <div className=\"skeleton-chart__line\">\n <Skeleton width=\"100%\" height=\"100%\" />\n </div>\n )}\n </div>\n );\n}\n\nexport default SkeletonChart;\n"],"mappings":";;;;AAwCA,SAAgB,EAAc,EAC5B,aAAU,OACV,YAAS,SACT,eAAY,IACZ,WACA,kBAAe,mBACM;CACrB,IAAM,IAAa;EAAC;EAAkB,mBAAmB;EAAW;CAAS,CAAC,CAC3E,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CAEX,OACE,kBAAC,OAAD;EACE,WAAW;EACX,OAAO,EAAE,UAAO;EAChB,eAAa;EACb,MAAK;EACL,aAAU;EACV,cAAY;EAEX,UAAA,MAAY,QACX,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,kBAAC,GAAD;IAAU,OAAM;IAAO,QAAO;IAAO,SAAQ;GAAU,CAAA,GACvD,kBAAC,OAAD;IAAK,WAAU;IACZ,UAAA,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,OAAD;KAAa,WAAU;KAAvB,UAAA,CACE,kBAAC,GAAD;MAAU,OAAM;MAAO,QAAO;MAAO,SAAQ;KAAU,CAAA,GACvD,kBAAC,GAAD;MAAU,OAAM;MAAO,QAAO;KAAQ,CAAA,CACnC;IAHK,GAAA,CAGL,CACN;GACE,CAAA,CACF;EACH,CAAA,IAAA,MAAY,QACd,kBAAC,OAAD;GAAK,WAAU;GACZ,UAAA;IAAC;IAAI;IAAI;IAAI;IAAI;IAAI;IAAI;IAAI;GAAE,CAAC,CAAC,KAAK,GAAW,MAChD,kBAAC,GAAD;IAEE,OAAM;IACN,QAAQ,GAAG,OAAO,CAAS,EAAE;IAC7B,WAAU;GACX,GAJM,CAIN,CACF;EACE,CAAA,IAEL,kBAAC,OAAD;GAAK,WAAU;GACb,UAAA,kBAAC,GAAD;IAAU,OAAM;IAAO,QAAO;GAAQ,CAAA;EACnC,CAAA;CAEJ,CAAA;AAET"}
1
+ {"version":3,"file":"SkeletonChart2.js","names":[],"sources":["../../../src/components/Skeleton/SkeletonChart.tsx"],"sourcesContent":["/**\n * SkeletonChart Component\n *\n * Skeleton placeholder for chart components.\n * Shows a simplified chart-like shape with bars or circular elements.\n *\n * @example\n * // Bar chart skeleton\n * <SkeletonChart variant=\"bar\" />\n *\n * @example\n * // Pie chart skeleton\n * <SkeletonChart variant=\"pie\" />\n */\n\nimport { Skeleton } from \"./Skeleton\";\nimport \"./SkeletonChart.css\";\n\nexport interface SkeletonChartProps {\n /** Chart type variant */\n variant?: \"bar\" | \"line\" | \"pie\" | \"area\";\n /** Chart height */\n height?: string;\n /** Additional CSS class names */\n className?: string;\n /** Test ID for testing */\n testId?: string;\n /** Accessible label announced while the chart is loading. Default: \"Loading chart\" */\n loadingLabel?: string;\n}\n\n/**\n * SkeletonChart provides a loading placeholder for chart components.\n *\n * Variants:\n * - bar: Vertical bars of varying heights\n * - line: Line graph placeholder\n * - pie: Circular chart placeholder\n * - area: Area chart placeholder\n */\nexport function SkeletonChart({\n variant = \"bar\",\n height = \"300px\",\n className = \"\",\n testId,\n loadingLabel = \"Loading chart\",\n}: SkeletonChartProps) {\n const classNames = [\"skeleton-chart\", `skeleton-chart--${variant}`, className]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n className={classNames}\n style={{ height }}\n data-testid={testId}\n role=\"status\"\n aria-busy=\"true\"\n aria-label={loadingLabel}\n >\n {variant === \"pie\" ? (\n <div className=\"skeleton-chart__pie\">\n <Skeleton decorative width=\"100%\" height=\"100%\" variant=\"circle\" />\n <div className=\"skeleton-chart__legend\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"skeleton-chart__legend-item\">\n <Skeleton decorative width=\"16px\" height=\"16px\" variant=\"circle\" />\n <Skeleton decorative width=\"80px\" height=\"14px\" />\n </div>\n ))}\n </div>\n </div>\n ) : variant === \"bar\" ? (\n <div className=\"skeleton-chart__bars\">\n {[60, 80, 45, 90, 70, 55, 85, 65].map((barHeight, i) => (\n <Skeleton\n decorative\n key={i}\n width=\"100%\"\n height={`${String(barHeight)}%`}\n className=\"skeleton-chart__bar\"\n />\n ))}\n </div>\n ) : (\n <div className=\"skeleton-chart__line\">\n <Skeleton decorative width=\"100%\" height=\"100%\" />\n </div>\n )}\n </div>\n );\n}\n\nexport default SkeletonChart;\n"],"mappings":";;;;AAwCA,SAAgB,EAAc,EAC5B,aAAU,OACV,YAAS,SACT,eAAY,IACZ,WACA,kBAAe,mBACM;CACrB,IAAM,IAAa;EAAC;EAAkB,mBAAmB;EAAW;CAAS,CAAC,CAC3E,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CAEX,OACE,kBAAC,OAAD;EACE,WAAW;EACX,OAAO,EAAE,UAAO;EAChB,eAAa;EACb,MAAK;EACL,aAAU;EACV,cAAY;EAEX,UAAA,MAAY,QACX,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,kBAAC,GAAD;IAAU,YAAA;IAAW,OAAM;IAAO,QAAO;IAAO,SAAQ;GAAU,CAAA,GAClE,kBAAC,OAAD;IAAK,WAAU;IACZ,UAAA,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,OAAD;KAAa,WAAU;KAAvB,UAAA,CACE,kBAAC,GAAD;MAAU,YAAA;MAAW,OAAM;MAAO,QAAO;MAAO,SAAQ;KAAU,CAAA,GAClE,kBAAC,GAAD;MAAU,YAAA;MAAW,OAAM;MAAO,QAAO;KAAQ,CAAA,CAC9C;IAHK,GAAA,CAGL,CACN;GACE,CAAA,CACF;EACH,CAAA,IAAA,MAAY,QACd,kBAAC,OAAD;GAAK,WAAU;GACZ,UAAA;IAAC;IAAI;IAAI;IAAI;IAAI;IAAI;IAAI;IAAI;GAAE,CAAC,CAAC,KAAK,GAAW,MAChD,kBAAC,GAAD;IACE,YAAA;IAEA,OAAM;IACN,QAAQ,GAAG,OAAO,CAAS,EAAE;IAC7B,WAAU;GACX,GAJM,CAIN,CACF;EACE,CAAA,IAEL,kBAAC,OAAD;GAAK,WAAU;GACb,UAAA,kBAAC,GAAD;IAAU,YAAA;IAAW,OAAM;IAAO,QAAO;GAAQ,CAAA;EAC9C,CAAA;CAEJ,CAAA;AAET"}
@@ -9,7 +9,7 @@ export interface SkeletonRowProps {
9
9
  className?: string;
10
10
  /** Test ID for testing */
11
11
  testId?: string;
12
- /** Accessible name for each skeleton inside. Defaults to "Loading". */
12
+ /** Accessible name announced for the placeholder as a whole. Defaults to "Loading". */
13
13
  loadingLabel?: string;
14
14
  }
15
15
  /**
@@ -9,11 +9,12 @@ function i({ showAvatar: i = !1, showActions: a = !1, count: o = 1, className: s
9
9
  "data-testid": c ? `${c}-${String(o)}` : void 0,
10
10
  role: "status",
11
11
  "aria-busy": "true",
12
+ "aria-label": l ?? "Loading",
12
13
  children: [
13
14
  i && /* @__PURE__ */ n("div", {
14
15
  className: "skeleton-row__avatar",
15
16
  children: /* @__PURE__ */ n(e, {
16
- loadingLabel: l,
17
+ decorative: !0,
17
18
  width: "40px",
18
19
  height: "40px",
19
20
  variant: "circle"
@@ -22,11 +23,11 @@ function i({ showAvatar: i = !1, showActions: a = !1, count: o = 1, className: s
22
23
  /* @__PURE__ */ r("div", {
23
24
  className: "skeleton-row__content",
24
25
  children: [/* @__PURE__ */ n(e, {
25
- loadingLabel: l,
26
+ decorative: !0,
26
27
  width: "25%",
27
28
  height: "14px"
28
29
  }), /* @__PURE__ */ n(e, {
29
- loadingLabel: l,
30
+ decorative: !0,
30
31
  width: "100%",
31
32
  height: "16px"
32
33
  })]
@@ -34,11 +35,11 @@ function i({ showAvatar: i = !1, showActions: a = !1, count: o = 1, className: s
34
35
  a && /* @__PURE__ */ r("div", {
35
36
  className: "skeleton-row__actions",
36
37
  children: [/* @__PURE__ */ n(e, {
37
- loadingLabel: l,
38
+ decorative: !0,
38
39
  width: "32px",
39
40
  height: "32px"
40
41
  }), /* @__PURE__ */ n(e, {
41
- loadingLabel: l,
42
+ decorative: !0,
42
43
  width: "32px",
43
44
  height: "32px"
44
45
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"SkeletonRow2.js","names":[],"sources":["../../../src/components/Skeleton/SkeletonRow.tsx"],"sourcesContent":["/**\n * SkeletonRow Component\n *\n * Skeleton placeholder for list rows and table rows.\n * Mimics the structure of a log entry or table row.\n *\n * @example\n * // Basic usage\n * <SkeletonRow />\n *\n * @example\n * // With avatar\n * <SkeletonRow showAvatar />\n */\n\nimport { Skeleton } from \"./Skeleton\";\nimport \"./SkeletonRow.css\";\n\nexport interface SkeletonRowProps {\n /** Show avatar/icon on the left */\n showAvatar?: boolean;\n /** Show action buttons on the right */\n showActions?: boolean;\n /** Number of rows to display */\n count?: number;\n /** Additional CSS class names */\n className?: string;\n /** Test ID for testing */\n testId?: string;\n /** Accessible name for each skeleton inside. Defaults to \"Loading\". */\n loadingLabel?: string;\n}\n\n/**\n * SkeletonRow provides a loading placeholder for list/table rows.\n *\n * Features:\n * - Optional avatar/icon\n * - Optional action buttons\n * - Multiple rows for lists\n */\nexport function SkeletonRow({\n showAvatar = false,\n showActions = false,\n count = 1,\n className = \"\",\n testId,\n loadingLabel,\n}: SkeletonRowProps) {\n const rows = Array.from({ length: count });\n\n return (\n <>\n {rows.map((_, index) => (\n <div\n key={index}\n className={`skeleton-row ${className}`}\n data-testid={testId ? `${testId}-${String(index)}` : undefined}\n role=\"status\"\n aria-busy=\"true\"\n >\n {showAvatar && (\n <div className=\"skeleton-row__avatar\">\n <Skeleton\n loadingLabel={loadingLabel}\n width=\"40px\"\n height=\"40px\"\n variant=\"circle\"\n />\n </div>\n )}\n <div className=\"skeleton-row__content\">\n <Skeleton loadingLabel={loadingLabel} width=\"25%\" height=\"14px\" />\n <Skeleton loadingLabel={loadingLabel} width=\"100%\" height=\"16px\" />\n </div>\n {showActions && (\n <div className=\"skeleton-row__actions\">\n <Skeleton loadingLabel={loadingLabel} width=\"32px\" height=\"32px\" />\n <Skeleton loadingLabel={loadingLabel} width=\"32px\" height=\"32px\" />\n </div>\n )}\n </div>\n ))}\n </>\n );\n}\n\nexport default SkeletonRow;\n"],"mappings":";;;;AAyCA,SAAgB,EAAY,EAC1B,gBAAa,IACb,iBAAc,IACd,WAAQ,GACR,eAAY,IACZ,WACA,mBACmB;CACnB,IAAM,IAAO,MAAM,KAAK,EAAE,QAAQ,EAAM,CAAC;CAEzC,OACE,kBAAA,GAAA,EAAA,UACG,EAAK,KAAK,GAAG,MACZ,kBAAC,OAAD;EAEE,WAAW,gBAAgB;EAC3B,eAAa,IAAS,GAAG,EAAO,GAAG,OAAO,CAAK,MAAM,KAAA;EACrD,MAAK;EACL,aAAU;EALZ,UAAA;GAOG,KACC,kBAAC,OAAD;IAAK,WAAU;IACb,UAAA,kBAAC,GAAD;KACgB;KACd,OAAM;KACN,QAAO;KACP,SAAQ;IACT,CAAA;GACE,CAAA;GAEP,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,GAAD;KAAwB;KAAc,OAAM;KAAM,QAAO;IAAQ,CAAA,GACjE,kBAAC,GAAD;KAAwB;KAAc,OAAM;KAAO,QAAO;IAAQ,CAAA,CAC/D;;GACJ,KACC,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,GAAD;KAAwB;KAAc,OAAM;KAAO,QAAO;IAAQ,CAAA,GAClE,kBAAC,GAAD;KAAwB;KAAc,OAAM;KAAO,QAAO;IAAQ,CAAA,CAC/D;;EAEJ;CA1BE,GAAA,CA0BF,CACN,EACD,CAAA;AAEN"}
1
+ {"version":3,"file":"SkeletonRow2.js","names":[],"sources":["../../../src/components/Skeleton/SkeletonRow.tsx"],"sourcesContent":["/**\n * SkeletonRow Component\n *\n * Skeleton placeholder for list rows and table rows.\n * Mimics the structure of a log entry or table row.\n *\n * @example\n * // Basic usage\n * <SkeletonRow />\n *\n * @example\n * // With avatar\n * <SkeletonRow showAvatar />\n */\n\nimport { Skeleton } from \"./Skeleton\";\nimport \"./SkeletonRow.css\";\n\nexport interface SkeletonRowProps {\n /** Show avatar/icon on the left */\n showAvatar?: boolean;\n /** Show action buttons on the right */\n showActions?: boolean;\n /** Number of rows to display */\n count?: number;\n /** Additional CSS class names */\n className?: string;\n /** Test ID for testing */\n testId?: string;\n /** Accessible name announced for the placeholder as a whole. Defaults to \"Loading\". */\n loadingLabel?: string;\n}\n\n/**\n * SkeletonRow provides a loading placeholder for list/table rows.\n *\n * Features:\n * - Optional avatar/icon\n * - Optional action buttons\n * - Multiple rows for lists\n */\nexport function SkeletonRow({\n showAvatar = false,\n showActions = false,\n count = 1,\n className = \"\",\n testId,\n loadingLabel,\n}: SkeletonRowProps) {\n const rows = Array.from({ length: count });\n\n return (\n <>\n {rows.map((_, index) => (\n <div\n key={index}\n className={`skeleton-row ${className}`}\n data-testid={testId ? `${testId}-${String(index)}` : undefined}\n role=\"status\"\n aria-busy=\"true\"\n aria-label={loadingLabel ?? \"Loading\"}\n >\n {showAvatar && (\n <div className=\"skeleton-row__avatar\">\n <Skeleton decorative width=\"40px\" height=\"40px\" variant=\"circle\" />\n </div>\n )}\n <div className=\"skeleton-row__content\">\n <Skeleton decorative width=\"25%\" height=\"14px\" />\n <Skeleton decorative width=\"100%\" height=\"16px\" />\n </div>\n {showActions && (\n <div className=\"skeleton-row__actions\">\n <Skeleton decorative width=\"32px\" height=\"32px\" />\n <Skeleton decorative width=\"32px\" height=\"32px\" />\n </div>\n )}\n </div>\n ))}\n </>\n );\n}\n\nexport default SkeletonRow;\n"],"mappings":";;;;AAyCA,SAAgB,EAAY,EAC1B,gBAAa,IACb,iBAAc,IACd,WAAQ,GACR,eAAY,IACZ,WACA,mBACmB;CACnB,IAAM,IAAO,MAAM,KAAK,EAAE,QAAQ,EAAM,CAAC;CAEzC,OACE,kBAAA,GAAA,EAAA,UACG,EAAK,KAAK,GAAG,MACZ,kBAAC,OAAD;EAEE,WAAW,gBAAgB;EAC3B,eAAa,IAAS,GAAG,EAAO,GAAG,OAAO,CAAK,MAAM,KAAA;EACrD,MAAK;EACL,aAAU;EACV,cAAY,KAAgB;EAN9B,UAAA;GAQG,KACC,kBAAC,OAAD;IAAK,WAAU;IACb,UAAA,kBAAC,GAAD;KAAU,YAAA;KAAW,OAAM;KAAO,QAAO;KAAO,SAAQ;IAAU,CAAA;GAC/D,CAAA;GAEP,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,GAAD;KAAU,YAAA;KAAW,OAAM;KAAM,QAAO;IAAQ,CAAA,GAChD,kBAAC,GAAD;KAAU,YAAA;KAAW,OAAM;KAAO,QAAO;IAAQ,CAAA,CAC9C;;GACJ,KACC,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,GAAD;KAAU,YAAA;KAAW,OAAM;KAAO,QAAO;IAAQ,CAAA,GACjD,kBAAC,GAAD;KAAU,YAAA;KAAW,OAAM;KAAO,QAAO;IAAQ,CAAA,CAC9C;;EAEJ;CAtBE,GAAA,CAsBF,CACN,EACD,CAAA;AAEN"}
@@ -7,7 +7,7 @@ export interface SkeletonTextProps {
7
7
  className?: string;
8
8
  /** Test ID for testing */
9
9
  testId?: string;
10
- /** Accessible name for each skeleton inside. Defaults to "Loading". */
10
+ /** Accessible name announced for the placeholder as a whole. Defaults to "Loading". */
11
11
  loadingLabel?: string;
12
12
  }
13
13
  /**
@@ -13,8 +13,9 @@ function n({ lines: n = 3, spacing: r = "normal", className: i = "", testId: a,
13
13
  "data-testid": a,
14
14
  role: "status",
15
15
  "aria-busy": "true",
16
+ "aria-label": o ?? "Loading",
16
17
  children: c.map((n, r) => /* @__PURE__ */ t(e, {
17
- loadingLabel: o,
18
+ decorative: !0,
18
19
  width: n,
19
20
  height: "1em",
20
21
  variant: "text"
@@ -1 +1 @@
1
- {"version":3,"file":"SkeletonText2.js","names":[],"sources":["../../../src/components/Skeleton/SkeletonText.tsx"],"sourcesContent":["/**\n * SkeletonText Component\n *\n * Skeleton placeholder for text content.\n * Provides multiple lines with varying widths for natural appearance.\n *\n * @example\n * // Basic usage (3 lines)\n * <SkeletonText />\n *\n * @example\n * // Custom number of lines\n * <SkeletonText lines={5} />\n */\n\nimport { Skeleton } from \"./Skeleton\";\nimport \"./SkeletonText.css\";\n\nexport interface SkeletonTextProps {\n /** Number of text lines to display */\n lines?: number;\n /** Spacing between lines */\n spacing?: \"compact\" | \"normal\" | \"relaxed\";\n /** Additional CSS class names */\n className?: string;\n /** Test ID for testing */\n testId?: string;\n /** Accessible name for each skeleton inside. Defaults to \"Loading\". */\n loadingLabel?: string;\n}\n\n/**\n * SkeletonText provides a loading placeholder for text content.\n *\n * Features:\n * - Multiple lines with varying widths\n * - Configurable line spacing\n * - Accessible via aria-busy\n */\nexport function SkeletonText({\n lines = 3,\n spacing = \"normal\",\n className = \"\",\n testId,\n loadingLabel,\n}: SkeletonTextProps) {\n const classNames = [\"skeleton-text\", `skeleton-text--${spacing}`, className]\n .filter(Boolean)\n .join(\" \");\n\n // Generate varying widths for natural appearance\n const widths = Array.from({ length: lines }, (_, i) => {\n if (i === lines - 1) {\n // Last line is shorter\n return \"60%\";\n }\n // Alternate between full and slightly shorter widths\n return i % 2 === 0 ? \"100%\" : \"95%\";\n });\n\n return (\n <div className={classNames} data-testid={testId} role=\"status\" aria-busy=\"true\">\n {widths.map((width, index) => (\n <Skeleton\n loadingLabel={loadingLabel}\n key={index}\n width={width}\n height=\"1em\"\n variant=\"text\"\n />\n ))}\n </div>\n );\n}\n\nexport default SkeletonText;\n"],"mappings":";;;;AAuCA,SAAgB,EAAa,EAC3B,WAAQ,GACR,aAAU,UACV,eAAY,IACZ,WACA,mBACoB;CACpB,IAAM,IAAa;EAAC;EAAiB,kBAAkB;EAAW;CAAS,CAAC,CACzE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,GAGL,IAAS,MAAM,KAAK,EAAE,QAAQ,EAAM,IAAI,GAAG,MAC3C,MAAM,IAAQ,IAET,QAGF,IAAI,KAAM,IAAI,SAAS,KAC/B;CAED,OACE,kBAAC,OAAD;EAAK,WAAW;EAAY,eAAa;EAAQ,MAAK;EAAS,aAAU;EACtE,UAAA,EAAO,KAAK,GAAO,MAClB,kBAAC,GAAD;GACgB;GAEP;GACP,QAAO;GACP,SAAQ;EACT,GAJM,CAIN,CACF;CACE,CAAA;AAET"}
1
+ {"version":3,"file":"SkeletonText2.js","names":[],"sources":["../../../src/components/Skeleton/SkeletonText.tsx"],"sourcesContent":["/**\n * SkeletonText Component\n *\n * Skeleton placeholder for text content.\n * Provides multiple lines with varying widths for natural appearance.\n *\n * @example\n * // Basic usage (3 lines)\n * <SkeletonText />\n *\n * @example\n * // Custom number of lines\n * <SkeletonText lines={5} />\n */\n\nimport { Skeleton } from \"./Skeleton\";\nimport \"./SkeletonText.css\";\n\nexport interface SkeletonTextProps {\n /** Number of text lines to display */\n lines?: number;\n /** Spacing between lines */\n spacing?: \"compact\" | \"normal\" | \"relaxed\";\n /** Additional CSS class names */\n className?: string;\n /** Test ID for testing */\n testId?: string;\n /** Accessible name announced for the placeholder as a whole. Defaults to \"Loading\". */\n loadingLabel?: string;\n}\n\n/**\n * SkeletonText provides a loading placeholder for text content.\n *\n * Features:\n * - Multiple lines with varying widths\n * - Configurable line spacing\n * - Accessible via aria-busy\n */\nexport function SkeletonText({\n lines = 3,\n spacing = \"normal\",\n className = \"\",\n testId,\n loadingLabel,\n}: SkeletonTextProps) {\n const classNames = [\"skeleton-text\", `skeleton-text--${spacing}`, className]\n .filter(Boolean)\n .join(\" \");\n\n // Generate varying widths for natural appearance\n const widths = Array.from({ length: lines }, (_, i) => {\n if (i === lines - 1) {\n // Last line is shorter\n return \"60%\";\n }\n // Alternate between full and slightly shorter widths\n return i % 2 === 0 ? \"100%\" : \"95%\";\n });\n\n return (\n <div\n className={classNames}\n data-testid={testId}\n role=\"status\"\n aria-busy=\"true\"\n aria-label={loadingLabel ?? \"Loading\"}\n >\n {widths.map((width, index) => (\n <Skeleton decorative key={index} width={width} height=\"1em\" variant=\"text\" />\n ))}\n </div>\n );\n}\n\nexport default SkeletonText;\n"],"mappings":";;;;AAuCA,SAAgB,EAAa,EAC3B,WAAQ,GACR,aAAU,UACV,eAAY,IACZ,WACA,mBACoB;CACpB,IAAM,IAAa;EAAC;EAAiB,kBAAkB;EAAW;CAAS,CAAC,CACzE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,GAGL,IAAS,MAAM,KAAK,EAAE,QAAQ,EAAM,IAAI,GAAG,MAC3C,MAAM,IAAQ,IAET,QAGF,IAAI,KAAM,IAAI,SAAS,KAC/B;CAED,OACE,kBAAC,OAAD;EACE,WAAW;EACX,eAAa;EACb,MAAK;EACL,aAAU;EACV,cAAY,KAAgB;EAE3B,UAAA,EAAO,KAAK,GAAO,MAClB,kBAAC,GAAD;GAAU,YAAA;GAA8B;GAAO,QAAO;GAAM,SAAQ;EAAQ,GAAlD,CAAkD,CAC7E;CACE,CAAA;AAET"}
@@ -1,100 +1,98 @@
1
1
  /* empty css */
2
- import { useCallback as e, useEffect as t, useId as n, useRef as r, useState as i } from "react";
3
- import { jsx as a, jsxs as o } from "react/jsx-runtime";
4
- import { createPortal as s } from "react-dom";
2
+ import { useCallback as e, useEffect as t, useId as n, useLayoutEffect as r, useRef as i, useState as a } from "react";
3
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
4
+ import { createPortal as c } from "react-dom";
5
5
  //#region src/components/Tooltip/Tooltip.tsx
6
- var c = 120;
7
- function l({ content: l, children: u, className: d, contentClassName: f, tooltipId: p, tabIndex: m = 0, toggleable: h = !1 }) {
8
- let [g, _] = i(!1), [v, y] = i(null), b = r(null), x = r(null), S = r(!0), C = r(null), w = n(), T = p ?? `tooltip-${w}`;
9
- t(() => (S.current = !0, () => {
10
- S.current = !1, C.current !== null && clearTimeout(C.current);
6
+ var l = 120;
7
+ function u({ content: u, children: d, className: f, contentClassName: p, tooltipId: m, tabIndex: h = 0, toggleable: g = !1 }) {
8
+ let [_, v] = a(!1), [y, b] = a(null), x = i(null), S = i(null), C = i(!0), w = i(null), T = n(), E = m ?? `tooltip-${T}`;
9
+ t(() => (C.current = !0, () => {
10
+ C.current = !1, w.current !== null && clearTimeout(w.current);
11
11
  }), []), t(() => {
12
- let e = x.current;
12
+ let e = S.current;
13
13
  if (!e) return;
14
14
  let t = () => {
15
- S.current && _(!0);
15
+ C.current && v(!0);
16
16
  }, n = () => {
17
- S.current && (_(!1), y(null));
17
+ C.current && (v(!1), b(null));
18
18
  };
19
19
  return e.addEventListener("focusin", t), e.addEventListener("focusout", n), () => {
20
20
  e.removeEventListener("focusin", t), e.removeEventListener("focusout", n);
21
21
  };
22
22
  }, []);
23
- let E = e(() => {
24
- let e = x.current, t = b.current;
25
- if (!S.current || !e || !t) return;
23
+ let D = e(() => {
24
+ let e = S.current, t = x.current;
25
+ if (!C.current || !e || !t) return;
26
26
  let n = e.getBoundingClientRect(), r = t.getBoundingClientRect(), i = n.top, a = window.innerHeight - n.bottom, o = r.height, s = i < o + 10 && a > i ? "bottom" : "top", c = n.left + n.width / 2, l = r.width, u = c - l / 2, d = window.innerWidth;
27
27
  u + l > d - 16 && (u = d - l - 16), u < 16 && (u = 16);
28
28
  let f = s === "top" ? n.top - o - 8 : n.bottom + 8;
29
- y({
29
+ b({
30
30
  top: f,
31
31
  left: u,
32
32
  placement: s
33
33
  });
34
34
  }, []);
35
- t(() => {
36
- if (!g) return;
37
- let e = requestAnimationFrame(E);
38
- return window.addEventListener("scroll", E, {
35
+ r(() => {
36
+ if (_) return D(), window.addEventListener("scroll", D, {
39
37
  capture: !0,
40
38
  passive: !0
41
- }), window.addEventListener("resize", E), () => {
42
- cancelAnimationFrame(e), window.removeEventListener("scroll", E, { capture: !0 }), window.removeEventListener("resize", E);
39
+ }), window.addEventListener("resize", D), () => {
40
+ window.removeEventListener("scroll", D, { capture: !0 }), window.removeEventListener("resize", D);
43
41
  };
44
- }, [g, E]);
45
- let D = e(() => {
46
- C.current !== null && (clearTimeout(C.current), C.current = null), _(!0);
47
- }, []), O = e(() => {
48
- C.current !== null && (clearTimeout(C.current), C.current = null), _(!1), y(null);
42
+ }, [_, D]);
43
+ let O = e(() => {
44
+ w.current !== null && (clearTimeout(w.current), w.current = null), v(!0);
49
45
  }, []), k = e(() => {
50
- C.current !== null && clearTimeout(C.current), C.current = setTimeout(() => {
51
- C.current = null, S.current && (_(!1), y(null));
52
- }, c);
46
+ w.current !== null && (clearTimeout(w.current), w.current = null), v(!1), b(null);
47
+ }, []), A = e(() => {
48
+ w.current !== null && clearTimeout(w.current), w.current = setTimeout(() => {
49
+ w.current = null, C.current && (v(!1), b(null));
50
+ }, l);
53
51
  }, []);
54
52
  t(() => {
55
- if (!g) return;
53
+ if (!_) return;
56
54
  let e = (e) => {
57
- e.key === "Escape" && O();
55
+ e.key === "Escape" && k();
58
56
  };
59
57
  return document.addEventListener("keydown", e), () => {
60
58
  document.removeEventListener("keydown", e);
61
59
  };
62
- }, [g, O]);
63
- let A = e((e) => {
64
- (e.key === "Enter" || e.key === " ") && (e.preventDefault(), g ? O() : D());
60
+ }, [_, k]);
61
+ let j = e((e) => {
62
+ (e.key === "Enter" || e.key === " ") && (e.preventDefault(), _ ? k() : O());
65
63
  }, [
66
- g,
67
- O,
68
- D
69
- ]), j = g && /* @__PURE__ */ a("div", {
70
- ref: b,
71
- id: T,
72
- className: `tooltip__content tooltip__content--${v?.placement ?? "top"} ${f ?? ""}`,
73
- style: v ? {
74
- top: `${String(v.top)}px`,
75
- left: `${String(v.left)}px`
64
+ _,
65
+ k,
66
+ O
67
+ ]), M = _ && /* @__PURE__ */ o("div", {
68
+ ref: x,
69
+ id: E,
70
+ className: `tooltip__content tooltip__content--${y?.placement ?? "top"} ${p ?? ""}`,
71
+ style: y ? {
72
+ top: `${String(y.top)}px`,
73
+ left: `${String(y.left)}px`
76
74
  } : { visibility: "hidden" },
77
75
  role: "tooltip",
78
- children: typeof l == "string" ? /* @__PURE__ */ a("p", {
76
+ children: typeof u == "string" ? /* @__PURE__ */ o("p", {
79
77
  className: "tooltip__text",
80
- children: l
81
- }) : l
78
+ children: u
79
+ }) : u
82
80
  });
83
- return /* @__PURE__ */ o("div", {
84
- ref: x,
85
- className: `tooltip__wrapper ${d ?? ""}`,
86
- onMouseEnter: D,
87
- onMouseLeave: k,
88
- onKeyDown: h ? A : void 0,
89
- tabIndex: m,
90
- role: h ? "button" : void 0,
91
- "aria-expanded": h ? g : void 0,
92
- "aria-describedby": g ? T : void 0,
93
- children: [u, j && s(j, document.body)]
81
+ return /* @__PURE__ */ s("div", {
82
+ ref: S,
83
+ className: `tooltip__wrapper ${f ?? ""}`,
84
+ onMouseEnter: O,
85
+ onMouseLeave: A,
86
+ onKeyDown: g ? j : void 0,
87
+ tabIndex: h,
88
+ role: g ? "button" : void 0,
89
+ "aria-expanded": g ? _ : void 0,
90
+ "aria-describedby": _ ? E : void 0,
91
+ children: [d, M && c(M, document.body)]
94
92
  });
95
93
  }
96
94
  //#endregion
97
- export { l as Tooltip };
95
+ export { u as Tooltip };
98
96
 
99
97
  //# sourceMappingURL=Tooltip2.js.map
100
98
  import "../../assets/components/Tooltip/Tooltip.css";
@@ -1 +1 @@
1
- {"version":3,"file":"Tooltip2.js","names":[],"sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["/**\n * Tooltip Component\n *\n * A reusable tooltip component that displays content on hover.\n * Supports both simple text and complex ReactNode content.\n *\n * Accessibility, against the three parts of WCAG 2.1 SC 1.4.13:\n * - Hoverable: the pointer can move onto the tooltip itself without it\n * disappearing. The content sits 8px away from the trigger, so the pointer\n * crosses a gap to get there and the hide is deferred over that crossing.\n * - Dismissible: Escape closes it without moving the pointer or focus.\n * - Persistent: it stays while the trigger is hovered or focused.\n *\n * Also uses role=\"tooltip\" and aria-describedby so a screen reader announces\n * the content with the trigger. `toggleable` additionally makes the trigger a\n * button with aria-expanded that Enter and Space operate, for content a reader\n * summons rather than passes over.\n */\n\nimport {\n useState,\n useRef,\n useEffect,\n useCallback,\n useId,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport \"./Tooltip.css\";\n\nexport interface TooltipProps {\n /** Tooltip content - can be string or ReactNode */\n content: ReactNode;\n /** Children to wrap with tooltip (trigger element) */\n children: ReactNode;\n /** Additional class name for the wrapper */\n className?: string;\n /** Additional class name for the tooltip content */\n contentClassName?: string;\n /** Custom ID for the tooltip (for aria-describedby association) */\n tooltipId?: string;\n /** Tab index for the wrapper. Use -1 when wrapping already-focusable elements like buttons. Defaults to 0. */\n tabIndex?: number;\n /**\n * Announce the trigger as a button carrying `aria-expanded`, and let Enter\n * and Space toggle the content.\n *\n * Off by default: a trigger whose content only supplements what is already\n * on screen is not a control and should not claim to be one. Turn it on\n * where the content is something a reader summons deliberately. A glossary\n * term is the case this exists for, and two products had independently built\n * the same trigger contract around one.\n *\n * Do not combine with `tabIndex={-1}`, which is for wrapping an element that\n * is already focusable and already owns its role.\n */\n toggleable?: boolean;\n}\n\n/** Long enough to cross the 8px gap to the tooltip, short enough not to linger. */\nconst HIDE_GRACE_MS = 120;\n\ninterface TooltipPosition {\n top: number;\n left: number;\n placement: \"top\" | \"bottom\";\n}\n\nexport function Tooltip({\n content,\n children,\n className,\n contentClassName,\n tooltipId,\n tabIndex = 0,\n toggleable = false,\n}: TooltipProps) {\n const [isVisible, setIsVisible] = useState(false);\n const [tooltipPosition, setTooltipPosition] = useState<TooltipPosition | null>(null);\n const tooltipRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const isMountedRef = useRef(true);\n const hideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const generatedId = useId();\n const effectiveTooltipId = tooltipId ?? `tooltip-${generatedId}`;\n\n // Track mount state\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n if (hideTimerRef.current !== null) clearTimeout(hideTimerRef.current);\n };\n }, []);\n\n // Handle focus events using focusin/focusout which bubble from child elements\n // This ensures tooltips show when keyboard focus moves to children (WCAG 1.4.13)\n useEffect(() => {\n const wrapper = wrapperRef.current;\n if (!wrapper) return;\n\n // focusin/focusout bubble from child elements, unlike focus/blur\n const handleFocusIn = () => {\n if (isMountedRef.current) {\n setIsVisible(true);\n }\n };\n\n const handleFocusOut = () => {\n if (isMountedRef.current) {\n setIsVisible(false);\n setTooltipPosition(null);\n }\n };\n\n wrapper.addEventListener(\"focusin\", handleFocusIn);\n wrapper.addEventListener(\"focusout\", handleFocusOut);\n\n return () => {\n wrapper.removeEventListener(\"focusin\", handleFocusIn);\n wrapper.removeEventListener(\"focusout\", handleFocusOut);\n };\n }, []);\n\n /**\n * Place the tooltip beside the trigger, in whichever direction has room.\n *\n * Reads both boxes at call time rather than closing over them, because this\n * runs again every time the trigger moves under the viewport.\n */\n const measure = useCallback(() => {\n const wrapper = wrapperRef.current;\n const tooltip = tooltipRef.current;\n if (!isMountedRef.current || !wrapper || !tooltip) return;\n\n const wrapperRect = wrapper.getBoundingClientRect();\n const tooltipRect = tooltip.getBoundingClientRect();\n const spaceAbove = wrapperRect.top;\n const spaceBelow = window.innerHeight - wrapperRect.bottom;\n\n const tooltipHeight = tooltipRect.height;\n const margin = 10;\n\n // Determine placement\n const placement: \"top\" | \"bottom\" =\n spaceAbove < tooltipHeight + margin && spaceBelow > spaceAbove ? \"bottom\" : \"top\";\n\n // Calculate left position (center on wrapper, but ensure it stays within viewport)\n const wrapperCenter = wrapperRect.left + wrapperRect.width / 2;\n const tooltipWidth = tooltipRect.width;\n let left = wrapperCenter - tooltipWidth / 2;\n const viewportWidth = window.innerWidth;\n\n // Keep tooltip within viewport bounds\n if (left + tooltipWidth > viewportWidth - 16) {\n left = viewportWidth - tooltipWidth - 16;\n }\n if (left < 16) {\n left = 16;\n }\n\n // Calculate top position\n const top =\n placement === \"top\"\n ? wrapperRect.top - tooltipHeight - 8\n : wrapperRect.bottom + 8;\n\n setTooltipPosition({ top, left, placement });\n }, []);\n\n /**\n * The content is `position: fixed` and lives in a portal, so nothing moves it\n * when the trigger moves. Any scroll between the two, on the page or on a\n * container in between, leaves it pointing at where the trigger used to be,\n * and a resize can leave it off the edge. Scroll events from a container do\n * not bubble, hence the capture phase.\n *\n * The first measurement waits for a frame so the content has been laid out\n * and has a height to place against.\n */\n useEffect(() => {\n if (!isVisible) return;\n\n const frame = requestAnimationFrame(measure);\n window.addEventListener(\"scroll\", measure, { capture: true, passive: true });\n window.addEventListener(\"resize\", measure);\n\n return () => {\n cancelAnimationFrame(frame);\n window.removeEventListener(\"scroll\", measure, { capture: true });\n window.removeEventListener(\"resize\", measure);\n };\n }, [isVisible, measure]);\n\n const handleShow = useCallback(() => {\n if (hideTimerRef.current !== null) {\n clearTimeout(hideTimerRef.current);\n hideTimerRef.current = null;\n }\n setIsVisible(true);\n }, []);\n\n const hideNow = useCallback(() => {\n if (hideTimerRef.current !== null) {\n clearTimeout(hideTimerRef.current);\n hideTimerRef.current = null;\n }\n setIsVisible(false);\n setTooltipPosition(null);\n }, []);\n\n /**\n * Deferred so the pointer can cross the 8px gap between the trigger and the\n * tooltip. React propagates enter and leave through the portal, so a direct\n * hop onto the content never fires this; the gap does, because the pointer\n * passes over a node that belongs to neither element. Hiding on that would\n * put the content out of reach (WCAG 2.1 SC 1.4.13, Hoverable). Arriving on\n * either element cancels the pending hide.\n */\n const handleHide = useCallback(() => {\n if (hideTimerRef.current !== null) clearTimeout(hideTimerRef.current);\n hideTimerRef.current = setTimeout(() => {\n hideTimerRef.current = null;\n if (!isMountedRef.current) return;\n setIsVisible(false);\n setTooltipPosition(null);\n }, HIDE_GRACE_MS);\n }, []);\n\n // Dismissible: Escape closes without moving the pointer or focus. Bound to\n // the document because focus may sit on a child of the trigger, or nowhere\n // at all when the tooltip was opened by hover.\n useEffect(() => {\n if (!isVisible) return;\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") hideNow();\n };\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [isVisible, hideNow]);\n\n /**\n * Enter and Space toggle, for a trigger that is a control rather than\n * incidental hover help. Both default to something else, Space scrolling the\n * page and Enter submitting an enclosing form, so both are prevented.\n */\n const handleKeyDown = useCallback(\n (e: ReactKeyboardEvent<HTMLDivElement>) => {\n if (e.key !== \"Enter\" && e.key !== \" \") return;\n e.preventDefault();\n if (isVisible) {\n hideNow();\n } else {\n handleShow();\n }\n },\n [isVisible, hideNow, handleShow],\n );\n\n const tooltipElement = isVisible && (\n <div\n ref={tooltipRef}\n id={effectiveTooltipId}\n className={`tooltip__content tooltip__content--${tooltipPosition?.placement ?? \"top\"} ${contentClassName ?? \"\"}`}\n style={\n tooltipPosition\n ? {\n top: `${String(tooltipPosition.top)}px`,\n left: `${String(tooltipPosition.left)}px`,\n }\n : { visibility: \"hidden\" }\n }\n role=\"tooltip\"\n >\n {typeof content === \"string\" ? (\n <p className=\"tooltip__text\">{content}</p>\n ) : (\n content\n )}\n </div>\n );\n\n return (\n <div\n ref={wrapperRef}\n className={`tooltip__wrapper ${className ?? \"\"}`}\n onMouseEnter={handleShow}\n onMouseLeave={handleHide}\n onKeyDown={toggleable ? handleKeyDown : undefined}\n tabIndex={tabIndex}\n role={toggleable ? \"button\" : undefined}\n aria-expanded={toggleable ? isVisible : undefined}\n aria-describedby={isVisible ? effectiveTooltipId : undefined}\n >\n {children}\n {tooltipElement && createPortal(tooltipElement, document.body)}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Tooltip2.js","names":[],"sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["/**\n * Tooltip Component\n *\n * A reusable tooltip component that displays content on hover.\n * Supports both simple text and complex ReactNode content.\n *\n * Accessibility, against the three parts of WCAG 2.1 SC 1.4.13:\n * - Hoverable: the pointer can move onto the tooltip itself without it\n * disappearing. The content sits 8px away from the trigger, so the pointer\n * crosses a gap to get there and the hide is deferred over that crossing.\n * - Dismissible: Escape closes it without moving the pointer or focus.\n * - Persistent: it stays while the trigger is hovered or focused.\n *\n * Also uses role=\"tooltip\" and aria-describedby so a screen reader announces\n * the content with the trigger. `toggleable` additionally makes the trigger a\n * button with aria-expanded that Enter and Space operate, for content a reader\n * summons rather than passes over.\n */\n\nimport {\n useState,\n useRef,\n useEffect,\n useLayoutEffect,\n useCallback,\n useId,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport \"./Tooltip.css\";\n\nexport interface TooltipProps {\n /** Tooltip content - can be string or ReactNode */\n content: ReactNode;\n /** Children to wrap with tooltip (trigger element) */\n children: ReactNode;\n /** Additional class name for the wrapper */\n className?: string;\n /** Additional class name for the tooltip content */\n contentClassName?: string;\n /** Custom ID for the tooltip (for aria-describedby association) */\n tooltipId?: string;\n /** Tab index for the wrapper. Use -1 when wrapping already-focusable elements like buttons. Defaults to 0. */\n tabIndex?: number;\n /**\n * Announce the trigger as a button carrying `aria-expanded`, and let Enter\n * and Space toggle the content.\n *\n * Off by default: a trigger whose content only supplements what is already\n * on screen is not a control and should not claim to be one. Turn it on\n * where the content is something a reader summons deliberately. A glossary\n * term is the case this exists for, and two products had independently built\n * the same trigger contract around one.\n *\n * Do not combine with `tabIndex={-1}`, which is for wrapping an element that\n * is already focusable and already owns its role.\n */\n toggleable?: boolean;\n}\n\n/** Long enough to cross the 8px gap to the tooltip, short enough not to linger. */\nconst HIDE_GRACE_MS = 120;\n\ninterface TooltipPosition {\n top: number;\n left: number;\n placement: \"top\" | \"bottom\";\n}\n\nexport function Tooltip({\n content,\n children,\n className,\n contentClassName,\n tooltipId,\n tabIndex = 0,\n toggleable = false,\n}: TooltipProps) {\n const [isVisible, setIsVisible] = useState(false);\n const [tooltipPosition, setTooltipPosition] = useState<TooltipPosition | null>(null);\n const tooltipRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const isMountedRef = useRef(true);\n const hideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const generatedId = useId();\n const effectiveTooltipId = tooltipId ?? `tooltip-${generatedId}`;\n\n // Track mount state\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n if (hideTimerRef.current !== null) clearTimeout(hideTimerRef.current);\n };\n }, []);\n\n // Handle focus events using focusin/focusout which bubble from child elements\n // This ensures tooltips show when keyboard focus moves to children (WCAG 1.4.13)\n useEffect(() => {\n const wrapper = wrapperRef.current;\n if (!wrapper) return;\n\n // focusin/focusout bubble from child elements, unlike focus/blur\n const handleFocusIn = () => {\n if (isMountedRef.current) {\n setIsVisible(true);\n }\n };\n\n const handleFocusOut = () => {\n if (isMountedRef.current) {\n setIsVisible(false);\n setTooltipPosition(null);\n }\n };\n\n wrapper.addEventListener(\"focusin\", handleFocusIn);\n wrapper.addEventListener(\"focusout\", handleFocusOut);\n\n return () => {\n wrapper.removeEventListener(\"focusin\", handleFocusIn);\n wrapper.removeEventListener(\"focusout\", handleFocusOut);\n };\n }, []);\n\n /**\n * Place the tooltip beside the trigger, in whichever direction has room.\n *\n * Reads both boxes at call time rather than closing over them, because this\n * runs again every time the trigger moves under the viewport.\n */\n const measure = useCallback(() => {\n const wrapper = wrapperRef.current;\n const tooltip = tooltipRef.current;\n if (!isMountedRef.current || !wrapper || !tooltip) return;\n\n const wrapperRect = wrapper.getBoundingClientRect();\n const tooltipRect = tooltip.getBoundingClientRect();\n const spaceAbove = wrapperRect.top;\n const spaceBelow = window.innerHeight - wrapperRect.bottom;\n\n const tooltipHeight = tooltipRect.height;\n const margin = 10;\n\n // Determine placement\n const placement: \"top\" | \"bottom\" =\n spaceAbove < tooltipHeight + margin && spaceBelow > spaceAbove ? \"bottom\" : \"top\";\n\n // Calculate left position (center on wrapper, but ensure it stays within viewport)\n const wrapperCenter = wrapperRect.left + wrapperRect.width / 2;\n const tooltipWidth = tooltipRect.width;\n let left = wrapperCenter - tooltipWidth / 2;\n const viewportWidth = window.innerWidth;\n\n // Keep tooltip within viewport bounds\n if (left + tooltipWidth > viewportWidth - 16) {\n left = viewportWidth - tooltipWidth - 16;\n }\n if (left < 16) {\n left = 16;\n }\n\n // Calculate top position\n const top =\n placement === \"top\"\n ? wrapperRect.top - tooltipHeight - 8\n : wrapperRect.bottom + 8;\n\n setTooltipPosition({ top, left, placement });\n }, []);\n\n /**\n * The content is `position: fixed` and lives in a portal, so nothing moves it\n * when the trigger moves. Any scroll between the two, on the page or on a\n * container in between, leaves it pointing at where the trigger used to be,\n * and a resize can leave it off the edge. Scroll events from a container do\n * not bubble, hence the capture phase.\n *\n * A layout effect, not an effect plus a frame. The content is in the DOM by\n * the time this runs and can be measured, and the state it sets is flushed\n * before the browser paints, so the tooltip is never painted unplaced. The\n * frame it used to wait for was a frame the tooltip spent mounted and\n * `visibility: hidden`, which is out of the accessibility tree: present but\n * unreadable, and unfindable by any query that respects that tree.\n */\n useLayoutEffect(() => {\n if (!isVisible) return;\n\n measure();\n window.addEventListener(\"scroll\", measure, { capture: true, passive: true });\n window.addEventListener(\"resize\", measure);\n\n return () => {\n window.removeEventListener(\"scroll\", measure, { capture: true });\n window.removeEventListener(\"resize\", measure);\n };\n }, [isVisible, measure]);\n\n const handleShow = useCallback(() => {\n if (hideTimerRef.current !== null) {\n clearTimeout(hideTimerRef.current);\n hideTimerRef.current = null;\n }\n setIsVisible(true);\n }, []);\n\n const hideNow = useCallback(() => {\n if (hideTimerRef.current !== null) {\n clearTimeout(hideTimerRef.current);\n hideTimerRef.current = null;\n }\n setIsVisible(false);\n setTooltipPosition(null);\n }, []);\n\n /**\n * Deferred so the pointer can cross the 8px gap between the trigger and the\n * tooltip. React propagates enter and leave through the portal, so a direct\n * hop onto the content never fires this; the gap does, because the pointer\n * passes over a node that belongs to neither element. Hiding on that would\n * put the content out of reach (WCAG 2.1 SC 1.4.13, Hoverable). Arriving on\n * either element cancels the pending hide.\n */\n const handleHide = useCallback(() => {\n if (hideTimerRef.current !== null) clearTimeout(hideTimerRef.current);\n hideTimerRef.current = setTimeout(() => {\n hideTimerRef.current = null;\n if (!isMountedRef.current) return;\n setIsVisible(false);\n setTooltipPosition(null);\n }, HIDE_GRACE_MS);\n }, []);\n\n // Dismissible: Escape closes without moving the pointer or focus. Bound to\n // the document because focus may sit on a child of the trigger, or nowhere\n // at all when the tooltip was opened by hover.\n useEffect(() => {\n if (!isVisible) return;\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") hideNow();\n };\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [isVisible, hideNow]);\n\n /**\n * Enter and Space toggle, for a trigger that is a control rather than\n * incidental hover help. Both default to something else, Space scrolling the\n * page and Enter submitting an enclosing form, so both are prevented.\n */\n const handleKeyDown = useCallback(\n (e: ReactKeyboardEvent<HTMLDivElement>) => {\n if (e.key !== \"Enter\" && e.key !== \" \") return;\n e.preventDefault();\n if (isVisible) {\n hideNow();\n } else {\n handleShow();\n }\n },\n [isVisible, hideNow, handleShow],\n );\n\n const tooltipElement = isVisible && (\n <div\n ref={tooltipRef}\n id={effectiveTooltipId}\n className={`tooltip__content tooltip__content--${tooltipPosition?.placement ?? \"top\"} ${contentClassName ?? \"\"}`}\n style={\n tooltipPosition\n ? {\n top: `${String(tooltipPosition.top)}px`,\n left: `${String(tooltipPosition.left)}px`,\n }\n : { visibility: \"hidden\" }\n }\n role=\"tooltip\"\n >\n {typeof content === \"string\" ? (\n <p className=\"tooltip__text\">{content}</p>\n ) : (\n content\n )}\n </div>\n );\n\n return (\n <div\n ref={wrapperRef}\n className={`tooltip__wrapper ${className ?? \"\"}`}\n onMouseEnter={handleShow}\n onMouseLeave={handleHide}\n onKeyDown={toggleable ? handleKeyDown : undefined}\n tabIndex={tabIndex}\n role={toggleable ? \"button\" : undefined}\n aria-expanded={toggleable ? isVisible : undefined}\n aria-describedby={isVisible ? effectiveTooltipId : undefined}\n >\n {children}\n {tooltipElement && createPortal(tooltipElement, document.body)}\n </div>\n );\n}\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -24,6 +24,7 @@ export * from './components/PageLayout';
24
24
  export * from './components/ProgressBar';
25
25
  export * from './components/Skeleton';
26
26
  export * from './components/SmoothHeight';
27
+ export * from './components/Select';
27
28
  export * from './components/StatCard';
28
29
  export * from './components/StatusTag';
29
30
  export * from './components/Tabs';
package/dist/index.js CHANGED
@@ -14,10 +14,11 @@ import { SkeletonText as f } from "./components/Skeleton/SkeletonText2.js";
14
14
  import { SkeletonChart as p } from "./components/Skeleton/SkeletonChart2.js";
15
15
  import { SkeletonRow as m } from "./components/Skeleton/SkeletonRow2.js";
16
16
  import { SmoothHeight as h } from "./components/SmoothHeight/SmoothHeight2.js";
17
- import { usePrefersReducedMotion as g } from "./hooks/usePrefersReducedMotion.js";
18
- import { StatCard as _ } from "./components/StatCard/StatCard2.js";
19
- import { formatCompactNumber as v } from "./components/StatCard/formatters.js";
20
- import { StatusTag as y } from "./components/StatusTag/StatusTag2.js";
21
- import { Tabs as b } from "./components/Tabs/Tabs2.js";
22
- import { Tooltip as x } from "./components/Tooltip/Tooltip2.js";
23
- export { t as Badge, e as BaseCard, n as Button, r as DataTable, i as Drawer, a as EmptyState, o as ErrorState, s as PageHeader, c as PageLayout, l as ProgressBar, u as Skeleton, d as SkeletonCard, p as SkeletonChart, m as SkeletonRow, f as SkeletonText, h as SmoothHeight, _ as StatCard, y as StatusTag, b as Tabs, x as Tooltip, v as formatCompactNumber, g as usePrefersReducedMotion };
17
+ import { Select as g } from "./components/Select/Select2.js";
18
+ import { usePrefersReducedMotion as _ } from "./hooks/usePrefersReducedMotion.js";
19
+ import { StatCard as v } from "./components/StatCard/StatCard2.js";
20
+ import { formatCompactNumber as y } from "./components/StatCard/formatters.js";
21
+ import { StatusTag as b } from "./components/StatusTag/StatusTag2.js";
22
+ import { Tabs as x } from "./components/Tabs/Tabs2.js";
23
+ import { Tooltip as S } from "./components/Tooltip/Tooltip2.js";
24
+ export { t as Badge, e as BaseCard, n as Button, r as DataTable, i as Drawer, a as EmptyState, o as ErrorState, s as PageHeader, c as PageLayout, l as ProgressBar, g as Select, u as Skeleton, d as SkeletonCard, p as SkeletonChart, m as SkeletonRow, f as SkeletonText, h as SmoothHeight, v as StatCard, b as StatusTag, x as Tabs, S as Tooltip, y as formatCompactNumber, _ as usePrefersReducedMotion };
@@ -131,6 +131,7 @@
131
131
  --token-colorPrimary: #ff7a00;
132
132
  --token-colorPrimaryActive: #cc6200;
133
133
  --token-colorPrimaryBg: rgba(255, 122, 0, 0.1);
134
+ --token-colorPrimaryBgHover: rgba(255, 122, 0, 0.16);
134
135
  --token-colorPrimaryBorder: rgba(255, 122, 0, 0.2);
135
136
  --token-colorSuccess: #007a63;
136
137
  --token-colorSuccessActive: #005244;
@@ -140,6 +141,12 @@
140
141
  --token-colorSuccessBoxShadow: 0 4px 12px rgba(0, 189, 155, 0.15);
141
142
  --token-colorWarning: #9a5d00;
142
143
  --token-colorText: #141414;
144
+ /* Both #767676 rather than the #bfbfbf a disabled or placeholder value is
145
+ * usually given: that measures 2.2:1 on white, and text a reader is expected
146
+ * to read (a placeholder naming the field, a disabled value explaining why it
147
+ * cannot be changed) is not exempt from the 4.5:1 floor. */
148
+ --token-colorTextDisabled: #767676;
149
+ --token-colorTextPlaceholder: #767676;
143
150
  --token-colorTextQuaternary: #bfbfbf;
144
151
  --token-colorTextSecondary: #595959;
145
152
  --token-colorTextTertiary: #737373;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lablup/ui-common",
3
- "version": "0.1.0-alpha.17",
3
+ "version": "0.1.0-alpha.19",
4
4
  "description": "Shared, product-neutral UI components and design tokens for Lablup products",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Lablup Inc.",