tempest-react-sdk 0.29.1 → 0.31.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.
Files changed (85) hide show
  1. package/dist/components/Chat/Chat.cjs +2 -0
  2. package/dist/components/Chat/Chat.cjs.map +1 -0
  3. package/dist/components/Chat/Chat.js +126 -0
  4. package/dist/components/Chat/Chat.js.map +1 -0
  5. package/dist/components/Chat/Chat.module.cjs +2 -0
  6. package/dist/components/Chat/Chat.module.cjs.map +1 -0
  7. package/dist/components/Chat/Chat.module.js +32 -0
  8. package/dist/components/Chat/Chat.module.js.map +1 -0
  9. package/dist/components/Chat/ChatComposer.cjs +2 -0
  10. package/dist/components/Chat/ChatComposer.cjs.map +1 -0
  11. package/dist/components/Chat/ChatComposer.js +67 -0
  12. package/dist/components/Chat/ChatComposer.js.map +1 -0
  13. package/dist/components/Chat/chat-groups.cjs +2 -0
  14. package/dist/components/Chat/chat-groups.cjs.map +1 -0
  15. package/dist/components/Chat/chat-groups.js +89 -0
  16. package/dist/components/Chat/chat-groups.js.map +1 -0
  17. package/dist/components/FilterBar/FilterBar.cjs +2 -0
  18. package/dist/components/FilterBar/FilterBar.cjs.map +1 -0
  19. package/dist/components/FilterBar/FilterBar.js +238 -0
  20. package/dist/components/FilterBar/FilterBar.js.map +1 -0
  21. package/dist/components/FilterBar/FilterBar.module.cjs +2 -0
  22. package/dist/components/FilterBar/FilterBar.module.cjs.map +1 -0
  23. package/dist/components/FilterBar/FilterBar.module.js +20 -0
  24. package/dist/components/FilterBar/FilterBar.module.js.map +1 -0
  25. package/dist/components/FilterBar/filter-model.cjs +2 -0
  26. package/dist/components/FilterBar/filter-model.cjs.map +1 -0
  27. package/dist/components/FilterBar/filter-model.js +167 -0
  28. package/dist/components/FilterBar/filter-model.js.map +1 -0
  29. package/dist/components/Markdown/Markdown.cjs +2 -0
  30. package/dist/components/Markdown/Markdown.cjs.map +1 -0
  31. package/dist/components/Markdown/Markdown.js +124 -0
  32. package/dist/components/Markdown/Markdown.js.map +1 -0
  33. package/dist/components/Markdown/Markdown.module.cjs +2 -0
  34. package/dist/components/Markdown/Markdown.module.cjs.map +1 -0
  35. package/dist/components/Markdown/Markdown.module.js +23 -0
  36. package/dist/components/Markdown/Markdown.module.js.map +1 -0
  37. package/dist/components/Markdown/markdown-parse.cjs +7 -0
  38. package/dist/components/Markdown/markdown-parse.cjs.map +1 -0
  39. package/dist/components/Markdown/markdown-parse.js +269 -0
  40. package/dist/components/Markdown/markdown-parse.js.map +1 -0
  41. package/dist/components/Markdown/markdown-url.cjs +2 -0
  42. package/dist/components/Markdown/markdown-url.cjs.map +1 -0
  43. package/dist/components/Markdown/markdown-url.js +28 -0
  44. package/dist/components/Markdown/markdown-url.js.map +1 -0
  45. package/dist/components/Masonry/Masonry.cjs +2 -0
  46. package/dist/components/Masonry/Masonry.cjs.map +1 -0
  47. package/dist/components/Masonry/Masonry.js +70 -0
  48. package/dist/components/Masonry/Masonry.js.map +1 -0
  49. package/dist/components/Masonry/Masonry.module.cjs +2 -0
  50. package/dist/components/Masonry/Masonry.module.cjs.map +1 -0
  51. package/dist/components/Masonry/Masonry.module.js +10 -0
  52. package/dist/components/Masonry/Masonry.module.js.map +1 -0
  53. package/dist/components/Masonry/masonry-layout.cjs +2 -0
  54. package/dist/components/Masonry/masonry-layout.cjs.map +1 -0
  55. package/dist/components/Masonry/masonry-layout.js +20 -0
  56. package/dist/components/Masonry/masonry-layout.js.map +1 -0
  57. package/dist/components/Tour/Tour.cjs +2 -0
  58. package/dist/components/Tour/Tour.cjs.map +1 -0
  59. package/dist/components/Tour/Tour.js +186 -0
  60. package/dist/components/Tour/Tour.js.map +1 -0
  61. package/dist/components/Tour/Tour.module.cjs +2 -0
  62. package/dist/components/Tour/Tour.module.cjs.map +1 -0
  63. package/dist/components/Tour/Tour.module.js +21 -0
  64. package/dist/components/Tour/Tour.module.js.map +1 -0
  65. package/dist/components/Tour/tour-position.cjs +2 -0
  66. package/dist/components/Tour/tour-position.cjs.map +1 -0
  67. package/dist/components/Tour/tour-position.js +105 -0
  68. package/dist/components/Tour/tour-position.js.map +1 -0
  69. package/dist/components/Transfer/Transfer.cjs +2 -0
  70. package/dist/components/Transfer/Transfer.cjs.map +1 -0
  71. package/dist/components/Transfer/Transfer.js +163 -0
  72. package/dist/components/Transfer/Transfer.js.map +1 -0
  73. package/dist/components/Transfer/Transfer.module.cjs +2 -0
  74. package/dist/components/Transfer/Transfer.module.cjs.map +1 -0
  75. package/dist/components/Transfer/Transfer.module.js +19 -0
  76. package/dist/components/Transfer/Transfer.module.js.map +1 -0
  77. package/dist/components/Transfer/transfer-state.cjs +2 -0
  78. package/dist/components/Transfer/transfer-state.cjs.map +1 -0
  79. package/dist/components/Transfer/transfer-state.js +59 -0
  80. package/dist/components/Transfer/transfer-state.js.map +1 -0
  81. package/dist/styles.css +1 -1
  82. package/dist/tempest-react-sdk.cjs +1 -1
  83. package/dist/tempest-react-sdk.d.ts +674 -0
  84. package/dist/tempest-react-sdk.js +117 -105
  85. package/package.json +1 -1
