tempest-react-sdk 0.29.0 → 0.30.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/bin/lib/alias/index.mjs +9 -3
- package/bin/lib/alias/tsconfig.mjs +48 -5
- package/bin/lib/alias/typescript.mjs +72 -2
- package/bin/lib/alias/typescript.test.mjs +120 -0
- package/bin/lib/css/extract.mjs +2 -3
- package/bin/lib/doctor/doctor.e2e.test.mjs +44 -0
- package/bin/tempest.mjs +17 -2
- package/dist/components/Chat/Chat.cjs +2 -0
- package/dist/components/Chat/Chat.cjs.map +1 -0
- package/dist/components/Chat/Chat.js +126 -0
- package/dist/components/Chat/Chat.js.map +1 -0
- package/dist/components/Chat/Chat.module.cjs +2 -0
- package/dist/components/Chat/Chat.module.cjs.map +1 -0
- package/dist/components/Chat/Chat.module.js +32 -0
- package/dist/components/Chat/Chat.module.js.map +1 -0
- package/dist/components/Chat/ChatComposer.cjs +2 -0
- package/dist/components/Chat/ChatComposer.cjs.map +1 -0
- package/dist/components/Chat/ChatComposer.js +67 -0
- package/dist/components/Chat/ChatComposer.js.map +1 -0
- package/dist/components/Chat/chat-groups.cjs +2 -0
- package/dist/components/Chat/chat-groups.cjs.map +1 -0
- package/dist/components/Chat/chat-groups.js +89 -0
- package/dist/components/Chat/chat-groups.js.map +1 -0
- package/dist/components/Markdown/Markdown.cjs +2 -0
- package/dist/components/Markdown/Markdown.cjs.map +1 -0
- package/dist/components/Markdown/Markdown.js +124 -0
- package/dist/components/Markdown/Markdown.js.map +1 -0
- package/dist/components/Markdown/Markdown.module.cjs +2 -0
- package/dist/components/Markdown/Markdown.module.cjs.map +1 -0
- package/dist/components/Markdown/Markdown.module.js +23 -0
- package/dist/components/Markdown/Markdown.module.js.map +1 -0
- package/dist/components/Markdown/markdown-parse.cjs +7 -0
- package/dist/components/Markdown/markdown-parse.cjs.map +1 -0
- package/dist/components/Markdown/markdown-parse.js +269 -0
- package/dist/components/Markdown/markdown-parse.js.map +1 -0
- package/dist/components/Markdown/markdown-url.cjs +2 -0
- package/dist/components/Markdown/markdown-url.cjs.map +1 -0
- package/dist/components/Markdown/markdown-url.js +28 -0
- package/dist/components/Markdown/markdown-url.js.map +1 -0
- package/dist/components/Masonry/Masonry.cjs +2 -0
- package/dist/components/Masonry/Masonry.cjs.map +1 -0
- package/dist/components/Masonry/Masonry.js +70 -0
- package/dist/components/Masonry/Masonry.js.map +1 -0
- package/dist/components/Masonry/Masonry.module.cjs +2 -0
- package/dist/components/Masonry/Masonry.module.cjs.map +1 -0
- package/dist/components/Masonry/Masonry.module.js +10 -0
- package/dist/components/Masonry/Masonry.module.js.map +1 -0
- package/dist/components/Masonry/masonry-layout.cjs +2 -0
- package/dist/components/Masonry/masonry-layout.cjs.map +1 -0
- package/dist/components/Masonry/masonry-layout.js +20 -0
- package/dist/components/Masonry/masonry-layout.js.map +1 -0
- package/dist/components/Transfer/Transfer.cjs +2 -0
- package/dist/components/Transfer/Transfer.cjs.map +1 -0
- package/dist/components/Transfer/Transfer.js +163 -0
- package/dist/components/Transfer/Transfer.js.map +1 -0
- package/dist/components/Transfer/Transfer.module.cjs +2 -0
- package/dist/components/Transfer/Transfer.module.cjs.map +1 -0
- package/dist/components/Transfer/Transfer.module.js +19 -0
- package/dist/components/Transfer/Transfer.module.js.map +1 -0
- package/dist/components/Transfer/transfer-state.cjs +2 -0
- package/dist/components/Transfer/transfer-state.cjs.map +1 -0
- package/dist/components/Transfer/transfer-state.js +59 -0
- package/dist/components/Transfer/transfer-state.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +493 -0
- package/dist/tempest-react-sdk.js +114 -105
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Transfer.cjs","names":[],"sources":["../../../src/components/Transfer/Transfer.tsx"],"sourcesContent":["import { useId, useMemo, useState, type HTMLAttributes, type ReactNode } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nimport { Checkbox } from \"../Checkbox\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport {\n applyMove,\n filterItems,\n movableIds,\n splitSides,\n transferStrings,\n type TransferItem,\n type TransferSide,\n} from \"./transfer-state\";\nimport styles from \"./Transfer.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\" | \"onChange\" | \"defaultValue\";\n\nexport interface TransferProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps> {\n /** The whole catalogue. Both panes are derived from it. */\n items: readonly TransferItem[];\n /** Ids on the target side. Controlled. */\n value: readonly string[];\n /** Next value, always in catalogue order. */\n onChange: (value: string[]) => void;\n /** Heading of the left pane. Default `\"Disponíveis\"`. */\n sourceTitle?: ReactNode;\n /** Heading of the right pane. Default `\"Selecionados\"`. */\n targetTitle?: ReactNode;\n /** Show a search box above each pane. Default `true` past 8 items. */\n searchable?: boolean;\n /** Custom row body. Gets the item and which side it is on. */\n renderItem?: (item: TransferItem, side: TransferSide) => ReactNode;\n /** Locale for labels. Default `\"pt-BR\"`. */\n locale?: \"pt-BR\" | \"en\";\n /** Height of each pane's scroll area, any CSS length. Default `\"16rem\"`. */\n height?: string;\n /** Block every move. */\n disabled?: boolean;\n}\n\n/**\n * Two panes and a set of move controls: pick a subset out of a catalogue.\n *\n * Controlled on **ids of the target side only**. The panes are derived on every\n * render, never stored, because two stored lists drift the moment the catalogue\n * changes under them — a permission removed upstream lingers in whichever pane\n * held it, and an id in both panes is a bug nobody can see.\n *\n * @example\n * const [roles, setRoles] = useState<string[]>([]);\n *\n * <Transfer\n * items={allRoles}\n * value={roles}\n * onChange={setRoles}\n * sourceTitle=\"Papéis disponíveis\"\n * targetTitle=\"Papéis do usuário\"\n * />\n */\nexport function Transfer({\n items,\n value,\n onChange,\n sourceTitle,\n targetTitle,\n searchable,\n renderItem,\n locale = \"pt-BR\",\n height = \"16rem\",\n disabled = false,\n className,\n ...rest\n}: TransferProps) {\n const strings = transferStrings(locale);\n const baseId = useId();\n const [checked, setChecked] = useState<Set<string>>(new Set());\n const [queries, setQueries] = useState<{ source: string; target: string }>({\n source: \"\",\n target: \"\",\n });\n const [announcement, setAnnouncement] = useState(\"\");\n\n const { source, target } = useMemo(() => splitSides(items, value), [items, value]);\n const withSearch = searchable ?? items.length > 8;\n\n /**\n * What each pane is showing right now — the filter included.\n *\n * Every control acts on this, not on the pane's full contents: filtering to\n * \"sao\" and pressing \"move all\" has to move what you are looking at. Moving\n * rows the filter hid is the kind of surprise that makes people stop trusting\n * the bulk button.\n */\n const visible: Record<TransferSide, TransferItem[]> = {\n source: withSearch ? filterItems(source, queries.source) : [...source],\n target: withSearch ? filterItems(target, queries.target) : [...target],\n };\n\n const panes: Array<{ side: TransferSide; title: ReactNode; items: TransferItem[] }> = [\n { side: \"source\", title: sourceTitle ?? strings.sourceTitle, items: source },\n { side: \"target\", title: targetTitle ?? strings.targetTitle, items: target },\n ];\n\n /**\n * Move ids and drop them from the checked set.\n *\n * Clearing the checks is the point: after a move the rows are on the other\n * side, and leaving them checked means the next click on the opposite button\n * sends them straight back — which reads as the component undoing itself.\n */\n const move = (moving: readonly string[], to: TransferSide): void => {\n if (disabled || moving.length === 0) return;\n const next = applyMove({ value, moving, to, items });\n const changed = to === \"target\" ? next.length - value.length : value.length - next.length;\n onChange(next);\n setChecked((current) => {\n const remaining = new Set(current);\n for (const id of moving) remaining.delete(id);\n return remaining;\n });\n if (changed > 0) {\n const sideName = to === \"target\" ? strings.targetTitle : strings.sourceTitle;\n setAnnouncement(strings.moved(changed, String(sideName).toLowerCase()));\n }\n };\n\n const toggle = (id: string): void => {\n setChecked((current) => {\n const next = new Set(current);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n };\n\n /** Ids checked **and visible** on one side — what the move buttons act on. */\n const checkedOn = (side: TransferSide): string[] =>\n movableIds(visible[side]).filter((id) => checked.has(id));\n\n const checkedSource = checkedOn(\"source\");\n const checkedTarget = checkedOn(\"target\");\n\n return (\n <div className={cn(styles.wrapper, className)} {...rest}>\n {panes.map((pane) => {\n const shown = visible[pane.side];\n const movable = movableIds(shown);\n const checkedHere = movable.filter((id) => checked.has(id));\n const allChecked = movable.length > 0 && checkedHere.length === movable.length;\n const titleId = `${baseId}-${pane.side}-title`;\n\n return (\n <section key={pane.side} className={styles.pane} aria-labelledby={titleId}>\n <div className={styles.paneHeader}>\n <div className={styles.paneTitle}>\n <Checkbox\n checked={allChecked}\n indeterminate={checkedHere.length > 0 && !allChecked}\n disabled={disabled || movable.length === 0}\n aria-label={\n typeof pane.title === \"string\"\n ? pane.title\n : String(pane.side)\n }\n onChange={() =>\n setChecked((current) => {\n const next = new Set(current);\n for (const id of movable) {\n if (allChecked) next.delete(id);\n else next.add(id);\n }\n return next;\n })\n }\n />\n <h3 className={styles.title} id={titleId}>\n {pane.title}\n </h3>\n </div>\n <span className={styles.count}>\n {strings.selected(checkedHere.length, pane.items.length)}\n </span>\n </div>\n\n {withSearch && (\n <input\n type=\"search\"\n className={styles.search}\n value={queries[pane.side]}\n placeholder={strings.search}\n aria-label={`${strings.search}: ${\n typeof pane.title === \"string\" ? pane.title : pane.side\n }`}\n onChange={(event) =>\n setQueries((current) => ({\n ...current,\n [pane.side]: event.target.value,\n }))\n }\n />\n )}\n\n <ul className={styles.list} style={{ height }}>\n {shown.length === 0 ? (\n <li className={styles.empty}>\n {queries[pane.side] ? strings.noMatches : strings.empty}\n </li>\n ) : (\n shown.map((item) => (\n <li key={item.id} className={styles.row}>\n <Checkbox\n checked={checked.has(item.id)}\n disabled={disabled || item.disabled}\n label={\n renderItem\n ? renderItem(item, pane.side)\n : item.label\n }\n onChange={() => toggle(item.id)}\n onDoubleClick={() =>\n move(\n [item.id],\n pane.side === \"source\" ? \"target\" : \"source\",\n )\n }\n />\n </li>\n ))\n )}\n </ul>\n </section>\n );\n })}\n\n <div className={styles.controls}>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || movableIds(visible.source).length === 0}\n aria-label={strings.allToTarget}\n title={strings.allToTarget}\n onClick={() => move(movableIds(visible.source), \"target\")}\n >\n »\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || checkedSource.length === 0}\n aria-label={strings.toTarget}\n title={strings.toTarget}\n onClick={() => move(checkedSource, \"target\")}\n >\n ›\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || checkedTarget.length === 0}\n aria-label={strings.toSource}\n title={strings.toSource}\n onClick={() => move(checkedTarget, \"source\")}\n >\n ‹\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || movableIds(visible.target).length === 0}\n aria-label={strings.allToSource}\n title={strings.allToSource}\n onClick={() => move(movableIds(visible.target), \"source\")}\n >\n «\n </button>\n </div>\n\n <VisuallyHidden aria-live=\"polite\" role=\"status\">\n {announcement}\n </VisuallyHidden>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { cn as e } from "../../utils/cn.js";
|
|
2
|
+
import { Checkbox as t } from "../Checkbox/Checkbox.js";
|
|
3
|
+
import { VisuallyHidden as n } from "../VisuallyHidden/VisuallyHidden.js";
|
|
4
|
+
import { applyMove as r, filterItems as i, movableIds as a, splitSides as o, transferStrings as s } from "./transfer-state.js";
|
|
5
|
+
import c from "./Transfer.module.js";
|
|
6
|
+
import { useId as l, useMemo as u, useState as d } from "react";
|
|
7
|
+
import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
8
|
+
//#region src/components/Transfer/Transfer.tsx
|
|
9
|
+
function m({ items: m, value: h, onChange: g, sourceTitle: _, targetTitle: v, searchable: y, renderItem: b, locale: x = "pt-BR", height: S = "16rem", disabled: C = !1, className: w, ...T }) {
|
|
10
|
+
let E = s(x), D = l(), [O, k] = d(/* @__PURE__ */ new Set()), [A, j] = d({
|
|
11
|
+
source: "",
|
|
12
|
+
target: ""
|
|
13
|
+
}), [M, N] = d(""), { source: P, target: F } = u(() => o(m, h), [m, h]), I = y ?? m.length > 8, L = {
|
|
14
|
+
source: I ? i(P, A.source) : [...P],
|
|
15
|
+
target: I ? i(F, A.target) : [...F]
|
|
16
|
+
}, R = [{
|
|
17
|
+
side: "source",
|
|
18
|
+
title: _ ?? E.sourceTitle,
|
|
19
|
+
items: P
|
|
20
|
+
}, {
|
|
21
|
+
side: "target",
|
|
22
|
+
title: v ?? E.targetTitle,
|
|
23
|
+
items: F
|
|
24
|
+
}], z = (e, t) => {
|
|
25
|
+
if (C || e.length === 0) return;
|
|
26
|
+
let n = r({
|
|
27
|
+
value: h,
|
|
28
|
+
moving: e,
|
|
29
|
+
to: t,
|
|
30
|
+
items: m
|
|
31
|
+
}), i = t === "target" ? n.length - h.length : h.length - n.length;
|
|
32
|
+
if (g(n), k((t) => {
|
|
33
|
+
let n = new Set(t);
|
|
34
|
+
for (let t of e) n.delete(t);
|
|
35
|
+
return n;
|
|
36
|
+
}), i > 0) {
|
|
37
|
+
let e = t === "target" ? E.targetTitle : E.sourceTitle;
|
|
38
|
+
N(E.moved(i, String(e).toLowerCase()));
|
|
39
|
+
}
|
|
40
|
+
}, B = (e) => {
|
|
41
|
+
k((t) => {
|
|
42
|
+
let n = new Set(t);
|
|
43
|
+
return n.has(e) ? n.delete(e) : n.add(e), n;
|
|
44
|
+
});
|
|
45
|
+
}, V = (e) => a(L[e]).filter((e) => O.has(e)), H = V("source"), U = V("target");
|
|
46
|
+
return /* @__PURE__ */ p("div", {
|
|
47
|
+
className: e(c.wrapper, w),
|
|
48
|
+
...T,
|
|
49
|
+
children: [
|
|
50
|
+
R.map((e) => {
|
|
51
|
+
let n = L[e.side], r = a(n), i = r.filter((e) => O.has(e)), o = r.length > 0 && i.length === r.length, s = `${D}-${e.side}-title`;
|
|
52
|
+
return /* @__PURE__ */ p("section", {
|
|
53
|
+
className: c.pane,
|
|
54
|
+
"aria-labelledby": s,
|
|
55
|
+
children: [
|
|
56
|
+
/* @__PURE__ */ p("div", {
|
|
57
|
+
className: c.paneHeader,
|
|
58
|
+
children: [/* @__PURE__ */ p("div", {
|
|
59
|
+
className: c.paneTitle,
|
|
60
|
+
children: [/* @__PURE__ */ f(t, {
|
|
61
|
+
checked: o,
|
|
62
|
+
indeterminate: i.length > 0 && !o,
|
|
63
|
+
disabled: C || r.length === 0,
|
|
64
|
+
"aria-label": typeof e.title == "string" ? e.title : String(e.side),
|
|
65
|
+
onChange: () => k((e) => {
|
|
66
|
+
let t = new Set(e);
|
|
67
|
+
for (let e of r) o ? t.delete(e) : t.add(e);
|
|
68
|
+
return t;
|
|
69
|
+
})
|
|
70
|
+
}), /* @__PURE__ */ f("h3", {
|
|
71
|
+
className: c.title,
|
|
72
|
+
id: s,
|
|
73
|
+
children: e.title
|
|
74
|
+
})]
|
|
75
|
+
}), /* @__PURE__ */ f("span", {
|
|
76
|
+
className: c.count,
|
|
77
|
+
children: E.selected(i.length, e.items.length)
|
|
78
|
+
})]
|
|
79
|
+
}),
|
|
80
|
+
I && /* @__PURE__ */ f("input", {
|
|
81
|
+
type: "search",
|
|
82
|
+
className: c.search,
|
|
83
|
+
value: A[e.side],
|
|
84
|
+
placeholder: E.search,
|
|
85
|
+
"aria-label": `${E.search}: ${typeof e.title == "string" ? e.title : e.side}`,
|
|
86
|
+
onChange: (t) => j((n) => ({
|
|
87
|
+
...n,
|
|
88
|
+
[e.side]: t.target.value
|
|
89
|
+
}))
|
|
90
|
+
}),
|
|
91
|
+
/* @__PURE__ */ f("ul", {
|
|
92
|
+
className: c.list,
|
|
93
|
+
style: { height: S },
|
|
94
|
+
children: n.length === 0 ? /* @__PURE__ */ f("li", {
|
|
95
|
+
className: c.empty,
|
|
96
|
+
children: A[e.side] ? E.noMatches : E.empty
|
|
97
|
+
}) : n.map((n) => /* @__PURE__ */ f("li", {
|
|
98
|
+
className: c.row,
|
|
99
|
+
children: /* @__PURE__ */ f(t, {
|
|
100
|
+
checked: O.has(n.id),
|
|
101
|
+
disabled: C || n.disabled,
|
|
102
|
+
label: b ? b(n, e.side) : n.label,
|
|
103
|
+
onChange: () => B(n.id),
|
|
104
|
+
onDoubleClick: () => z([n.id], e.side === "source" ? "target" : "source")
|
|
105
|
+
})
|
|
106
|
+
}, n.id))
|
|
107
|
+
})
|
|
108
|
+
]
|
|
109
|
+
}, e.side);
|
|
110
|
+
}),
|
|
111
|
+
/* @__PURE__ */ p("div", {
|
|
112
|
+
className: c.controls,
|
|
113
|
+
children: [
|
|
114
|
+
/* @__PURE__ */ f("button", {
|
|
115
|
+
type: "button",
|
|
116
|
+
className: c.control,
|
|
117
|
+
disabled: C || a(L.source).length === 0,
|
|
118
|
+
"aria-label": E.allToTarget,
|
|
119
|
+
title: E.allToTarget,
|
|
120
|
+
onClick: () => z(a(L.source), "target"),
|
|
121
|
+
children: "»"
|
|
122
|
+
}),
|
|
123
|
+
/* @__PURE__ */ f("button", {
|
|
124
|
+
type: "button",
|
|
125
|
+
className: c.control,
|
|
126
|
+
disabled: C || H.length === 0,
|
|
127
|
+
"aria-label": E.toTarget,
|
|
128
|
+
title: E.toTarget,
|
|
129
|
+
onClick: () => z(H, "target"),
|
|
130
|
+
children: "›"
|
|
131
|
+
}),
|
|
132
|
+
/* @__PURE__ */ f("button", {
|
|
133
|
+
type: "button",
|
|
134
|
+
className: c.control,
|
|
135
|
+
disabled: C || U.length === 0,
|
|
136
|
+
"aria-label": E.toSource,
|
|
137
|
+
title: E.toSource,
|
|
138
|
+
onClick: () => z(U, "source"),
|
|
139
|
+
children: "‹"
|
|
140
|
+
}),
|
|
141
|
+
/* @__PURE__ */ f("button", {
|
|
142
|
+
type: "button",
|
|
143
|
+
className: c.control,
|
|
144
|
+
disabled: C || a(L.target).length === 0,
|
|
145
|
+
"aria-label": E.allToSource,
|
|
146
|
+
title: E.allToSource,
|
|
147
|
+
onClick: () => z(a(L.target), "source"),
|
|
148
|
+
children: "«"
|
|
149
|
+
})
|
|
150
|
+
]
|
|
151
|
+
}),
|
|
152
|
+
/* @__PURE__ */ f(n, {
|
|
153
|
+
"aria-live": "polite",
|
|
154
|
+
role: "status",
|
|
155
|
+
children: M
|
|
156
|
+
})
|
|
157
|
+
]
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
export { m as Transfer };
|
|
162
|
+
|
|
163
|
+
//# sourceMappingURL=Transfer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Transfer.js","names":[],"sources":["../../../src/components/Transfer/Transfer.tsx"],"sourcesContent":["import { useId, useMemo, useState, type HTMLAttributes, type ReactNode } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nimport { Checkbox } from \"../Checkbox\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport {\n applyMove,\n filterItems,\n movableIds,\n splitSides,\n transferStrings,\n type TransferItem,\n type TransferSide,\n} from \"./transfer-state\";\nimport styles from \"./Transfer.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\" | \"onChange\" | \"defaultValue\";\n\nexport interface TransferProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps> {\n /** The whole catalogue. Both panes are derived from it. */\n items: readonly TransferItem[];\n /** Ids on the target side. Controlled. */\n value: readonly string[];\n /** Next value, always in catalogue order. */\n onChange: (value: string[]) => void;\n /** Heading of the left pane. Default `\"Disponíveis\"`. */\n sourceTitle?: ReactNode;\n /** Heading of the right pane. Default `\"Selecionados\"`. */\n targetTitle?: ReactNode;\n /** Show a search box above each pane. Default `true` past 8 items. */\n searchable?: boolean;\n /** Custom row body. Gets the item and which side it is on. */\n renderItem?: (item: TransferItem, side: TransferSide) => ReactNode;\n /** Locale for labels. Default `\"pt-BR\"`. */\n locale?: \"pt-BR\" | \"en\";\n /** Height of each pane's scroll area, any CSS length. Default `\"16rem\"`. */\n height?: string;\n /** Block every move. */\n disabled?: boolean;\n}\n\n/**\n * Two panes and a set of move controls: pick a subset out of a catalogue.\n *\n * Controlled on **ids of the target side only**. The panes are derived on every\n * render, never stored, because two stored lists drift the moment the catalogue\n * changes under them — a permission removed upstream lingers in whichever pane\n * held it, and an id in both panes is a bug nobody can see.\n *\n * @example\n * const [roles, setRoles] = useState<string[]>([]);\n *\n * <Transfer\n * items={allRoles}\n * value={roles}\n * onChange={setRoles}\n * sourceTitle=\"Papéis disponíveis\"\n * targetTitle=\"Papéis do usuário\"\n * />\n */\nexport function Transfer({\n items,\n value,\n onChange,\n sourceTitle,\n targetTitle,\n searchable,\n renderItem,\n locale = \"pt-BR\",\n height = \"16rem\",\n disabled = false,\n className,\n ...rest\n}: TransferProps) {\n const strings = transferStrings(locale);\n const baseId = useId();\n const [checked, setChecked] = useState<Set<string>>(new Set());\n const [queries, setQueries] = useState<{ source: string; target: string }>({\n source: \"\",\n target: \"\",\n });\n const [announcement, setAnnouncement] = useState(\"\");\n\n const { source, target } = useMemo(() => splitSides(items, value), [items, value]);\n const withSearch = searchable ?? items.length > 8;\n\n /**\n * What each pane is showing right now — the filter included.\n *\n * Every control acts on this, not on the pane's full contents: filtering to\n * \"sao\" and pressing \"move all\" has to move what you are looking at. Moving\n * rows the filter hid is the kind of surprise that makes people stop trusting\n * the bulk button.\n */\n const visible: Record<TransferSide, TransferItem[]> = {\n source: withSearch ? filterItems(source, queries.source) : [...source],\n target: withSearch ? filterItems(target, queries.target) : [...target],\n };\n\n const panes: Array<{ side: TransferSide; title: ReactNode; items: TransferItem[] }> = [\n { side: \"source\", title: sourceTitle ?? strings.sourceTitle, items: source },\n { side: \"target\", title: targetTitle ?? strings.targetTitle, items: target },\n ];\n\n /**\n * Move ids and drop them from the checked set.\n *\n * Clearing the checks is the point: after a move the rows are on the other\n * side, and leaving them checked means the next click on the opposite button\n * sends them straight back — which reads as the component undoing itself.\n */\n const move = (moving: readonly string[], to: TransferSide): void => {\n if (disabled || moving.length === 0) return;\n const next = applyMove({ value, moving, to, items });\n const changed = to === \"target\" ? next.length - value.length : value.length - next.length;\n onChange(next);\n setChecked((current) => {\n const remaining = new Set(current);\n for (const id of moving) remaining.delete(id);\n return remaining;\n });\n if (changed > 0) {\n const sideName = to === \"target\" ? strings.targetTitle : strings.sourceTitle;\n setAnnouncement(strings.moved(changed, String(sideName).toLowerCase()));\n }\n };\n\n const toggle = (id: string): void => {\n setChecked((current) => {\n const next = new Set(current);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n };\n\n /** Ids checked **and visible** on one side — what the move buttons act on. */\n const checkedOn = (side: TransferSide): string[] =>\n movableIds(visible[side]).filter((id) => checked.has(id));\n\n const checkedSource = checkedOn(\"source\");\n const checkedTarget = checkedOn(\"target\");\n\n return (\n <div className={cn(styles.wrapper, className)} {...rest}>\n {panes.map((pane) => {\n const shown = visible[pane.side];\n const movable = movableIds(shown);\n const checkedHere = movable.filter((id) => checked.has(id));\n const allChecked = movable.length > 0 && checkedHere.length === movable.length;\n const titleId = `${baseId}-${pane.side}-title`;\n\n return (\n <section key={pane.side} className={styles.pane} aria-labelledby={titleId}>\n <div className={styles.paneHeader}>\n <div className={styles.paneTitle}>\n <Checkbox\n checked={allChecked}\n indeterminate={checkedHere.length > 0 && !allChecked}\n disabled={disabled || movable.length === 0}\n aria-label={\n typeof pane.title === \"string\"\n ? pane.title\n : String(pane.side)\n }\n onChange={() =>\n setChecked((current) => {\n const next = new Set(current);\n for (const id of movable) {\n if (allChecked) next.delete(id);\n else next.add(id);\n }\n return next;\n })\n }\n />\n <h3 className={styles.title} id={titleId}>\n {pane.title}\n </h3>\n </div>\n <span className={styles.count}>\n {strings.selected(checkedHere.length, pane.items.length)}\n </span>\n </div>\n\n {withSearch && (\n <input\n type=\"search\"\n className={styles.search}\n value={queries[pane.side]}\n placeholder={strings.search}\n aria-label={`${strings.search}: ${\n typeof pane.title === \"string\" ? pane.title : pane.side\n }`}\n onChange={(event) =>\n setQueries((current) => ({\n ...current,\n [pane.side]: event.target.value,\n }))\n }\n />\n )}\n\n <ul className={styles.list} style={{ height }}>\n {shown.length === 0 ? (\n <li className={styles.empty}>\n {queries[pane.side] ? strings.noMatches : strings.empty}\n </li>\n ) : (\n shown.map((item) => (\n <li key={item.id} className={styles.row}>\n <Checkbox\n checked={checked.has(item.id)}\n disabled={disabled || item.disabled}\n label={\n renderItem\n ? renderItem(item, pane.side)\n : item.label\n }\n onChange={() => toggle(item.id)}\n onDoubleClick={() =>\n move(\n [item.id],\n pane.side === \"source\" ? \"target\" : \"source\",\n )\n }\n />\n </li>\n ))\n )}\n </ul>\n </section>\n );\n })}\n\n <div className={styles.controls}>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || movableIds(visible.source).length === 0}\n aria-label={strings.allToTarget}\n title={strings.allToTarget}\n onClick={() => move(movableIds(visible.source), \"target\")}\n >\n »\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || checkedSource.length === 0}\n aria-label={strings.toTarget}\n title={strings.toTarget}\n onClick={() => move(checkedSource, \"target\")}\n >\n ›\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || checkedTarget.length === 0}\n aria-label={strings.toSource}\n title={strings.toSource}\n onClick={() => move(checkedTarget, \"source\")}\n >\n ‹\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || movableIds(visible.target).length === 0}\n aria-label={strings.allToSource}\n title={strings.allToSource}\n onClick={() => move(movableIds(visible.target), \"source\")}\n >\n «\n </button>\n </div>\n\n <VisuallyHidden aria-live=\"polite\" role=\"status\">\n {announcement}\n </VisuallyHidden>\n </div>\n );\n}\n"],"mappings":";;;;;;;;AA8DA,SAAgB,EAAS,EACrB,UACA,UACA,aACA,gBACA,gBACA,eACA,eACA,YAAS,SACT,YAAS,SACT,cAAW,IACX,cACA,GAAG,KACW;CACd,IAAM,IAAU,EAAgB,CAAM,GAChC,IAAS,EAAM,GACf,CAAC,GAAS,KAAc,kBAAsB,IAAI,IAAI,CAAC,GACvD,CAAC,GAAS,KAAc,EAA6C;EACvE,QAAQ;EACR,QAAQ;CACZ,CAAC,GACK,CAAC,GAAc,KAAmB,EAAS,EAAE,GAE7C,EAAE,WAAQ,cAAW,QAAc,EAAW,GAAO,CAAK,GAAG,CAAC,GAAO,CAAK,CAAC,GAC3E,IAAa,KAAc,EAAM,SAAS,GAU1C,IAAgD;EAClD,QAAQ,IAAa,EAAY,GAAQ,EAAQ,MAAM,IAAI,CAAC,GAAG,CAAM;EACrE,QAAQ,IAAa,EAAY,GAAQ,EAAQ,MAAM,IAAI,CAAC,GAAG,CAAM;CACzE,GAEM,IAAgF,CAClF;EAAE,MAAM;EAAU,OAAO,KAAe,EAAQ;EAAa,OAAO;CAAO,GAC3E;EAAE,MAAM;EAAU,OAAO,KAAe,EAAQ;EAAa,OAAO;CAAO,CAC/E,GASM,KAAQ,GAA2B,MAA2B;EAChE,IAAI,KAAY,EAAO,WAAW,GAAG;EACrC,IAAM,IAAO,EAAU;GAAE;GAAO;GAAQ;GAAI;EAAM,CAAC,GAC7C,IAAU,MAAO,WAAW,EAAK,SAAS,EAAM,SAAS,EAAM,SAAS,EAAK;EAOnF,IANA,EAAS,CAAI,GACb,GAAY,MAAY;GACpB,IAAM,IAAY,IAAI,IAAI,CAAO;GACjC,KAAK,IAAM,KAAM,GAAQ,EAAU,OAAO,CAAE;GAC5C,OAAO;EACX,CAAC,GACG,IAAU,GAAG;GACb,IAAM,IAAW,MAAO,WAAW,EAAQ,cAAc,EAAQ;GACjE,EAAgB,EAAQ,MAAM,GAAS,OAAO,CAAQ,CAAC,CAAC,YAAY,CAAC,CAAC;EAC1E;CACJ,GAEM,KAAU,MAAqB;EACjC,GAAY,MAAY;GACpB,IAAM,IAAO,IAAI,IAAI,CAAO;GAG5B,OAFI,EAAK,IAAI,CAAE,IAAG,EAAK,OAAO,CAAE,IAC3B,EAAK,IAAI,CAAE,GACT;EACX,CAAC;CACL,GAGM,KAAa,MACf,EAAW,EAAQ,EAAK,CAAC,CAAC,QAAQ,MAAO,EAAQ,IAAI,CAAE,CAAC,GAEtD,IAAgB,EAAU,QAAQ,GAClC,IAAgB,EAAU,QAAQ;CAExC,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,SAAS,CAAS;EAAG,GAAI;YAAnD;GACK,EAAM,KAAK,MAAS;IACjB,IAAM,IAAQ,EAAQ,EAAK,OACrB,IAAU,EAAW,CAAK,GAC1B,IAAc,EAAQ,QAAQ,MAAO,EAAQ,IAAI,CAAE,CAAC,GACpD,IAAa,EAAQ,SAAS,KAAK,EAAY,WAAW,EAAQ,QAClE,IAAU,GAAG,EAAO,GAAG,EAAK,KAAK;IAEvC,OACI,kBAAC,WAAD;KAAyB,WAAW,EAAO;KAAM,mBAAiB;eAAlE;MACI,kBAAC,OAAD;OAAK,WAAW,EAAO;iBAAvB,CACI,kBAAC,OAAD;QAAK,WAAW,EAAO;kBAAvB,CACI,kBAAC,GAAD;SACI,SAAS;SACT,eAAe,EAAY,SAAS,KAAK,CAAC;SAC1C,UAAU,KAAY,EAAQ,WAAW;SACzC,cACI,OAAO,EAAK,SAAU,WAChB,EAAK,QACL,OAAO,EAAK,IAAI;SAE1B,gBACI,GAAY,MAAY;UACpB,IAAM,IAAO,IAAI,IAAI,CAAO;UAC5B,KAAK,IAAM,KAAM,GACb,AAAI,IAAY,EAAK,OAAO,CAAE,IACzB,EAAK,IAAI,CAAE;UAEpB,OAAO;SACX,CAAC;QAER,CAAA,GACD,kBAAC,MAAD;SAAI,WAAW,EAAO;SAAO,IAAI;mBAC5B,EAAK;QACN,CAAA,CACH;WACL,kBAAC,QAAD;QAAM,WAAW,EAAO;kBACnB,EAAQ,SAAS,EAAY,QAAQ,EAAK,MAAM,MAAM;OACrD,CAAA,CACL;;MAEJ,KACG,kBAAC,SAAD;OACI,MAAK;OACL,WAAW,EAAO;OAClB,OAAO,EAAQ,EAAK;OACpB,aAAa,EAAQ;OACrB,cAAY,GAAG,EAAQ,OAAO,IAC1B,OAAO,EAAK,SAAU,WAAW,EAAK,QAAQ,EAAK;OAEvD,WAAW,MACP,GAAY,OAAa;QACrB,GAAG;SACF,EAAK,OAAO,EAAM,OAAO;OAC9B,EAAE;MAET,CAAA;MAGL,kBAAC,MAAD;OAAI,WAAW,EAAO;OAAM,OAAO,EAAE,UAAO;iBACvC,EAAM,WAAW,IACd,kBAAC,MAAD;QAAI,WAAW,EAAO;kBACjB,EAAQ,EAAK,QAAQ,EAAQ,YAAY,EAAQ;OAClD,CAAA,IAEJ,EAAM,KAAK,MACP,kBAAC,MAAD;QAAkB,WAAW,EAAO;kBAChC,kBAAC,GAAD;SACI,SAAS,EAAQ,IAAI,EAAK,EAAE;SAC5B,UAAU,KAAY,EAAK;SAC3B,OACI,IACM,EAAW,GAAM,EAAK,IAAI,IAC1B,EAAK;SAEf,gBAAgB,EAAO,EAAK,EAAE;SAC9B,qBACI,EACI,CAAC,EAAK,EAAE,GACR,EAAK,SAAS,WAAW,WAAW,QACxC;QAEP,CAAA;OACD,GAjBK,EAAK,EAiBV,CACP;MAEL,CAAA;KACC;OA9EK,EAAK,IA8EV;GAEjB,CAAC;GAED,kBAAC,OAAD;IAAK,WAAW,EAAO;cAAvB;KACI,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,UAAU,KAAY,EAAW,EAAQ,MAAM,CAAC,CAAC,WAAW;MAC5D,cAAY,EAAQ;MACpB,OAAO,EAAQ;MACf,eAAe,EAAK,EAAW,EAAQ,MAAM,GAAG,QAAQ;gBAC3D;KAEO,CAAA;KACR,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,UAAU,KAAY,EAAc,WAAW;MAC/C,cAAY,EAAQ;MACpB,OAAO,EAAQ;MACf,eAAe,EAAK,GAAe,QAAQ;gBAC9C;KAEO,CAAA;KACR,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,UAAU,KAAY,EAAc,WAAW;MAC/C,cAAY,EAAQ;MACpB,OAAO,EAAQ;MACf,eAAe,EAAK,GAAe,QAAQ;gBAC9C;KAEO,CAAA;KACR,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,UAAU,KAAY,EAAW,EAAQ,MAAM,CAAC,CAAC,WAAW;MAC5D,cAAY,EAAQ;MACpB,OAAO,EAAQ;MACf,eAAe,EAAK,EAAW,EAAQ,MAAM,GAAG,QAAQ;gBAC3D;KAEO,CAAA;IACP;;GAEL,kBAAC,GAAD;IAAgB,aAAU;IAAS,MAAK;cACnC;GACW,CAAA;EACf;;AAEb"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=`tempest_wrapper_v0MUQ`,t=`tempest_controls_Khwwy`,n=`tempest_pane_EZRbR`,r=`tempest_paneHeader_JymjN`,i=`tempest_paneTitle_FS-D2`,a=`tempest_title_IhvAM`,o=`tempest_count_2NX52`,s=`tempest_search_OPJ2A`,c=`tempest_list_LrZlc`,l=`tempest_row_uZH5v`,u=`tempest_empty_sM4jo`,d=`tempest_control_-y936`,f={wrapper:e,controls:t,pane:n,paneHeader:r,paneTitle:i,title:a,count:o,search:s,list:c,row:l,empty:u,control:d};exports.control=d,exports.controls=t,exports.count=o,exports.default=f,exports.empty=u,exports.list=c,exports.pane=n,exports.paneHeader=r,exports.paneTitle=i,exports.row=l,exports.search=s,exports.title=a,exports.wrapper=e;
|
|
2
|
+
//# sourceMappingURL=Transfer.module.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Transfer.module.cjs","names":[],"sources":["../../../src/components/Transfer/Transfer.module.css"],"sourcesContent":[".wrapper {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);\n gap: var(--tempest-space-3);\n align-items: stretch;\n font-family: var(--tempest-font-sans);\n}\n\n/*\n * The move controls sit between the panes in the grid, but come **last** in the\n * DOM: a keyboard reaching the buttons before it has seen what they act on has to\n * go back, and a screen reader reads \"move checked to the right\" with no idea what\n * is checked. Order 2 puts them back in the middle visually.\n */\n.controls {\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: var(--tempest-space-2);\n order: 2;\n}\n\n.pane {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\n min-width: 0;\n padding: var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-bg);\n}\n\n.pane:first-of-type {\n order: 1;\n}\n\n.pane:last-of-type {\n order: 3;\n}\n\n.paneHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-2);\n min-width: 0;\n}\n\n.paneTitle {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n min-width: 0;\n}\n\n.title {\n margin: 0;\n overflow: hidden;\n color: var(--tempest-text);\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.count {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.search {\n width: 100%;\n min-width: 0;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-bg);\n color: inherit;\n font: inherit;\n font-size: var(--tempest-text-sm);\n}\n\n.search:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: -1px;\n}\n\n.list {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n margin: 0;\n padding: 0;\n overflow-y: auto;\n overscroll-behavior: contain;\n list-style: none;\n}\n\n.row {\n padding: var(--tempest-space-1) var(--tempest-space-2);\n border-radius: var(--tempest-radius-sm);\n font-size: var(--tempest-text-sm);\n}\n\n.row:hover {\n background-color: var(--tempest-surface);\n}\n\n.empty {\n padding: var(--tempest-space-4) var(--tempest-space-2);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n text-align: center;\n}\n\n.control {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--tempest-control-height-sm);\n height: var(--tempest-control-height-sm);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-size: var(--tempest-text-base);\n line-height: 1;\n cursor: pointer;\n}\n\n.control:hover:not(:disabled) {\n border-color: var(--tempest-primary);\n color: var(--tempest-primary);\n}\n\n.control:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 1px;\n}\n\n.control:disabled {\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n/*\n * Below the md breakpoint the panes stack and the controls turn horizontal: two\n * 16rem lists side by side on a phone leave each about 150px wide, where a role\n * name wraps to three lines.\n */\n@media (max-width: 640px) {\n .wrapper {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .controls {\n flex-direction: row;\n justify-content: center;\n }\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/components/Transfer/Transfer.module.css
|
|
2
|
+
var e = "tempest_wrapper_v0MUQ", t = "tempest_controls_Khwwy", n = "tempest_pane_EZRbR", r = "tempest_paneHeader_JymjN", i = "tempest_paneTitle_FS-D2", a = "tempest_title_IhvAM", o = "tempest_count_2NX52", s = "tempest_search_OPJ2A", c = "tempest_list_LrZlc", l = "tempest_row_uZH5v", u = "tempest_empty_sM4jo", d = "tempest_control_-y936", f = {
|
|
3
|
+
wrapper: e,
|
|
4
|
+
controls: t,
|
|
5
|
+
pane: n,
|
|
6
|
+
paneHeader: r,
|
|
7
|
+
paneTitle: i,
|
|
8
|
+
title: a,
|
|
9
|
+
count: o,
|
|
10
|
+
search: s,
|
|
11
|
+
list: c,
|
|
12
|
+
row: l,
|
|
13
|
+
empty: u,
|
|
14
|
+
control: d
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { d as control, t as controls, o as count, f as default, u as empty, c as list, n as pane, r as paneHeader, i as paneTitle, l as row, s as search, a as title, e as wrapper };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=Transfer.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Transfer.module.js","names":[],"sources":["../../../src/components/Transfer/Transfer.module.css"],"sourcesContent":[".wrapper {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);\n gap: var(--tempest-space-3);\n align-items: stretch;\n font-family: var(--tempest-font-sans);\n}\n\n/*\n * The move controls sit between the panes in the grid, but come **last** in the\n * DOM: a keyboard reaching the buttons before it has seen what they act on has to\n * go back, and a screen reader reads \"move checked to the right\" with no idea what\n * is checked. Order 2 puts them back in the middle visually.\n */\n.controls {\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: var(--tempest-space-2);\n order: 2;\n}\n\n.pane {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\n min-width: 0;\n padding: var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-bg);\n}\n\n.pane:first-of-type {\n order: 1;\n}\n\n.pane:last-of-type {\n order: 3;\n}\n\n.paneHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-2);\n min-width: 0;\n}\n\n.paneTitle {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n min-width: 0;\n}\n\n.title {\n margin: 0;\n overflow: hidden;\n color: var(--tempest-text);\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.count {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.search {\n width: 100%;\n min-width: 0;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-bg);\n color: inherit;\n font: inherit;\n font-size: var(--tempest-text-sm);\n}\n\n.search:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: -1px;\n}\n\n.list {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n margin: 0;\n padding: 0;\n overflow-y: auto;\n overscroll-behavior: contain;\n list-style: none;\n}\n\n.row {\n padding: var(--tempest-space-1) var(--tempest-space-2);\n border-radius: var(--tempest-radius-sm);\n font-size: var(--tempest-text-sm);\n}\n\n.row:hover {\n background-color: var(--tempest-surface);\n}\n\n.empty {\n padding: var(--tempest-space-4) var(--tempest-space-2);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n text-align: center;\n}\n\n.control {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--tempest-control-height-sm);\n height: var(--tempest-control-height-sm);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-size: var(--tempest-text-base);\n line-height: 1;\n cursor: pointer;\n}\n\n.control:hover:not(:disabled) {\n border-color: var(--tempest-primary);\n color: var(--tempest-primary);\n}\n\n.control:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 1px;\n}\n\n.control:disabled {\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n/*\n * Below the md breakpoint the panes stack and the controls turn horizontal: two\n * 16rem lists side by side on a phone leave each about 150px wide, where a role\n * name wraps to three lines.\n */\n@media (max-width: 640px) {\n .wrapper {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .controls {\n flex-direction: row;\n justify-content: center;\n }\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function e(e,t){let n=new Set(t),r=[],i=[];for(let t of e)(n.has(t.id)?i:r).push(t);return{source:r,target:i}}function t(e){return e.searchText===void 0?typeof e.label==`string`?e.label:``:e.searchText}function n(e,n){let i=r(n);return i?e.filter(e=>r(t(e)).includes(i)):[...e]}function r(e){return e.normalize(`NFD`).replace(/\p{Diacritic}/gu,``).toLowerCase().trim()}function i({value:e,moving:t,to:n,items:r}){let i=new Map(r.map(e=>[e.id,e])),a=t.filter(e=>i.has(e)&&!i.get(e)?.disabled),o=new Set(e);for(let e of a)n===`target`?o.add(e):o.delete(e);return r.filter(e=>o.has(e.id)).map(e=>e.id)}function a(e){return e.filter(e=>!e.disabled).map(e=>e.id)}var o={sourceTitle:`Disponíveis`,targetTitle:`Selecionados`,search:`Buscar`,toTarget:`Mover selecionados para a direita`,toSource:`Mover selecionados para a esquerda`,allToTarget:`Mover todos para a direita`,allToSource:`Mover todos para a esquerda`,empty:`Nada aqui`,noMatches:`Nenhum resultado`,selected:(e,t)=>`${e} de ${t} marcados`,moved:(e,t)=>`${e} ${e===1?`item movido`:`itens movidos`} para ${t}`},s={sourceTitle:`Available`,targetTitle:`Selected`,search:`Search`,toTarget:`Move checked to the right`,toSource:`Move checked to the left`,allToTarget:`Move all to the right`,allToSource:`Move all to the left`,empty:`Nothing here`,noMatches:`No matches`,selected:(e,t)=>`${e} of ${t} checked`,moved:(e,t)=>`${e} ${e===1?`item moved`:`items moved`} to ${t}`};function c(e){return e===`en`?s:o}exports.applyMove=i,exports.filterItems=n,exports.movableIds=a,exports.searchTextOf=t,exports.splitSides=e,exports.transferStrings=c;
|
|
2
|
+
//# sourceMappingURL=transfer-state.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer-state.cjs","names":[],"sources":["../../../src/components/Transfer/transfer-state.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\n\n/** One movable entry. */\nexport interface TransferItem {\n /** Stable identity. What `value`/`onChange` carry. */\n id: string;\n /** What the row shows. */\n label: ReactNode;\n /** Text the search box matches against. Falls back to `label` when it is a string. */\n searchText?: string;\n /** Row that cannot move — a mandatory permission, a locked seat. */\n disabled?: boolean;\n /** Anything the app wants back in `renderItem`. */\n data?: Record<string, unknown>;\n}\n\n/** Which pane an operation is about. */\nexport type TransferSide = \"source\" | \"target\";\n\n/**\n * Split the catalogue into the two panes.\n *\n * The selected ids are the single source of truth — the panes are derived, never\n * stored. Two stored lists drift the moment the catalogue changes under them: a\n * permission that is removed upstream lingers in whichever pane held it, and an\n * id that appears in both is a bug nobody can see.\n *\n * Order follows `items`, so both panes read in the catalogue's order rather than\n * in the order somebody happened to click.\n *\n * @param items - The whole catalogue.\n * @param value - Ids currently on the target side.\n */\nexport function splitSides(\n items: readonly TransferItem[],\n value: readonly string[],\n): { source: TransferItem[]; target: TransferItem[] } {\n const selected = new Set(value);\n const source: TransferItem[] = [];\n const target: TransferItem[] = [];\n for (const item of items) {\n (selected.has(item.id) ? target : source).push(item);\n }\n return { source, target };\n}\n\n/** Text a row is searched by. */\nexport function searchTextOf(item: TransferItem): string {\n if (item.searchText !== undefined) return item.searchText;\n return typeof item.label === \"string\" ? item.label : \"\";\n}\n\n/**\n * Filter a pane by a query, case- and accent-insensitively.\n *\n * Accent folding is not optional for a PT-BR audience: typing \"sao\" has to find\n * \"São Paulo\", and a plain `includes` would not.\n */\nexport function filterItems(items: readonly TransferItem[], query: string): TransferItem[] {\n const needle = fold(query);\n if (!needle) return [...items];\n return items.filter((item) => fold(searchTextOf(item)).includes(needle));\n}\n\n/** Lowercase and strip diacritics. */\nfunction fold(text: string): string {\n return text\n .normalize(\"NFD\")\n .replace(/\\p{Diacritic}/gu, \"\")\n .toLowerCase()\n .trim();\n}\n\n/**\n * Apply a move and return the next value.\n *\n * Disabled rows are dropped here rather than at the call site, so no caller can\n * move one by pressing the bulk button — the check has to live where the\n * decision is made, not in each of the four places that trigger one.\n *\n * @param params.value - Current target ids.\n * @param params.moving - Ids being moved.\n * @param params.to - Destination pane.\n * @param params.items - The catalogue, to look disabled rows up.\n * @param params.order - Ids in catalogue order, so the result stays stable.\n * @returns The next value, in catalogue order.\n */\nexport function applyMove({\n value,\n moving,\n to,\n items,\n}: {\n value: readonly string[];\n moving: readonly string[];\n to: TransferSide;\n items: readonly TransferItem[];\n}): string[] {\n const byId = new Map(items.map((item) => [item.id, item]));\n const allowed = moving.filter((id) => byId.has(id) && !byId.get(id)?.disabled);\n const next = new Set(value);\n for (const id of allowed) {\n if (to === \"target\") next.add(id);\n else next.delete(id);\n }\n return items.filter((item) => next.has(item.id)).map((item) => item.id);\n}\n\n/** Ids in `items` that a checkbox set may still act on. */\nexport function movableIds(items: readonly TransferItem[]): string[] {\n return items.filter((item) => !item.disabled).map((item) => item.id);\n}\n\n/** Labels the component needs, per locale. */\ninterface TransferStrings {\n sourceTitle: string;\n targetTitle: string;\n search: string;\n toTarget: string;\n toSource: string;\n allToTarget: string;\n allToSource: string;\n empty: string;\n noMatches: string;\n selected: (n: number, total: number) => string;\n moved: (n: number, side: string) => string;\n}\n\nconst PT_BR: TransferStrings = {\n sourceTitle: \"Disponíveis\",\n targetTitle: \"Selecionados\",\n search: \"Buscar\",\n toTarget: \"Mover selecionados para a direita\",\n toSource: \"Mover selecionados para a esquerda\",\n allToTarget: \"Mover todos para a direita\",\n allToSource: \"Mover todos para a esquerda\",\n empty: \"Nada aqui\",\n noMatches: \"Nenhum resultado\",\n selected: (n, total) => `${n} de ${total} marcados`,\n moved: (n, side) => `${n} ${n === 1 ? \"item movido\" : \"itens movidos\"} para ${side}`,\n};\n\nconst EN: TransferStrings = {\n sourceTitle: \"Available\",\n targetTitle: \"Selected\",\n search: \"Search\",\n toTarget: \"Move checked to the right\",\n toSource: \"Move checked to the left\",\n allToTarget: \"Move all to the right\",\n allToSource: \"Move all to the left\",\n empty: \"Nothing here\",\n noMatches: \"No matches\",\n selected: (n, total) => `${n} of ${total} checked`,\n moved: (n, side) => `${n} ${n === 1 ? \"item moved\" : \"items moved\"} to ${side}`,\n};\n\n/** Locale strings for the dual list. */\nexport function transferStrings(locale: \"pt-BR\" | \"en\"): TransferStrings {\n return locale === \"en\" ? EN : PT_BR;\n}\n"],"mappings":"AAiCA,SAAgB,EACZ,EACA,EACkD,CAClD,IAAM,EAAW,IAAI,IAAI,CAAK,EACxB,EAAyB,CAAC,EAC1B,EAAyB,CAAC,EAChC,IAAK,IAAM,KAAQ,GACd,EAAS,IAAI,EAAK,EAAE,EAAI,EAAS,EAAA,CAAQ,KAAK,CAAI,EAEvD,MAAO,CAAE,SAAQ,QAAO,CAC5B,CAGA,SAAgB,EAAa,EAA4B,CAErD,OADI,EAAK,aAAe,IAAA,GACjB,OAAO,EAAK,OAAU,SAAW,EAAK,MAAQ,GADX,EAAK,UAEnD,CAQA,SAAgB,EAAY,EAAgC,EAA+B,CACvF,IAAM,EAAS,EAAK,CAAK,EAEzB,OADK,EACE,EAAM,OAAQ,GAAS,EAAK,EAAa,CAAI,CAAC,CAAC,CAAC,SAAS,CAAM,CAAC,EADnD,CAAC,GAAG,CAAK,CAEjC,CAGA,SAAS,EAAK,EAAsB,CAChC,OAAO,EACF,UAAU,KAAK,CAAC,CAChB,QAAQ,kBAAmB,EAAE,CAAC,CAC9B,YAAY,CAAC,CACb,KAAK,CACd,CAgBA,SAAgB,EAAU,CACtB,QACA,SACA,KACA,SAMS,CACT,IAAM,EAAO,IAAI,IAAI,EAAM,IAAK,GAAS,CAAC,EAAK,GAAI,CAAI,CAAC,CAAC,EACnD,EAAU,EAAO,OAAQ,GAAO,EAAK,IAAI,CAAE,GAAK,CAAC,EAAK,IAAI,CAAE,CAAC,EAAE,QAAQ,EACvE,EAAO,IAAI,IAAI,CAAK,EAC1B,IAAK,IAAM,KAAM,EACT,IAAO,SAAU,EAAK,IAAI,CAAE,EAC3B,EAAK,OAAO,CAAE,EAEvB,OAAO,EAAM,OAAQ,GAAS,EAAK,IAAI,EAAK,EAAE,CAAC,CAAC,CAAC,IAAK,GAAS,EAAK,EAAE,CAC1E,CAGA,SAAgB,EAAW,EAA0C,CACjE,OAAO,EAAM,OAAQ,GAAS,CAAC,EAAK,QAAQ,CAAC,CAAC,IAAK,GAAS,EAAK,EAAE,CACvE,CAiBA,IAAM,EAAyB,CAC3B,YAAa,cACb,YAAa,eACb,OAAQ,SACR,SAAU,oCACV,SAAU,qCACV,YAAa,6BACb,YAAa,8BACb,MAAO,YACP,UAAW,mBACX,UAAW,EAAG,IAAU,GAAG,EAAE,MAAM,EAAM,WACzC,OAAQ,EAAG,IAAS,GAAG,EAAE,GAAG,IAAM,EAAI,cAAgB,gBAAgB,QAAQ,GAClF,EAEM,EAAsB,CACxB,YAAa,YACb,YAAa,WACb,OAAQ,SACR,SAAU,4BACV,SAAU,2BACV,YAAa,wBACb,YAAa,uBACb,MAAO,eACP,UAAW,aACX,UAAW,EAAG,IAAU,GAAG,EAAE,MAAM,EAAM,UACzC,OAAQ,EAAG,IAAS,GAAG,EAAE,GAAG,IAAM,EAAI,aAAe,cAAc,MAAM,GAC7E,EAGA,SAAgB,EAAgB,EAAyC,CACrE,OAAO,IAAW,KAAO,EAAK,CAClC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//#region src/components/Transfer/transfer-state.ts
|
|
2
|
+
function e(e, t) {
|
|
3
|
+
let n = new Set(t), r = [], i = [];
|
|
4
|
+
for (let t of e) (n.has(t.id) ? i : r).push(t);
|
|
5
|
+
return {
|
|
6
|
+
source: r,
|
|
7
|
+
target: i
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function t(e) {
|
|
11
|
+
return e.searchText === void 0 ? typeof e.label == "string" ? e.label : "" : e.searchText;
|
|
12
|
+
}
|
|
13
|
+
function n(e, n) {
|
|
14
|
+
let i = r(n);
|
|
15
|
+
return i ? e.filter((e) => r(t(e)).includes(i)) : [...e];
|
|
16
|
+
}
|
|
17
|
+
function r(e) {
|
|
18
|
+
return e.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().trim();
|
|
19
|
+
}
|
|
20
|
+
function i({ value: e, moving: t, to: n, items: r }) {
|
|
21
|
+
let i = new Map(r.map((e) => [e.id, e])), a = t.filter((e) => i.has(e) && !i.get(e)?.disabled), o = new Set(e);
|
|
22
|
+
for (let e of a) n === "target" ? o.add(e) : o.delete(e);
|
|
23
|
+
return r.filter((e) => o.has(e.id)).map((e) => e.id);
|
|
24
|
+
}
|
|
25
|
+
function a(e) {
|
|
26
|
+
return e.filter((e) => !e.disabled).map((e) => e.id);
|
|
27
|
+
}
|
|
28
|
+
var o = {
|
|
29
|
+
sourceTitle: "Disponíveis",
|
|
30
|
+
targetTitle: "Selecionados",
|
|
31
|
+
search: "Buscar",
|
|
32
|
+
toTarget: "Mover selecionados para a direita",
|
|
33
|
+
toSource: "Mover selecionados para a esquerda",
|
|
34
|
+
allToTarget: "Mover todos para a direita",
|
|
35
|
+
allToSource: "Mover todos para a esquerda",
|
|
36
|
+
empty: "Nada aqui",
|
|
37
|
+
noMatches: "Nenhum resultado",
|
|
38
|
+
selected: (e, t) => `${e} de ${t} marcados`,
|
|
39
|
+
moved: (e, t) => `${e} ${e === 1 ? "item movido" : "itens movidos"} para ${t}`
|
|
40
|
+
}, s = {
|
|
41
|
+
sourceTitle: "Available",
|
|
42
|
+
targetTitle: "Selected",
|
|
43
|
+
search: "Search",
|
|
44
|
+
toTarget: "Move checked to the right",
|
|
45
|
+
toSource: "Move checked to the left",
|
|
46
|
+
allToTarget: "Move all to the right",
|
|
47
|
+
allToSource: "Move all to the left",
|
|
48
|
+
empty: "Nothing here",
|
|
49
|
+
noMatches: "No matches",
|
|
50
|
+
selected: (e, t) => `${e} of ${t} checked`,
|
|
51
|
+
moved: (e, t) => `${e} ${e === 1 ? "item moved" : "items moved"} to ${t}`
|
|
52
|
+
};
|
|
53
|
+
function c(e) {
|
|
54
|
+
return e === "en" ? s : o;
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { i as applyMove, n as filterItems, a as movableIds, t as searchTextOf, e as splitSides, c as transferStrings };
|
|
58
|
+
|
|
59
|
+
//# sourceMappingURL=transfer-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer-state.js","names":[],"sources":["../../../src/components/Transfer/transfer-state.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\n\n/** One movable entry. */\nexport interface TransferItem {\n /** Stable identity. What `value`/`onChange` carry. */\n id: string;\n /** What the row shows. */\n label: ReactNode;\n /** Text the search box matches against. Falls back to `label` when it is a string. */\n searchText?: string;\n /** Row that cannot move — a mandatory permission, a locked seat. */\n disabled?: boolean;\n /** Anything the app wants back in `renderItem`. */\n data?: Record<string, unknown>;\n}\n\n/** Which pane an operation is about. */\nexport type TransferSide = \"source\" | \"target\";\n\n/**\n * Split the catalogue into the two panes.\n *\n * The selected ids are the single source of truth — the panes are derived, never\n * stored. Two stored lists drift the moment the catalogue changes under them: a\n * permission that is removed upstream lingers in whichever pane held it, and an\n * id that appears in both is a bug nobody can see.\n *\n * Order follows `items`, so both panes read in the catalogue's order rather than\n * in the order somebody happened to click.\n *\n * @param items - The whole catalogue.\n * @param value - Ids currently on the target side.\n */\nexport function splitSides(\n items: readonly TransferItem[],\n value: readonly string[],\n): { source: TransferItem[]; target: TransferItem[] } {\n const selected = new Set(value);\n const source: TransferItem[] = [];\n const target: TransferItem[] = [];\n for (const item of items) {\n (selected.has(item.id) ? target : source).push(item);\n }\n return { source, target };\n}\n\n/** Text a row is searched by. */\nexport function searchTextOf(item: TransferItem): string {\n if (item.searchText !== undefined) return item.searchText;\n return typeof item.label === \"string\" ? item.label : \"\";\n}\n\n/**\n * Filter a pane by a query, case- and accent-insensitively.\n *\n * Accent folding is not optional for a PT-BR audience: typing \"sao\" has to find\n * \"São Paulo\", and a plain `includes` would not.\n */\nexport function filterItems(items: readonly TransferItem[], query: string): TransferItem[] {\n const needle = fold(query);\n if (!needle) return [...items];\n return items.filter((item) => fold(searchTextOf(item)).includes(needle));\n}\n\n/** Lowercase and strip diacritics. */\nfunction fold(text: string): string {\n return text\n .normalize(\"NFD\")\n .replace(/\\p{Diacritic}/gu, \"\")\n .toLowerCase()\n .trim();\n}\n\n/**\n * Apply a move and return the next value.\n *\n * Disabled rows are dropped here rather than at the call site, so no caller can\n * move one by pressing the bulk button — the check has to live where the\n * decision is made, not in each of the four places that trigger one.\n *\n * @param params.value - Current target ids.\n * @param params.moving - Ids being moved.\n * @param params.to - Destination pane.\n * @param params.items - The catalogue, to look disabled rows up.\n * @param params.order - Ids in catalogue order, so the result stays stable.\n * @returns The next value, in catalogue order.\n */\nexport function applyMove({\n value,\n moving,\n to,\n items,\n}: {\n value: readonly string[];\n moving: readonly string[];\n to: TransferSide;\n items: readonly TransferItem[];\n}): string[] {\n const byId = new Map(items.map((item) => [item.id, item]));\n const allowed = moving.filter((id) => byId.has(id) && !byId.get(id)?.disabled);\n const next = new Set(value);\n for (const id of allowed) {\n if (to === \"target\") next.add(id);\n else next.delete(id);\n }\n return items.filter((item) => next.has(item.id)).map((item) => item.id);\n}\n\n/** Ids in `items` that a checkbox set may still act on. */\nexport function movableIds(items: readonly TransferItem[]): string[] {\n return items.filter((item) => !item.disabled).map((item) => item.id);\n}\n\n/** Labels the component needs, per locale. */\ninterface TransferStrings {\n sourceTitle: string;\n targetTitle: string;\n search: string;\n toTarget: string;\n toSource: string;\n allToTarget: string;\n allToSource: string;\n empty: string;\n noMatches: string;\n selected: (n: number, total: number) => string;\n moved: (n: number, side: string) => string;\n}\n\nconst PT_BR: TransferStrings = {\n sourceTitle: \"Disponíveis\",\n targetTitle: \"Selecionados\",\n search: \"Buscar\",\n toTarget: \"Mover selecionados para a direita\",\n toSource: \"Mover selecionados para a esquerda\",\n allToTarget: \"Mover todos para a direita\",\n allToSource: \"Mover todos para a esquerda\",\n empty: \"Nada aqui\",\n noMatches: \"Nenhum resultado\",\n selected: (n, total) => `${n} de ${total} marcados`,\n moved: (n, side) => `${n} ${n === 1 ? \"item movido\" : \"itens movidos\"} para ${side}`,\n};\n\nconst EN: TransferStrings = {\n sourceTitle: \"Available\",\n targetTitle: \"Selected\",\n search: \"Search\",\n toTarget: \"Move checked to the right\",\n toSource: \"Move checked to the left\",\n allToTarget: \"Move all to the right\",\n allToSource: \"Move all to the left\",\n empty: \"Nothing here\",\n noMatches: \"No matches\",\n selected: (n, total) => `${n} of ${total} checked`,\n moved: (n, side) => `${n} ${n === 1 ? \"item moved\" : \"items moved\"} to ${side}`,\n};\n\n/** Locale strings for the dual list. */\nexport function transferStrings(locale: \"pt-BR\" | \"en\"): TransferStrings {\n return locale === \"en\" ? EN : PT_BR;\n}\n"],"mappings":";AAiCA,SAAgB,EACZ,GACA,GACkD;CAClD,IAAM,IAAW,IAAI,IAAI,CAAK,GACxB,IAAyB,CAAC,GAC1B,IAAyB,CAAC;CAChC,KAAK,IAAM,KAAQ,GACf,CAAC,EAAS,IAAI,EAAK,EAAE,IAAI,IAAS,EAAA,CAAQ,KAAK,CAAI;CAEvD,OAAO;EAAE;EAAQ;CAAO;AAC5B;AAGA,SAAgB,EAAa,GAA4B;CAErD,OADI,EAAK,eAAe,KAAA,IACjB,OAAO,EAAK,SAAU,WAAW,EAAK,QAAQ,KADX,EAAK;AAEnD;AAQA,SAAgB,EAAY,GAAgC,GAA+B;CACvF,IAAM,IAAS,EAAK,CAAK;CAEzB,OADK,IACE,EAAM,QAAQ,MAAS,EAAK,EAAa,CAAI,CAAC,CAAC,CAAC,SAAS,CAAM,CAAC,IADnD,CAAC,GAAG,CAAK;AAEjC;AAGA,SAAS,EAAK,GAAsB;CAChC,OAAO,EACF,UAAU,KAAK,CAAC,CAChB,QAAQ,mBAAmB,EAAE,CAAC,CAC9B,YAAY,CAAC,CACb,KAAK;AACd;AAgBA,SAAgB,EAAU,EACtB,UACA,WACA,OACA,YAMS;CACT,IAAM,IAAO,IAAI,IAAI,EAAM,KAAK,MAAS,CAAC,EAAK,IAAI,CAAI,CAAC,CAAC,GACnD,IAAU,EAAO,QAAQ,MAAO,EAAK,IAAI,CAAE,KAAK,CAAC,EAAK,IAAI,CAAE,CAAC,EAAE,QAAQ,GACvE,IAAO,IAAI,IAAI,CAAK;CAC1B,KAAK,IAAM,KAAM,GACb,AAAI,MAAO,WAAU,EAAK,IAAI,CAAE,IAC3B,EAAK,OAAO,CAAE;CAEvB,OAAO,EAAM,QAAQ,MAAS,EAAK,IAAI,EAAK,EAAE,CAAC,CAAC,CAAC,KAAK,MAAS,EAAK,EAAE;AAC1E;AAGA,SAAgB,EAAW,GAA0C;CACjE,OAAO,EAAM,QAAQ,MAAS,CAAC,EAAK,QAAQ,CAAC,CAAC,KAAK,MAAS,EAAK,EAAE;AACvE;AAiBA,IAAM,IAAyB;CAC3B,aAAa;CACb,aAAa;CACb,QAAQ;CACR,UAAU;CACV,UAAU;CACV,aAAa;CACb,aAAa;CACb,OAAO;CACP,WAAW;CACX,WAAW,GAAG,MAAU,GAAG,EAAE,MAAM,EAAM;CACzC,QAAQ,GAAG,MAAS,GAAG,EAAE,GAAG,MAAM,IAAI,gBAAgB,gBAAgB,QAAQ;AAClF,GAEM,IAAsB;CACxB,aAAa;CACb,aAAa;CACb,QAAQ;CACR,UAAU;CACV,UAAU;CACV,aAAa;CACb,aAAa;CACb,OAAO;CACP,WAAW;CACX,WAAW,GAAG,MAAU,GAAG,EAAE,MAAM,EAAM;CACzC,QAAQ,GAAG,MAAS,GAAG,EAAE,GAAG,MAAM,IAAI,eAAe,cAAc,MAAM;AAC7E;AAGA,SAAgB,EAAgB,GAAyC;CACrE,OAAO,MAAW,OAAO,IAAK;AAClC"}
|