@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.
- package/LICENSE +20 -5
- package/README.md +88 -19
- package/package.json +21 -12
- package/react/README.md +47 -0
- package/react/dist/index.css +76 -0
- package/react/dist/index.d.ts +58 -0
- package/react/dist/index.js +254 -0
- package/src/assets/icons.js +95 -72
- package/src/components/feedback.js +9 -7
- package/src/components/index.js +90 -15
- package/src/components/tabs.js +6 -2
- package/src/components/topbar.js +39 -5
- package/src/styles/badge.css +11 -13
- package/src/styles/callout.css +5 -5
- package/src/styles/card.css +1 -1
- package/src/styles/drawer.css +2 -4
- package/src/styles/dropdown.css +1 -1
- package/src/styles/empty.css +1 -1
- package/src/styles/feedback.css +12 -6
- package/src/styles/input.css +4 -1
- package/src/styles/layout.css +1 -1
- package/src/styles/motion.css +1 -1
- package/src/styles/segmented.css +3 -0
- package/src/styles/tabs.css +3 -0
- package/src/styles/topbar.css +3 -3
- package/src/tokens/accents.css +6 -2
- package/src/tokens/brand.generated.css +1 -1
- package/src/tokens/tokens.css +54 -0
|
@@ -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
|
+
};
|
package/src/assets/icons.js
CHANGED
|
@@ -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="
|
|
13
|
-
chevronUp: '<
|
|
14
|
-
chevronLeft: '<
|
|
15
|
-
chevronRight: '<path d="
|
|
16
|
-
arrowRight: '<path d="M5
|
|
17
|
-
arrowLeft: '<path d="M19
|
|
18
|
-
arrowUp: '<
|
|
19
|
-
arrowDown: '<
|
|
20
|
-
menu: '<path d="M3 6h18M3
|
|
21
|
-
x: '<path d="M18
|
|
22
|
-
externalLink: '<path d="
|
|
23
|
-
home: '<path d="
|
|
24
|
-
search: '<
|
|
25
|
-
compass: '<circle cx="12" cy="12" r="
|
|
26
|
-
globe: '<circle cx="12" cy="12" r="
|
|
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
|
|
33
|
-
edit: '<path d="
|
|
34
|
-
copy: '<rect x="9" y="9" width="
|
|
35
|
-
trash: '<path d="M3
|
|
36
|
-
download: '<path d="
|
|
37
|
-
upload: '<path d="
|
|
38
|
-
share: '<circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><
|
|
39
|
-
refresh: '<path d="
|
|
40
|
-
filter: '<path d="
|
|
41
|
-
gear: '<circle cx="12" cy="12" r="3"/><path d="M19.4
|
|
42
|
-
link: '<path d="M10 13a5 5 0 0 0 7
|
|
43
|
-
moreHorizontal: '<circle cx="
|
|
44
|
-
moreVertical: '<circle cx="12" cy="
|
|
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: '<
|
|
49
|
-
circleAlert: '<circle cx="12" cy="12" r="
|
|
50
|
-
alert: '<path d="
|
|
51
|
-
info: '<circle cx="12" cy="12" r="
|
|
52
|
-
bell: '<path d="M18
|
|
53
|
-
clock: '<circle cx="12" cy="12" r="
|
|
54
|
-
eye: '<path d="
|
|
55
|
-
eyeOff: '<path d="
|
|
56
|
-
lock: '<rect x="
|
|
57
|
-
unlock: '<rect x="
|
|
58
|
-
shield: '<path d="M12
|
|
59
|
-
key: '<
|
|
60
|
-
star: '<
|
|
61
|
-
heart: '<path d="
|
|
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="
|
|
66
|
-
wallet: '<path d="
|
|
67
|
-
chart: '<
|
|
68
|
-
trendingUp: '<
|
|
69
|
-
trendingDown: '<
|
|
70
|
-
table: '<
|
|
71
|
-
database: '<ellipse cx="12" cy="5" rx="
|
|
72
|
-
layers: '<
|
|
73
|
-
cube: '<path d="
|
|
74
|
-
bolt: '<
|
|
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
|
|
79
|
-
folder: '<path d="
|
|
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="
|
|
81
|
-
play: '<
|
|
82
|
-
pause: '<rect x="
|
|
83
|
-
calendar: '<rect x="3" y="
|
|
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: '<
|
|
88
|
-
chat: '<path d="M21
|
|
89
|
-
phone: '<path d="
|
|
90
|
-
user: '<circle cx="12" cy="
|
|
91
|
-
logout: '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><
|
|
92
|
-
plug: '<path d="
|
|
93
|
-
sparkle: '<path d="
|
|
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-
|
|
98
|
-
linkedin: '<path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
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">“</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
|
}
|