@@ -0,0 +1,105 @@
1
+ function e(e, t, n) {
2
+ return Math.max(t, Math.min(e, Math.max(t, n)));
3
+ }
4
+ function t(e, t, n, r) {
5
+ switch (e) {
6
+ case "top": return t.top - 12 - n.height >= 8;
7
+ case "bottom": return t.top + t.height + 12 + n.height <= r.height - 8;
8
+ case "left": return t.left - 12 - n.width >= 8;
9
+ case "right": return t.left + t.width + 12 + n.width <= r.width - 8;
10
+ default: return !0;
11
+ }
12
+ }
13
+ var n = {
14
+ bottom: [
15
+ "bottom",
16
+ "top",
17
+ "right",
18
+ "left"
19
+ ],
20
+ top: [
21
+ "top",
22
+ "bottom",
23
+ "right",
24
+ "left"
25
+ ],
26
+ right: [
27
+ "right",
28
+ "left",
29
+ "bottom",
30
+ "top"
31
+ ],
32
+ left: [
33
+ "left",
34
+ "right",
35
+ "bottom",
36
+ "top"
37
+ ]
38
+ };
39
+ function r({ target: r, card: i, viewport: a, preferred: o = "bottom" }) {
40
+ let s = {
41
+ placement: "center",
42
+ top: Math.max(8, (a.height - i.height) / 2),
43
+ left: Math.max(8, (a.width - i.width) / 2)
44
+ };
45
+ if (!r || o === "center") return s;
46
+ let c = (n[o] ?? n.bottom).find((e) => t(e, r, i, a));
47
+ if (!c) return s;
48
+ let l = r.left + r.width / 2 - i.width / 2, u = r.top + r.height / 2 - i.height / 2, d = c === "top" ? {
49
+ top: r.top - 12 - i.height,
50
+ left: l
51
+ } : c === "bottom" ? {
52
+ top: r.top + r.height + 12,
53
+ left: l
54
+ } : c === "left" ? {
55
+ top: u,
56
+ left: r.left - 12 - i.width
57
+ } : {
58
+ top: u,
59
+ left: r.left + r.width + 12
60
+ };
61
+ return {
62
+ placement: c,
63
+ top: e(d.top, 8, a.height - i.height - 8),
64
+ left: e(d.left, 8, a.width - i.width - 8)
65
+ };
66
+ }
67
+ function i(e, t, n = 4) {
68
+ if (!e) return [{
69
+ top: 0,
70
+ left: 0,
71
+ width: t.width,
72
+ height: t.height
73
+ }];
74
+ let r = Math.max(0, e.top - n), i = Math.max(0, e.left - n), a = Math.min(t.width, e.left + e.width + n), o = Math.min(t.height, e.top + e.height + n);
75
+ return [
76
+ {
77
+ top: 0,
78
+ left: 0,
79
+ width: t.width,
80
+ height: r
81
+ },
82
+ {
83
+ top: o,
84
+ left: 0,
85
+ width: t.width,
86
+ height: t.height - o
87
+ },
88
+ {
89
+ top: r,
90
+ left: 0,
91
+ width: i,
92
+ height: o - r
93
+ },
94
+ {
95
+ top: r,
96
+ left: a,
97
+ width: t.width - a,
98
+ height: o - r
99
+ }
100
+ ].filter((e) => e.width > 0 && e.height > 0);
101
+ }
102
+ //#endregion
103
+ export { i as backdropRects, r as placeCard };
104
+
105
+ //# sourceMappingURL=tour-position.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tour-position.js","names":[],"sources":["../../../src/components/Tour/tour-position.ts"],"sourcesContent":["/** Where the card sits relative to the highlighted element. */\nexport type TourPlacement = \"top\" | \"bottom\" | \"left\" | \"right\" | \"center\";\n\n/** A rectangle in viewport coordinates. */\nexport interface TourRect {\n top: number;\n left: number;\n width: number;\n height: number;\n}\n\n/** Viewport size the placement is solved against. */\nexport interface TourViewport {\n width: number;\n height: number;\n}\n\n/** Gap between the highlighted element and the card. */\nconst OFFSET = 12;\n\n/** Smallest margin the card keeps from the viewport edge. */\nconst MARGIN = 8;\n\n/** Clamp a value into a range, tolerating a range narrower than the value. */\nfunction clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(value, Math.max(min, max)));\n}\n\n/** Does the card fit on this side of the target? */\nfunction fits(\n placement: TourPlacement,\n target: TourRect,\n card: { width: number; height: number },\n viewport: TourViewport,\n): boolean {\n switch (placement) {\n case \"top\":\n return target.top - OFFSET - card.height >= MARGIN;\n case \"bottom\":\n return target.top + target.height + OFFSET + card.height <= viewport.height - MARGIN;\n case \"left\":\n return target.left - OFFSET - card.width >= MARGIN;\n case \"right\":\n return target.left + target.width + OFFSET + card.width <= viewport.width - MARGIN;\n default:\n return true;\n }\n}\n\n/** Sides tried, in order, when the preferred one does not fit. */\nconst FALLBACKS: Record<Exclude<TourPlacement, \"center\">, TourPlacement[]> = {\n bottom: [\"bottom\", \"top\", \"right\", \"left\"],\n top: [\"top\", \"bottom\", \"right\", \"left\"],\n right: [\"right\", \"left\", \"bottom\", \"top\"],\n left: [\"left\", \"right\", \"bottom\", \"top\"],\n};\n\n/**\n * Place the tour card next to the highlighted element.\n *\n * The preferred side is tried first and the opposite one second, because flipping\n * a \"below\" card to \"above\" keeps the reading relationship with the target;\n * jumping to a side would move the card across the screen for no reason the reader\n * can see.\n *\n * Nothing fitting anywhere ends at `center`: a card pinned half off-screen is worse\n * than a card in the middle, and this happens for real whenever the target is\n * taller than the viewport.\n *\n * The result is always clamped inside the viewport, so the card cannot be pushed\n * off the edge by a target near a corner.\n *\n * @param params.target - The highlighted element's rect, viewport coordinates.\n * @param params.card - Measured card size.\n * @param params.viewport - Viewport size.\n * @param params.preferred - Placement the step asked for. Default `\"bottom\"`.\n * @returns The chosen placement and the card's top/left.\n */\nexport function placeCard({\n target,\n card,\n viewport,\n preferred = \"bottom\",\n}: {\n target: TourRect | null;\n card: { width: number; height: number };\n viewport: TourViewport;\n preferred?: TourPlacement;\n}): { placement: TourPlacement; top: number; left: number } {\n const centered = {\n placement: \"center\" as TourPlacement,\n top: Math.max(MARGIN, (viewport.height - card.height) / 2),\n left: Math.max(MARGIN, (viewport.width - card.width) / 2),\n };\n\n // No target — a step whose element is not on the page still has to show its\n // message, so it is shown centered rather than dropped.\n if (!target || preferred === \"center\") return centered;\n\n const order = FALLBACKS[preferred as Exclude<TourPlacement, \"center\">] ?? FALLBACKS.bottom;\n const placement = order.find((candidate) => fits(candidate, target, card, viewport));\n if (!placement) return centered;\n\n const horizontalCenter = target.left + target.width / 2 - card.width / 2;\n const verticalCenter = target.top + target.height / 2 - card.height / 2;\n\n const raw =\n placement === \"top\"\n ? { top: target.top - OFFSET - card.height, left: horizontalCenter }\n : placement === \"bottom\"\n ? { top: target.top + target.height + OFFSET, left: horizontalCenter }\n : placement === \"left\"\n ? { top: verticalCenter, left: target.left - OFFSET - card.width }\n : { top: verticalCenter, left: target.left + target.width + OFFSET };\n\n return {\n placement,\n top: clamp(raw.top, MARGIN, viewport.height - card.height - MARGIN),\n left: clamp(raw.left, MARGIN, viewport.width - card.width - MARGIN),\n };\n}\n\n/**\n * The four backdrop rectangles that surround the highlighted element.\n *\n * Four rects rather than one overlay with a `box-shadow` hole: a box-shadow is not\n * hit-testable, so the \"dimmed\" area would not block clicks — and blocking the rest\n * of the page while leaving the highlighted control usable is the whole behaviour a\n * coachmark needs. Any rect that comes out empty is dropped, which is what happens\n * when the target touches an edge.\n *\n * @param target - Highlighted rect, or `null` for a full-screen backdrop.\n * @param viewport - Viewport size.\n * @param padding - Extra space kept clear around the target.\n */\nexport function backdropRects(\n target: TourRect | null,\n viewport: TourViewport,\n padding = 4,\n): TourRect[] {\n if (!target) {\n return [{ top: 0, left: 0, width: viewport.width, height: viewport.height }];\n }\n const top = Math.max(0, target.top - padding);\n const left = Math.max(0, target.left - padding);\n const right = Math.min(viewport.width, target.left + target.width + padding);\n const bottom = Math.min(viewport.height, target.top + target.height + padding);\n\n return [\n { top: 0, left: 0, width: viewport.width, height: top },\n { top: bottom, left: 0, width: viewport.width, height: viewport.height - bottom },\n { top, left: 0, width: left, height: bottom - top },\n { top, left: right, width: viewport.width - right, height: bottom - top },\n ].filter((rect) => rect.width > 0 && rect.height > 0);\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require("../../utils/cn.cjs"),t=require("../Checkbox/Checkbox.cjs"),n=require("../VisuallyHidden/VisuallyHidden.cjs"),r=require("./transfer-state.cjs"),i=require("./Transfer.module.cjs");let a=require("react"),o=require("react/jsx-runtime");function s({items:s,value:c,onChange:l,sourceTitle:u,targetTitle:d,searchable:f,renderItem:p,locale:m=`pt-BR`,height:h=`16rem`,disabled:g=!1,className:_,...v}){let y=r.transferStrings(m),b=(0,a.useId)(),[x,S]=(0,a.useState)(new Set),[C,w]=(0,a.useState)({source:``,target:``}),[T,E]=(0,a.useState)(``),{source:D,target:O}=(0,a.useMemo)(()=>r.splitSides(s,c),[s,c]),k=f??s.length>8,A={source:k?r.filterItems(D,C.source):[...D],target:k?r.filterItems(O,C.target):[...O]},j=[{side:`source`,title:u??y.sourceTitle,items:D},{side:`target`,title:d??y.targetTitle,items:O}],M=(e,t)=>{if(g||e.length===0)return;let n=r.applyMove({value:c,moving:e,to:t,items:s}),i=t===`target`?n.length-c.length:c.length-n.length;if(l(n),S(t=>{let n=new Set(t);for(let t of e)n.delete(t);return n}),i>0){let e=t===`target`?y.targetTitle:y.sourceTitle;E(y.moved(i,String(e).toLowerCase()))}},N=e=>{S(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},P=e=>r.movableIds(A[e]).filter(e=>x.has(e)),F=P(`source`),I=P(`target`);return(0,o.jsxs)(`div`,{className:e.cn(i.default.wrapper,_),...v,children:[j.map(e=>{let n=A[e.side],a=r.movableIds(n),s=a.filter(e=>x.has(e)),c=a.length>0&&s.length===a.length,l=`${b}-${e.side}-title`;return(0,o.jsxs)(`section`,{className:i.default.pane,"aria-labelledby":l,children:[(0,o.jsxs)(`div`,{className:i.default.paneHeader,children:[(0,o.jsxs)(`div`,{className:i.default.paneTitle,children:[(0,o.jsx)(t.Checkbox,{checked:c,indeterminate:s.length>0&&!c,disabled:g||a.length===0,"aria-label":typeof e.title==`string`?e.title:String(e.side),onChange:()=>S(e=>{let t=new Set(e);for(let e of a)c?t.delete(e):t.add(e);return t})}),(0,o.jsx)(`h3`,{className:i.default.title,id:l,children:e.title})]}),(0,o.jsx)(`span`,{className:i.default.count,children:y.selected(s.length,e.items.length)})]}),k&&(0,o.jsx)(`input`,{type:`search`,className:i.default.search,value:C[e.side],placeholder:y.search,"aria-label":`${y.search}: ${typeof e.title==`string`?e.title:e.side}`,onChange:t=>w(n=>({...n,[e.side]:t.target.value}))}),(0,o.jsx)(`ul`,{className:i.default.list,style:{height:h},children:n.length===0?(0,o.jsx)(`li`,{className:i.default.empty,children:C[e.side]?y.noMatches:y.empty}):n.map(n=>(0,o.jsx)(`li`,{className:i.default.row,children:(0,o.jsx)(t.Checkbox,{checked:x.has(n.id),disabled:g||n.disabled,label:p?p(n,e.side):n.label,onChange:()=>N(n.id),onDoubleClick:()=>M([n.id],e.side===`source`?`target`:`source`)})},n.id))})]},e.side)}),(0,o.jsxs)(`div`,{className:i.default.controls,children:[(0,o.jsx)(`button`,{type:`button`,className:i.default.control,disabled:g||r.movableIds(A.source).length===0,"aria-label":y.allToTarget,title:y.allToTarget,onClick:()=>M(r.movableIds(A.source),`target`),children:`»`}),(0,o.jsx)(`button`,{type:`button`,className:i.default.control,disabled:g||F.length===0,"aria-label":y.toTarget,title:y.toTarget,onClick:()=>M(F,`target`),children:`›`}),(0,o.jsx)(`button`,{type:`button`,className:i.default.control,disabled:g||I.length===0,"aria-label":y.toSource,title:y.toSource,onClick:()=>M(I,`source`),children:`‹`}),(0,o.jsx)(`button`,{type:`button`,className:i.default.control,disabled:g||r.movableIds(A.target).length===0,"aria-label":y.allToSource,title:y.allToSource,onClick:()=>M(r.movableIds(A.target),`source`),children:`«`})]}),(0,o.jsx)(n.VisuallyHidden,{"aria-live":`polite`,role:`status`,children:T})]})}exports.Transfer=s;
2
+ //# sourceMappingURL=Transfer.cjs.map
@@ -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":"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"}
@@ -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"}