@apliteni/apliteni-ui 0.7.1 → 0.8.0

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,254 @@
1
+ "use client";
2
+
3
+ // src/primitives/Icon.tsx
4
+ import { icon } from "@apliteni/apliteni-ui";
5
+ import { jsx } from "react/jsx-runtime";
6
+ function Icon({ name }) {
7
+ return /* @__PURE__ */ jsx(
8
+ "span",
9
+ {
10
+ "aria-hidden": "true",
11
+ style: { display: "inline-flex" },
12
+ dangerouslySetInnerHTML: { __html: icon(name) }
13
+ }
14
+ );
15
+ }
16
+
17
+ // src/primitives/Button.tsx
18
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
19
+ var cx = (...a) => a.filter(Boolean).join(" ");
20
+ function Button({
21
+ variant = "secondary",
22
+ size = "md",
23
+ icon: icon2,
24
+ iconRight,
25
+ iconOnly,
26
+ block,
27
+ children,
28
+ type = "button",
29
+ ...rest
30
+ }) {
31
+ const cls = cx(
32
+ "ui-btn",
33
+ variant && `ui-btn--${variant}`,
34
+ size !== "md" && `ui-btn--${size}`,
35
+ block && "ui-btn--block",
36
+ iconOnly && "ui-btn--icon"
37
+ );
38
+ const labelled = rest["aria-label"] != null || rest["aria-labelledby"] != null;
39
+ const fallback = typeof children === "string" && children.trim() ? children.trim() : icon2;
40
+ const named = iconOnly && !labelled && fallback ? { "aria-label": fallback, title: rest.title ?? fallback } : {};
41
+ return /* @__PURE__ */ jsxs("button", { type, className: cls, ...rest, ...named, children: [
42
+ icon2 && /* @__PURE__ */ jsx2(Icon, { name: icon2 }),
43
+ !iconOnly && children != null && /* @__PURE__ */ jsx2("span", { children }),
44
+ iconRight && /* @__PURE__ */ jsx2(Icon, { name: iconRight })
45
+ ] });
46
+ }
47
+
48
+ // src/primitives/Badge.tsx
49
+ import { jsx as jsx3 } from "react/jsx-runtime";
50
+ function Badge({ variant = "neutral", children }) {
51
+ const cls = ["ui-badge", variant !== "neutral" && `ui-badge--${variant}`].filter(Boolean).join(" ");
52
+ return /* @__PURE__ */ jsx3("span", { className: cls, children });
53
+ }
54
+
55
+ // src/primitives/Card.tsx
56
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
57
+ function Card({ title, sub, children }) {
58
+ return /* @__PURE__ */ jsxs2("div", { className: "ui-card", children: [
59
+ title != null && /* @__PURE__ */ jsx4("div", { className: "ui-card__title", children: title }),
60
+ sub != null && /* @__PURE__ */ jsx4("div", { className: "ui-card__sub", children: sub }),
61
+ children
62
+ ] });
63
+ }
64
+
65
+ // src/Modal.tsx
66
+ import { useEffect, useRef } from "react";
67
+ import { createPortal } from "react-dom";
68
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
69
+ var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
70
+ function Modal({ open, title, onClose, footer, children }) {
71
+ const panel = useRef(null);
72
+ useEffect(() => {
73
+ if (!open) return;
74
+ const onKey = (e) => {
75
+ if (e.key === "Escape") {
76
+ onClose();
77
+ return;
78
+ }
79
+ if (e.key !== "Tab" || !panel.current) return;
80
+ const items = Array.from(panel.current.querySelectorAll(FOCUSABLE));
81
+ if (items.length === 0) {
82
+ e.preventDefault();
83
+ panel.current.focus();
84
+ return;
85
+ }
86
+ const first = items[0];
87
+ const last = items[items.length - 1];
88
+ const active = document.activeElement;
89
+ if (!panel.current.contains(active)) {
90
+ e.preventDefault();
91
+ first.focus();
92
+ return;
93
+ }
94
+ if (!e.shiftKey && active === last) {
95
+ e.preventDefault();
96
+ first.focus();
97
+ } else if (e.shiftKey && active === first) {
98
+ e.preventDefault();
99
+ last.focus();
100
+ }
101
+ };
102
+ document.addEventListener("keydown", onKey);
103
+ return () => document.removeEventListener("keydown", onKey);
104
+ }, [open, onClose]);
105
+ useEffect(() => {
106
+ if (!open) return;
107
+ const opener = document.activeElement;
108
+ const portalRoot = panel.current?.closest(".rx-scrim");
109
+ const muted = Array.from(document.body.children).filter((el) => el !== portalRoot && !el.hasAttribute("inert"));
110
+ muted.forEach((el) => el.setAttribute("inert", ""));
111
+ return () => {
112
+ muted.forEach((el) => el.removeAttribute("inert"));
113
+ if (opener && document.contains(opener)) opener.focus();
114
+ };
115
+ }, [open]);
116
+ useEffect(() => {
117
+ if (!open) return;
118
+ const target = panel.current?.querySelector(
119
+ ".rx-modal__body input, .rx-modal__body select, .rx-modal__body textarea, .rx-modal__body button"
120
+ ) || panel.current;
121
+ target?.focus();
122
+ }, [open]);
123
+ if (!open) return null;
124
+ return createPortal(
125
+ /* @__PURE__ */ jsx5("div", { className: "rx-scrim", onMouseDown: (e) => {
126
+ if (e.target === e.currentTarget) onClose();
127
+ }, children: /* @__PURE__ */ jsxs3("div", { className: "rx-modal", role: "dialog", "aria-modal": "true", "aria-label": title, tabIndex: -1, ref: panel, children: [
128
+ /* @__PURE__ */ jsxs3("div", { className: "rx-modal__head", children: [
129
+ /* @__PURE__ */ jsx5("div", { className: "rx-modal__title", children: title }),
130
+ /* @__PURE__ */ jsx5(Button, { variant: "ghost", size: "sm", iconOnly: true, icon: "x", "aria-label": "Close", onClick: onClose })
131
+ ] }),
132
+ /* @__PURE__ */ jsx5("div", { className: "rx-modal__body", children }),
133
+ footer && /* @__PURE__ */ jsx5("div", { className: "rx-modal__foot", children: footer })
134
+ ] }) }),
135
+ document.body
136
+ );
137
+ }
138
+
139
+ // src/DataTable.tsx
140
+ import { useMemo, useState } from "react";
141
+ import { Fragment, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
142
+ function DataTable({
143
+ columns,
144
+ rows,
145
+ pageSize = 4,
146
+ selected,
147
+ onToggle,
148
+ onTogglePage
149
+ }) {
150
+ const [sort, setSort] = useState(
151
+ { key: columns.find((c) => c.sortable)?.key, dir: -1 }
152
+ );
153
+ const [page, setPage] = useState(0);
154
+ const sorted = useMemo(() => {
155
+ if (!sort.key) return rows;
156
+ const k = sort.key;
157
+ return [...rows].sort((a, b) => (a[k] > b[k] ? 1 : a[k] < b[k] ? -1 : 0) * sort.dir);
158
+ }, [rows, sort]);
159
+ const pages = Math.max(1, Math.ceil(sorted.length / pageSize));
160
+ const safePage = Math.min(page, pages - 1);
161
+ const slice = sorted.slice(safePage * pageSize, safePage * pageSize + pageSize);
162
+ const onSort = (k) => {
163
+ setSort((s) => s.key === k ? { key: k, dir: s.dir === 1 ? -1 : 1 } : { key: k, dir: -1 });
164
+ setPage(0);
165
+ };
166
+ const caret = (k) => sort.key === k ? sort.dir === 1 ? " \u25B2" : " \u25BC" : " \u2195";
167
+ const pageAllOn = slice.length > 0 && slice.every((r) => selected.has(r.name));
168
+ return /* @__PURE__ */ jsxs4(Fragment, { children: [
169
+ /* @__PURE__ */ jsxs4("table", { className: "ui-table ui-table--hover ui-table--zebra", children: [
170
+ /* @__PURE__ */ jsx6("thead", { children: /* @__PURE__ */ jsxs4("tr", { children: [
171
+ /* @__PURE__ */ jsx6("th", { scope: "col", children: /* @__PURE__ */ jsx6(
172
+ "input",
173
+ {
174
+ type: "checkbox",
175
+ checked: pageAllOn,
176
+ "aria-label": "Select all rows on this page",
177
+ onChange: () => onTogglePage(slice.map((r) => r.name))
178
+ }
179
+ ) }),
180
+ columns.map((c) => (
181
+ // The sort control is a real <button> inside the header cell. It used to be
182
+ // role="button" ON the <th>, which threw away the columnheader role and put
183
+ // aria-sort on a role that forbids it.
184
+ /* @__PURE__ */ jsx6(
185
+ "th",
186
+ {
187
+ scope: "col",
188
+ className: [c.num && "ui-table__num", c.sortable && "rx-sortable"].filter(Boolean).join(" "),
189
+ "aria-sort": c.sortable ? sort.key === c.key ? sort.dir === 1 ? "ascending" : "descending" : "none" : void 0,
190
+ children: c.sortable ? /* @__PURE__ */ jsxs4("button", { type: "button", className: "rx-sort", onClick: () => onSort(c.key), children: [
191
+ c.label,
192
+ /* @__PURE__ */ jsx6("span", { className: "rx-caret", "aria-hidden": "true", children: caret(c.key) })
193
+ ] }) : c.label
194
+ },
195
+ c.key
196
+ )
197
+ ))
198
+ ] }) }),
199
+ /* @__PURE__ */ jsx6("tbody", { children: slice.map((r) => /* @__PURE__ */ jsxs4("tr", { children: [
200
+ /* @__PURE__ */ jsx6("td", { children: /* @__PURE__ */ jsx6(
201
+ "input",
202
+ {
203
+ type: "checkbox",
204
+ checked: selected.has(r.name),
205
+ "aria-label": `Select ${r.name}`,
206
+ onChange: () => onToggle(r.name)
207
+ }
208
+ ) }),
209
+ columns.map((c) => /* @__PURE__ */ jsx6("td", { className: c.num ? "ui-table__num" : void 0, children: c.render ? c.render(r) : String(r[c.key]) }, c.key))
210
+ ] }, r.name)) })
211
+ ] }),
212
+ /* @__PURE__ */ jsxs4("div", { className: "rx-pager", children: [
213
+ /* @__PURE__ */ jsxs4("span", { className: "rx-pager__info", children: [
214
+ "Page ",
215
+ safePage + 1,
216
+ " of ",
217
+ pages,
218
+ " \xB7 ",
219
+ sorted.length,
220
+ " rows"
221
+ ] }),
222
+ /* @__PURE__ */ jsx6(
223
+ Button,
224
+ {
225
+ variant: "ghost",
226
+ size: "sm",
227
+ icon: "chevronLeft",
228
+ disabled: safePage === 0,
229
+ onClick: () => setPage(safePage - 1),
230
+ children: "Prev"
231
+ }
232
+ ),
233
+ /* @__PURE__ */ jsx6(
234
+ Button,
235
+ {
236
+ variant: "ghost",
237
+ size: "sm",
238
+ iconRight: "chevronRight",
239
+ disabled: safePage >= pages - 1,
240
+ onClick: () => setPage(safePage + 1),
241
+ children: "Next"
242
+ }
243
+ )
244
+ ] })
245
+ ] });
246
+ }
247
+ export {
248
+ Badge,
249
+ Button,
250
+ Card,
251
+ DataTable,
252
+ Icon,
253
+ Modal
254
+ };
@@ -9,93 +9,106 @@
9
9
  // iconCategories drives the Storybook grid. Add a glyph to the right group.
10
10
 
11
11
  const NAV = {
12
- chevronDown: '<path d="m6 9 6 6 6-6"/>',
13
- chevronUp: '<path d="m18 15-6-6-6 6"/>',
14
- chevronLeft: '<path d="m15 6-6 6 6 6"/>',
15
- chevronRight: '<path d="m9 6 6 6-6 6"/>',
16
- arrowRight: '<path d="M5 12h14M13 6l6 6-6 6"/>',
17
- arrowLeft: '<path d="M19 12H5M11 6l-6 6 6 6"/>',
18
- arrowUp: '<path d="M12 19V5M6 11l6-6 6 6"/>',
19
- arrowDown: '<path d="M12 5v14M6 13l6 6 6-6"/>',
20
- menu: '<path d="M3 6h18M3 12h18M3 18h18"/>',
21
- x: '<path d="M18 6 6 18M6 6l12 12"/>',
22
- externalLink: '<path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>',
23
- home: '<path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/>',
24
- search: '<circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/>',
25
- compass: '<circle cx="12" cy="12" r="9"/><path d="m15 9-2 6-4 2 2-6z"/>',
26
- globe: '<circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a15 15 0 0 1 0 18 15 15 0 0 1 0-18z"/>',
12
+ chevronDown: '<path d="M6 9l6 6 6-6" stroke-linecap="round" stroke-linejoin="round"/>',
13
+ chevronUp: '<polyline points="18 15 12 9 6 15"/>',
14
+ chevronLeft: '<polyline points="15 18 9 12 15 6"/>',
15
+ chevronRight: '<path d="M9 18l6-6-6-6" stroke-linecap="round" stroke-linejoin="round"/>',
16
+ arrowRight: '<path d="M5 12h14M12 5l7 7-7 7" stroke-linecap="round" stroke-linejoin="round"/>',
17
+ arrowLeft: '<path d="M19 12H5M12 19l-7-7 7-7" stroke-linecap="round" stroke-linejoin="round"/>',
18
+ arrowUp: '<line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/>',
19
+ arrowDown: '<line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/>',
20
+ menu: '<path d="M3 12h18M3 6h18M3 18h18" stroke-linecap="round"/>',
21
+ x: '<path d="M18 6L6 18M6 6l12 12" stroke-linecap="round"/>',
22
+ externalLink: '<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/>',
23
+ home: '<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/>',
24
+ search: '<path d="M21 21l-4.35-4.35M17 11A6 6 0 1 1 5 11a6 6 0 0 1 12 0z" stroke-linecap="round"/>',
25
+ compass: '<circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"/>',
26
+ globe: '<circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>',
27
27
  };
28
28
 
29
29
  const ACTIONS = {
30
- plus: '<path d="M12 5v14M5 12h14"/>',
31
- minus: '<path d="M5 12h14"/>',
32
- check: '<path d="M20 6 9 17l-5-5"/>',
33
- edit: '<path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z"/>',
34
- copy: '<rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/>',
35
- trash: '<path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M6 6v14a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V6"/><path d="M10 11v6M14 11v6"/>',
36
- download: '<path d="M12 3v12"/><path d="m7 10 5 5 5-5"/><path d="M5 21h14"/>',
37
- upload: '<path d="M12 21V9"/><path d="m7 14 5-5 5 5"/><path d="M5 3h14"/>',
38
- share: '<circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="m8.6 13.5 6.8 4M15.4 6.5l-6.8 4"/>',
39
- refresh: '<path d="M21 12a9 9 0 1 1-2.6-6.3L21 8"/><path d="M21 3v5h-5"/>',
40
- filter: '<path d="M3 4h18l-7 8v6l-4 2v-8z"/>',
41
- gear: '<circle cx="12" cy="12" r="3"/><path d="M19.4 13a7.9 7.9 0 0 0 0-2l1.6-1.3-1.6-2.7-1.9.8a7.6 7.6 0 0 0-1.7-1L15.5 5h-3l-.3 1.8a7.6 7.6 0 0 0-1.7 1l-1.9-.8-1.6 2.7L8.6 11a7.9 7.9 0 0 0 0 2l-1.6 1.3 1.6 2.7 1.9-.8a7.6 7.6 0 0 0 1.7 1l.3 1.8h3l.3-1.8a7.6 7.6 0 0 0 1.7-1l1.9.8 1.6-2.7z"/>',
42
- link: '<path d="M10 13a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-1 1"/><path d="M14 11a5 5 0 0 0-7 0l-3 3a5 5 0 0 0 7 7l1-1"/>',
43
- moreHorizontal: '<circle cx="5" cy="12" r="1.3"/><circle cx="12" cy="12" r="1.3"/><circle cx="19" cy="12" r="1.3"/>',
44
- moreVertical: '<circle cx="12" cy="5" r="1.3"/><circle cx="12" cy="12" r="1.3"/><circle cx="12" cy="19" r="1.3"/>',
30
+ plus: '<path d="M12 5v14M5 12h14" stroke-linecap="round"/>',
31
+ minus: '<path d="M5 12h14" stroke-linecap="round"/>',
32
+ check: '<path d="M20 6L9 17l-5-5" stroke-linecap="round" stroke-linejoin="round"/>',
33
+ edit: '<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>',
34
+ copy: '<rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>',
35
+ trash: '<path d="M3 6h18M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6M10 11v6M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/>',
36
+ download: '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M7 10l5 5 5-5"/><path d="M12 15V3"/>',
37
+ upload: '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M17 8l-5-5-5 5"/><path d="M12 3v12"/>',
38
+ share: '<circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/>',
39
+ refresh: '<path d="M23 4v6h-6"/><path d="M1 20v-6h6"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>',
40
+ filter: '<path d="M22 3H2l8 9.46V19l4 2v-8.54L22 3z"/>',
41
+ gear: '<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>',
42
+ link: '<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>',
43
+ moreHorizontal: '<circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/>',
44
+ moreVertical: '<circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/>',
45
+ code: '<path d="M16 18l6-6-6-6M8 6l-6 6 6 6"/>',
46
+ droplet: '<path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z"/>',
47
+ type: '<polyline points="4 7 4 4 20 4 20 7"/><line x1="9" y1="20" x2="15" y2="20"/><line x1="12" y1="4" x2="12" y2="20"/>',
45
48
  };
46
49
 
47
50
  const STATUS = {
48
- circleCheck: '<circle cx="12" cy="12" r="9"/><path d="m8.5 12 2.5 2.5 5-5"/>',
49
- circleAlert: '<circle cx="12" cy="12" r="9"/><path d="M12 8v4M12 16h.01"/>',
50
- alert: '<path d="M12 3 2 20h20z"/><path d="M12 9v5M12 17h.01"/>',
51
- info: '<circle cx="12" cy="12" r="9"/><path d="M12 8h.01M11 12h1v4h1"/>',
52
- bell: '<path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/>',
53
- clock: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
54
- eye: '<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/>',
55
- eyeOff: '<path d="M9.9 4.2A9.6 9.6 0 0 1 12 4c6.5 0 10 8 10 8a13.2 13.2 0 0 1-2.2 2.9M6.6 6.6A13.2 13.2 0 0 0 2 12s3.5 7 10 7a9.6 9.6 0 0 0 4-.9"/><path d="M9.9 9.9a3 3 0 0 0 4.2 4.2"/><path d="m2 2 20 20"/>',
56
- lock: '<rect x="5" y="11" width="14" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/>',
57
- unlock: '<rect x="5" y="11" width="14" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 7.5-2"/>',
58
- shield: '<path d="M12 3 4 6v6c0 5 3.5 8 8 9 4.5-1 8-4 8-9V6z"/>',
59
- key: '<rect x="4" y="10" width="16" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/>',
60
- star: '<path d="m12 3 2.9 5.9 6.5.9-4.7 4.6 1.1 6.5L12 18l-5.8 3 1.1-6.5L2.6 9.8l6.5-.9z"/>',
61
- heart: '<path d="M12 21s-7-4.5-9.4-9A5 5 0 0 1 12 6a5 5 0 0 1 9.4 6c-2.4 4.5-9.4 9-9.4 9z"/>',
51
+ circleCheck: '<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><path d="M22 4L12 14.01l-3-3"/>',
52
+ circleAlert: '<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>',
53
+ alert: '<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><path stroke-linecap="round" d="M12 9v4"/><path stroke-linecap="round" d="M12 17h.01"/>',
54
+ info: '<circle cx="12" cy="12" r="10"/><path stroke-linecap="round" d="M12 16v-4"/><path stroke-linecap="round" d="M12 8h.01"/>',
55
+ bell: '<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/>',
56
+ clock: '<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>',
57
+ eye: '<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/>',
58
+ eyeOff: '<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/>',
59
+ lock: '<rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
60
+ unlock: '<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 9.9-1"/>',
61
+ shield: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>',
62
+ key: '<path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/>',
63
+ star: '<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>',
64
+ heart: '<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>',
65
+ circleX: '<circle cx="12" cy="12" r="10"/><path d="M15 9l-6 6M9 9l6 6"/>',
66
+ target: '<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/>',
67
+ sun: '<circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>',
68
+ moon: '<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>',
62
69
  };
63
70
 
64
71
  const DATA = {
65
- card: '<rect x="2" y="5" width="20" height="14" rx="2"/><path d="M2 10h20M6 15h4"/>',
66
- wallet: '<path d="M4 6a2 2 0 0 1 2-2h11a1 1 0 0 1 1 1v3"/><path d="M4 6v12a2 2 0 0 0 2 2h13a1 1 0 0 0 1-1v-3"/><path d="M21 11h-4a2 2 0 0 0 0 4h4a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1z"/>',
67
- chart: '<path d="M3 3v18h18"/><path d="M8 17v-5M13 17V8M18 17v-8"/>',
68
- trendingUp: '<path d="m3 17 7-7 4 4 7-7"/><path d="M17 7h4v4"/>',
69
- trendingDown: '<path d="m3 7 7 7 4-4 7 7"/><path d="M17 17h4v-4"/>',
70
- table: '<rect x="3" y="4" width="18" height="16" rx="2"/><path d="M3 10h18M3 15h18M9 4v16M15 4v16"/>',
71
- database: '<ellipse cx="12" cy="5" rx="8" ry="3"/><path d="M4 5v6c0 1.7 3.6 3 8 3s8-1.3 8-3V5"/><path d="M4 11v6c0 1.7 3.6 3 8 3s8-1.3 8-3v-6"/>',
72
- layers: '<path d="m12 2 9 5-9 5-9-5z"/><path d="m3 12 9 5 9-5M3 17l9 5 9-5"/>',
73
- cube: '<path d="M12 2 3 7v10l9 5 9-5V7z"/><path d="M3 7l9 5 9-5M12 12v10"/>',
74
- bolt: '<path d="M13 2 3 14h8l-1 8 10-12h-8z"/>',
72
+ card: '<rect x="1" y="4" width="22" height="16" rx="2" ry="2"/><line x1="1" y1="10" x2="23" y2="10"/>',
73
+ wallet: '<path d="M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1"/><path d="M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4"/>',
74
+ chart: '<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>',
75
+ trendingUp: '<polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/><polyline points="17 6 23 6 23 12"/>',
76
+ trendingDown: '<polyline points="23 18 13.5 8.5 8.5 13.5 1 6"/><polyline points="17 18 23 18 23 12"/>',
77
+ table: '<path d="M9 3H5a2 2 0 0 0-2 2v4m6-6h10a2 2 0 0 1 2 2v4M9 3v18m0 0h10a2 2 0 0 0 2-2V9M9 21H5a2 2 0 0 1-2-2V9m0 0h18"/>',
78
+ database: '<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>',
79
+ layers: '<polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/>',
80
+ cube: '<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/>',
81
+ bolt: '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>',
82
+ grid: '<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/>',
83
+ layout: '<rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 21V9"/>',
84
+ monitor: '<rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/>',
75
85
  };
76
86
 
77
87
  const FILES = {
78
- doc: '<path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><path d="M14 3v6h6M8 13h8M8 17h5"/>',
79
- folder: '<path d="M4 5h5l2 2h9a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1z"/>',
80
- image: '<rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="m21 15-5-5L5 21"/>',
81
- play: '<path d="M7 4v16l13-8z"/>',
82
- pause: '<rect x="7" y="4" width="3.5" height="16" rx="1"/><rect x="13.5" y="4" width="3.5" height="16" rx="1"/>',
83
- calendar: '<rect x="3" y="5" width="18" height="16" rx="2"/><path d="M3 9h18M8 3v4M16 3v4"/>',
88
+ doc: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/>',
89
+ folder: '<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>',
90
+ image: '<rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="M21 15l-5-5L5 21"/>',
91
+ play: '<polygon points="5 3 19 12 5 21 5 3"/>',
92
+ pause: '<rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/>',
93
+ calendar: '<rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/>',
84
94
  };
85
95
 
86
96
  const COMMS = {
87
- mail: '<rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/>',
88
- chat: '<path d="M21 15a2 2 0 0 1-2 2H8l-4 4V5a2 2 0 0 1 2-2h13a2 2 0 0 1 2 2z"/>',
89
- phone: '<path d="M6 3h3l2 5-2.5 1.5a11 11 0 0 0 5 5L16 13l5 2v3a2 2 0 0 1-2 2A16 16 0 0 1 3 5a2 2 0 0 1 2-2z"/>',
90
- user: '<circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/>',
91
- logout: '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><path d="M16 17l5-5-5-5"/><path d="M21 12H9"/>',
92
- plug: '<path d="M9 2v6M15 2v6M7 8h10v3a5 5 0 0 1-10 0z"/><path d="M12 16v6"/>',
93
- sparkle: '<path d="M12 3v4M12 17v4M3 12h4M17 12h4M6 6l2 2M16 16l2 2M18 6l-2 2M8 16l-2 2"/>',
97
+ mail: '<path d="M4 4h16c1.1 0 2 .9 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2z"/><path d="M22 6l-10 7L2 6"/>',
98
+ chat: '<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/>',
99
+ phone: '<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/>',
100
+ user: '<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>',
101
+ logout: '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/>',
102
+ plug: '<path d="M12 22v-5"/><path d="M15 8V2"/><path d="M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z"/><path d="M9 8V2"/>',
103
+ sparkle: '<path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"/><path d="M20 2v4"/><path d="M22 4h-4"/><circle cx="4" cy="20" r="2"/>',
104
+ card: '<rect x="1" y="4" width="22" height="16" rx="2" ry="2"/><line x1="1" y1="10" x2="23" y2="10"/>',
105
+ doc: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/>',
106
+ chart: '<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>',
94
107
  };
95
108
 
96
109
  const BRAND = {
97
- github: '<path d="M9 19c-4 1.4-4-2.2-6-2.6m12 5v-3.4a3 3 0 0 0-.8-2.3c2.7-.3 5.5-1.3 5.5-6a4.6 4.6 0 0 0-1.3-3.2 4.3 4.3 0 0 0-.1-3.2s-1-.3-3.4 1.3a11.6 11.6 0 0 0-6 0C6.9 2.7 5.9 3 5.9 3a4.3 4.3 0 0 0-.1 3.2A4.6 4.6 0 0 0 4.5 9.4c0 4.6 2.8 5.6 5.5 6a3 3 0 0 0-.8 2.3V21"/>',
98
- linkedin: '<path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-4 0v7h-4v-7a6 6 0 0 1 6-6z"/><rect x="2" y="9" width="4" height="12"/><circle cx="4" cy="4" r="2"/>',
110
+ github: '<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/>',
111
+ linkedin: '<path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"/><rect x="2" y="9" width="4" height="12"/><circle cx="4" cy="4" r="2"/>',
99
112
  };
100
113
 
101
114
  const ICONS = { ...NAV, ...ACTIONS, ...STATUS, ...DATA, ...FILES, ...COMMS, ...BRAND };
@@ -112,10 +125,20 @@ export const iconCategories = [
112
125
  { name: 'Brand', names: Object.keys(BRAND) },
113
126
  ];
114
127
 
128
+ // Every glyph the kit ships is DECORATIVE: it repeats meaning the control's own
129
+ // text or aria-label already carries. So icon() always emits `aria-hidden="true"`
130
+ // (never announced) + `focusable="false"` (never a stray tab stop in legacy IE/
131
+ // Edge), exactly like the hand-written SVGs elsewhere in the kit.
132
+ //
133
+ // The corollary is a rule, not a suggestion: an icon-only control must name
134
+ // itself — button({ iconOnly }) does it from `label`, everything else needs an
135
+ // explicit aria-label. There is no `icon(name, cls, label)` escape hatch on
136
+ // purpose; a named graphic would let a caller hang a control's only accessible
137
+ // name off a decorative glyph, which is what we just fixed.
115
138
  export const icon = (name, cls = '') =>
116
- `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"${cls ? ` class="${cls}"` : ''}>${ICONS[name] || ''}</svg>`;
139
+ `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"${cls ? ` class="${cls}"` : ''}>${ICONS[name] || ''}</svg>`;
117
140
 
118
141
  export const iconNames = Object.keys(ICONS);
119
142
 
120
- export const sun = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>';
121
- export const moon = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>';
143
+ export const sun = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>';
144
+ export const moon = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>';
@@ -10,9 +10,11 @@
10
10
  // ship in styles/feedback.css (part of the kit stylesheet). Accent-aware.
11
11
  import { esc } from './index.js';
12
12
 
13
- const IC_MSG = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H8l-4 4V5a2 2 0 0 1 2-2h13a2 2 0 0 1 2 2z"/></svg>';
14
- const IC_LINES = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 5h16M4 12h10M4 19h7"/></svg>';
15
- const IC_X = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg>';
13
+ // Decorative, like every glyph in the kit the pill and the buttons carry their
14
+ // own text or aria-label, so the SVGs stay out of the accessibility tree.
15
+ const IC_MSG = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M21 15a2 2 0 0 1-2 2H8l-4 4V5a2 2 0 0 1 2-2h13a2 2 0 0 1 2 2z"/></svg>';
16
+ const IC_LINES = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true" focusable="false"><path d="M4 5h16M4 12h10M4 19h7"/></svg>';
17
+ const IC_X = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true" focusable="false"><path d="M6 6l12 12M18 6L6 18"/></svg>';
16
18
  const CHECK = '<svg class="ui-fbck" viewBox="0 0 150 150" aria-hidden="true"><path class="ui-fbck-t" d="M40 78l24 24 46-50"/><path class="ui-fbck-m" d="M40 78l24 24 46-50"/><path class="ui-fbck-s" d="M40 78l24 24 46-50"/></svg>';
17
19
 
18
20
  // The widget markup — append once to the page (e.g. document.body). Copy is
@@ -36,21 +38,21 @@ export function feedbackWidget({
36
38
  <div data-fb-form>
37
39
  <div class="ui-fbc__head">
38
40
  <span class="ui-fbc__chip">${IC_LINES}<span data-fb-chip>this page</span></span>
39
- <button class="ui-fbc__x" data-fb-close aria-label="Close">${IC_X}</button>
41
+ <button type="button" class="ui-fbc__x" data-fb-close aria-label="Close">${IC_X}</button>
40
42
  </div>
41
43
  <div class="ui-fbc__quote"><span class="ui-fbc__qm">&#8220;</span><q data-fb-quote></q></div>
42
44
  <div class="ui-fbc__body"><textarea data-fb-note maxlength="6000" placeholder="${esc(placeholder)}" aria-label="Your note"></textarea></div>
43
45
  <div class="ui-fbc__err" data-fb-err></div>
44
46
  <div class="ui-fbc__foot">
45
- <button class="ui-fbbtn ghost" data-fb-cancel>Cancel</button>
46
- <button class="ui-fbbtn primary" data-fb-send disabled><span class="ui-fbc__send" data-fb-sendlb>Send feedback</span></button>
47
+ <button type="button" class="ui-fbbtn ghost" data-fb-cancel>Cancel</button>
48
+ <button type="button" class="ui-fbbtn primary" data-fb-send disabled><span class="ui-fbc__send" data-fb-sendlb>Send feedback</span></button>
47
49
  </div>
48
50
  </div>
49
51
  <div class="ui-fbc__done" data-fb-done style="display:none">
50
52
  <div class="ui-fbc__ck">${CHECK}</div>
51
53
  <h4>${esc(doneTitle)}</h4>
52
54
  <p>${esc(doneBody)}</p>
53
- <div class="row"><button class="ui-fbbtn ghost" data-fb-done-close>Close</button></div>
55
+ <div class="row"><button type="button" class="ui-fbbtn ghost" data-fb-done-close>Close</button></div>
54
56
  </div>
55
57
  </div>`;
56
58
  }