tempest-react-sdk 0.35.0 → 0.36.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 (211) hide show
  1. package/README.md +72 -70
  2. package/dist/audio/audio-output.cjs +2 -0
  3. package/dist/audio/audio-output.cjs.map +1 -0
  4. package/dist/audio/audio-output.js +17 -0
  5. package/dist/audio/audio-output.js.map +1 -0
  6. package/dist/audio/audio-player.cjs +1 -1
  7. package/dist/audio/audio-player.cjs.map +1 -1
  8. package/dist/audio/audio-player.js +21 -20
  9. package/dist/audio/audio-player.js.map +1 -1
  10. package/dist/audio/audio-recorder.cjs +2 -0
  11. package/dist/audio/audio-recorder.cjs.map +1 -0
  12. package/dist/audio/audio-recorder.js +30 -0
  13. package/dist/audio/audio-recorder.js.map +1 -0
  14. package/dist/audio/duration.cjs +2 -0
  15. package/dist/audio/duration.cjs.map +1 -0
  16. package/dist/audio/duration.js +10 -0
  17. package/dist/audio/duration.js.map +1 -0
  18. package/dist/audio/level-meter.cjs +2 -0
  19. package/dist/audio/level-meter.cjs.map +1 -0
  20. package/dist/audio/level-meter.js +34 -0
  21. package/dist/audio/level-meter.js.map +1 -0
  22. package/dist/audio/media-access.cjs +2 -0
  23. package/dist/audio/media-access.cjs.map +1 -0
  24. package/dist/audio/media-access.js +51 -0
  25. package/dist/audio/media-access.js.map +1 -0
  26. package/dist/audio/use-audio-recorder.cjs +2 -0
  27. package/dist/audio/use-audio-recorder.cjs.map +1 -0
  28. package/dist/audio/use-audio-recorder.js +94 -0
  29. package/dist/audio/use-audio-recorder.js.map +1 -0
  30. package/dist/audio/use-audio.cjs.map +1 -1
  31. package/dist/audio/use-audio.js.map +1 -1
  32. package/dist/audio/use-microphone.cjs +2 -0
  33. package/dist/audio/use-microphone.cjs.map +1 -0
  34. package/dist/audio/use-microphone.js +54 -0
  35. package/dist/audio/use-microphone.js.map +1 -0
  36. package/dist/audio/wav.cjs +2 -0
  37. package/dist/audio/wav.cjs.map +1 -0
  38. package/dist/audio/wav.js +49 -0
  39. package/dist/audio/wav.js.map +1 -0
  40. package/dist/auth/passkey.cjs +2 -0
  41. package/dist/auth/passkey.cjs.map +1 -0
  42. package/dist/auth/passkey.js +171 -0
  43. package/dist/auth/passkey.js.map +1 -0
  44. package/dist/auth/use-passkey.cjs +2 -0
  45. package/dist/auth/use-passkey.cjs.map +1 -0
  46. package/dist/auth/use-passkey.js +120 -0
  47. package/dist/auth/use-passkey.js.map +1 -0
  48. package/dist/br/PixQRCode.cjs +2 -0
  49. package/dist/br/PixQRCode.cjs.map +1 -0
  50. package/dist/br/PixQRCode.js +70 -0
  51. package/dist/br/PixQRCode.js.map +1 -0
  52. package/dist/br/PixQRCode.module.cjs +2 -0
  53. package/dist/br/PixQRCode.module.cjs.map +1 -0
  54. package/dist/br/PixQRCode.module.js +15 -0
  55. package/dist/br/PixQRCode.module.js.map +1 -0
  56. package/dist/br/boleto.cjs +2 -0
  57. package/dist/br/boleto.cjs.map +1 -0
  58. package/dist/br/boleto.js +225 -0
  59. package/dist/br/boleto.js.map +1 -0
  60. package/dist/br/holidays.cjs +2 -0
  61. package/dist/br/holidays.cjs.map +1 -0
  62. package/dist/br/holidays.js +148 -0
  63. package/dist/br/holidays.js.map +1 -0
  64. package/dist/br/nfe.cjs +2 -0
  65. package/dist/br/nfe.cjs.map +1 -0
  66. package/dist/br/nfe.js +104 -0
  67. package/dist/br/nfe.js.map +1 -0
  68. package/dist/br/pix.cjs +2 -0
  69. package/dist/br/pix.cjs.map +1 -0
  70. package/dist/br/pix.js +148 -0
  71. package/dist/br/pix.js.map +1 -0
  72. package/dist/br.cjs +1 -1
  73. package/dist/br.cjs.map +1 -1
  74. package/dist/br.d.ts +878 -0
  75. package/dist/br.js +7 -2
  76. package/dist/br.js.map +1 -1
  77. package/dist/capture/barcode.cjs +2 -0
  78. package/dist/capture/barcode.cjs.map +1 -0
  79. package/dist/capture/barcode.js +59 -0
  80. package/dist/capture/barcode.js.map +1 -0
  81. package/dist/capture/media-recorder.cjs +2 -0
  82. package/dist/capture/media-recorder.cjs.map +1 -0
  83. package/dist/capture/media-recorder.js +64 -0
  84. package/dist/capture/media-recorder.js.map +1 -0
  85. package/dist/capture/use-barcode-scanner.cjs +2 -0
  86. package/dist/capture/use-barcode-scanner.cjs.map +1 -0
  87. package/dist/capture/use-barcode-scanner.js +86 -0
  88. package/dist/capture/use-barcode-scanner.js.map +1 -0
  89. package/dist/capture/use-screen-capture.cjs +2 -0
  90. package/dist/capture/use-screen-capture.cjs.map +1 -0
  91. package/dist/capture/use-screen-capture.js +79 -0
  92. package/dist/capture/use-screen-capture.js.map +1 -0
  93. package/dist/capture/use-speech-recognition.cjs +2 -0
  94. package/dist/capture/use-speech-recognition.cjs.map +1 -0
  95. package/dist/capture/use-speech-recognition.js +118 -0
  96. package/dist/capture/use-speech-recognition.js.map +1 -0
  97. package/dist/capture/use-torch.cjs +2 -0
  98. package/dist/capture/use-torch.cjs.map +1 -0
  99. package/dist/capture/use-torch.js +33 -0
  100. package/dist/capture/use-torch.js.map +1 -0
  101. package/dist/capture/use-video-recorder.cjs +2 -0
  102. package/dist/capture/use-video-recorder.cjs.map +1 -0
  103. package/dist/capture/use-video-recorder.js +90 -0
  104. package/dist/capture/use-video-recorder.js.map +1 -0
  105. package/dist/capture/video-recorder.cjs +2 -0
  106. package/dist/capture/video-recorder.cjs.map +1 -0
  107. package/dist/capture/video-recorder.js +31 -0
  108. package/dist/capture/video-recorder.js.map +1 -0
  109. package/dist/components/AIChat/AIChat.cjs +1 -1
  110. package/dist/components/AIChat/AIChat.cjs.map +1 -1
  111. package/dist/components/AIChat/AIChat.js +93 -97
  112. package/dist/components/AIChat/AIChat.js.map +1 -1
  113. package/dist/components/AIChat/AIChatComposer.cjs +1 -1
  114. package/dist/components/AIChat/AIChatComposer.cjs.map +1 -1
  115. package/dist/components/AIChat/AIChatComposer.js +2 -1
  116. package/dist/components/AIChat/AIChatComposer.js.map +1 -1
  117. package/dist/components/AIChat/AIChatTurn.cjs +2 -2
  118. package/dist/components/AIChat/AIChatTurn.js +6 -6
  119. package/dist/components/AIChat/AIChatTurn.js.map +1 -1
  120. package/dist/components/Alert/Alert.module.cjs.map +1 -1
  121. package/dist/components/Alert/Alert.module.js.map +1 -1
  122. package/dist/components/AudioPlayer/AudioPlayer.cjs +2 -0
  123. package/dist/components/AudioPlayer/AudioPlayer.cjs.map +1 -0
  124. package/dist/components/AudioPlayer/AudioPlayer.js +123 -0
  125. package/dist/components/AudioPlayer/AudioPlayer.js.map +1 -0
  126. package/dist/components/AudioPlayer/AudioPlayer.module.cjs +2 -0
  127. package/dist/components/AudioPlayer/AudioPlayer.module.cjs.map +1 -0
  128. package/dist/components/AudioPlayer/AudioPlayer.module.js +12 -0
  129. package/dist/components/AudioPlayer/AudioPlayer.module.js.map +1 -0
  130. package/dist/components/AudioRecorder/AudioRecorder.cjs +2 -0
  131. package/dist/components/AudioRecorder/AudioRecorder.cjs.map +1 -0
  132. package/dist/components/AudioRecorder/AudioRecorder.js +179 -0
  133. package/dist/components/AudioRecorder/AudioRecorder.js.map +1 -0
  134. package/dist/components/AudioRecorder/AudioRecorder.module.cjs +2 -0
  135. package/dist/components/AudioRecorder/AudioRecorder.module.cjs.map +1 -0
  136. package/dist/components/AudioRecorder/AudioRecorder.module.js +19 -0
  137. package/dist/components/AudioRecorder/AudioRecorder.module.js.map +1 -0
  138. package/dist/components/Badge/Badge.module.cjs.map +1 -1
  139. package/dist/components/Badge/Badge.module.js.map +1 -1
  140. package/dist/components/BarcodeScanner/BarcodeScanner.cjs +2 -0
  141. package/dist/components/BarcodeScanner/BarcodeScanner.cjs.map +1 -0
  142. package/dist/components/BarcodeScanner/BarcodeScanner.js +123 -0
  143. package/dist/components/BarcodeScanner/BarcodeScanner.js.map +1 -0
  144. package/dist/components/BarcodeScanner/BarcodeScanner.module.cjs +2 -0
  145. package/dist/components/BarcodeScanner/BarcodeScanner.module.cjs.map +1 -0
  146. package/dist/components/BarcodeScanner/BarcodeScanner.module.js +22 -0
  147. package/dist/components/BarcodeScanner/BarcodeScanner.module.js.map +1 -0
  148. package/dist/components/BottomNavigation/BottomNavigation.module.cjs.map +1 -1
  149. package/dist/components/BottomNavigation/BottomNavigation.module.js.map +1 -1
  150. package/dist/components/Button/Button.module.cjs.map +1 -1
  151. package/dist/components/Button/Button.module.js.map +1 -1
  152. package/dist/components/Combobox/Combobox.module.cjs.map +1 -1
  153. package/dist/components/Combobox/Combobox.module.js.map +1 -1
  154. package/dist/components/DataTable/DataTable.cjs +1 -1
  155. package/dist/components/DataTable/DataTable.cjs.map +1 -1
  156. package/dist/components/DataTable/DataTable.js +203 -66
  157. package/dist/components/DataTable/DataTable.js.map +1 -1
  158. package/dist/components/DataTable/DataTable.module.cjs +1 -1
  159. package/dist/components/DataTable/DataTable.module.cjs.map +1 -1
  160. package/dist/components/DataTable/DataTable.module.js +9 -3
  161. package/dist/components/DataTable/DataTable.module.js.map +1 -1
  162. package/dist/components/DataTable/EditableCell.cjs +2 -0
  163. package/dist/components/DataTable/EditableCell.cjs.map +1 -0
  164. package/dist/components/DataTable/EditableCell.js +77 -0
  165. package/dist/components/DataTable/EditableCell.js.map +1 -0
  166. package/dist/components/DataTable/edit-labels.cjs +2 -0
  167. package/dist/components/DataTable/edit-labels.cjs.map +1 -0
  168. package/dist/components/DataTable/edit-labels.js +11 -0
  169. package/dist/components/DataTable/edit-labels.js.map +1 -0
  170. package/dist/components/DateRangePicker/DateRangePicker.module.cjs.map +1 -1
  171. package/dist/components/DateRangePicker/DateRangePicker.module.js.map +1 -1
  172. package/dist/components/MultiSelect/MultiSelect.module.cjs.map +1 -1
  173. package/dist/components/MultiSelect/MultiSelect.module.js.map +1 -1
  174. package/dist/components/NavigationRail/NavigationRail.module.cjs.map +1 -1
  175. package/dist/components/NavigationRail/NavigationRail.module.js.map +1 -1
  176. package/dist/components/SyncStatusBadge/SyncStatusBadge.cjs +1 -1
  177. package/dist/components/SyncStatusBadge/SyncStatusBadge.cjs.map +1 -1
  178. package/dist/components/SyncStatusBadge/SyncStatusBadge.js +33 -32
  179. package/dist/components/SyncStatusBadge/SyncStatusBadge.js.map +1 -1
  180. package/dist/components/Toast/ToastProvider.cjs +1 -1
  181. package/dist/components/Toast/ToastProvider.cjs.map +1 -1
  182. package/dist/components/Toast/ToastProvider.js +1 -2
  183. package/dist/components/Toast/ToastProvider.js.map +1 -1
  184. package/dist/hooks/use-announce.cjs +2 -0
  185. package/dist/hooks/use-announce.cjs.map +1 -0
  186. package/dist/hooks/use-announce.js +50 -0
  187. package/dist/hooks/use-announce.js.map +1 -0
  188. package/dist/hooks/use-media-devices.cjs +2 -0
  189. package/dist/hooks/use-media-devices.cjs.map +1 -0
  190. package/dist/hooks/use-media-devices.js +46 -0
  191. package/dist/hooks/use-media-devices.js.map +1 -0
  192. package/dist/hooks/use-media-permission.cjs +2 -0
  193. package/dist/hooks/use-media-permission.cjs.map +1 -0
  194. package/dist/hooks/use-media-permission.js +33 -0
  195. package/dist/hooks/use-media-permission.js.map +1 -0
  196. package/dist/http/resumable-upload.cjs +2 -0
  197. package/dist/http/resumable-upload.cjs.map +1 -0
  198. package/dist/http/resumable-upload.js +249 -0
  199. package/dist/http/resumable-upload.js.map +1 -0
  200. package/dist/styles.css +1 -1
  201. package/dist/tempest-react-sdk.cjs +1 -1
  202. package/dist/tempest-react-sdk.d.ts +2485 -29
  203. package/dist/tempest-react-sdk.js +268 -244
  204. package/dist/vision/use-camera-stream.cjs +1 -1
  205. package/dist/vision/use-camera-stream.cjs.map +1 -1
  206. package/dist/vision/use-camera-stream.js +40 -57
  207. package/dist/vision/use-camera-stream.js.map +1 -1
  208. package/dist/vision.cjs +1 -1
  209. package/dist/vision.d.ts +25 -3
  210. package/dist/vision.js +19 -19
  211. package/package.json +1 -1
@@ -0,0 +1,77 @@
1
+ import { cn as e } from "../../utils/cn.js";
2
+ import { VisuallyHidden as t } from "../VisuallyHidden/VisuallyHidden.js";
3
+ import n from "./DataTable.module.js";
4
+ import { useEffect as r, useRef as i, useState as a } from "react";
5
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
6
+ //#region src/components/DataTable/EditableCell.tsx
7
+ function c({ initial: t, type: r, label: s, describedBy: c, invalid: l, onCommit: u, onCancel: d }) {
8
+ let [f, p] = a(t), m = i(!1);
9
+ function h(e) {
10
+ m.current = !0, u(f, e);
11
+ }
12
+ function g(e) {
13
+ if (e.key === "Enter") {
14
+ e.preventDefault(), h("none");
15
+ return;
16
+ }
17
+ if (e.key === "Escape") {
18
+ e.preventDefault(), m.current = !0, d();
19
+ return;
20
+ }
21
+ e.key === "Tab" && (e.preventDefault(), h(e.shiftKey ? "previous" : "next"));
22
+ }
23
+ return /* @__PURE__ */ o("input", {
24
+ autoFocus: !0,
25
+ className: e(n.cellEditor, l && n.cellEditorInvalid),
26
+ type: r,
27
+ value: f,
28
+ "aria-label": s,
29
+ "aria-invalid": l || void 0,
30
+ "aria-describedby": c,
31
+ onChange: (e) => {
32
+ m.current = !1, p(e.target.value);
33
+ },
34
+ onKeyDown: g,
35
+ onBlur: () => {
36
+ m.current || h("none");
37
+ }
38
+ });
39
+ }
40
+ function l({ children: a, text: l, columnLabel: u, rowNumber: d, inputType: f, editing: p, refocus: m, saving: h, error: g, errorId: _, labels: v, onOpen: y, onCommit: b, onCancel: x }) {
41
+ let S = i(null), C = i(!1);
42
+ return r(() => {
43
+ !p && C.current && m && S.current?.focus(), C.current = p;
44
+ }, [p, m]), /* @__PURE__ */ s("span", {
45
+ className: n.cellWrap,
46
+ children: [p ? /* @__PURE__ */ o(c, {
47
+ initial: l,
48
+ type: f,
49
+ label: v.editor(u, d),
50
+ describedBy: g ? _ : void 0,
51
+ invalid: g !== null,
52
+ onCommit: b,
53
+ onCancel: x
54
+ }) : /* @__PURE__ */ s("button", {
55
+ ref: S,
56
+ type: "button",
57
+ className: e(n.cellButton, g && n.cellButtonInvalid),
58
+ "aria-describedby": g ? _ : void 0,
59
+ "aria-busy": h || void 0,
60
+ onClick: y,
61
+ children: [
62
+ /* @__PURE__ */ o(t, { children: v.editCell(u) }),
63
+ " ",
64
+ a
65
+ ]
66
+ }), g && /* @__PURE__ */ o("span", {
67
+ className: n.cellError,
68
+ id: _,
69
+ role: "alert",
70
+ children: g
71
+ })]
72
+ });
73
+ }
74
+ //#endregion
75
+ export { l as EditableCell };
76
+
77
+ //# sourceMappingURL=EditableCell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditableCell.js","names":[],"sources":["../../../src/components/DataTable/EditableCell.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport type { KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport type { CellCommitMove, DataTableEditLabels } from \"./edit-labels\";\nimport styles from \"./DataTable.module.css\";\n\ninterface CellEditorProps {\n /** Text the editor opens with. */\n initial: string;\n /** `<input type>` for the editor. */\n type: string;\n /** Accessible name. */\n label: string;\n /** Id of the message element, when there is one. */\n describedBy?: string;\n /** Whether the current draft was rejected by validation. */\n invalid: boolean;\n /** Commit the draft. The parent decides whether the editor closes. */\n onCommit: (raw: string, move: CellCommitMove) => void;\n /** Discard the draft. */\n onCancel: () => void;\n}\n\n/**\n * The `<input>` half of an editable cell.\n *\n * A separate component so it **mounts fresh** every time a cell opens: the draft\n * then starts from the current value through `useState`'s initialiser, with no\n * effect syncing props into state and no render where the draft is stale.\n *\n * `Tab` is intercepted rather than left to the browser. Default tab order would\n * walk into the next row's trigger button, which is one keystroke away from the\n * next *editor* — and in a table the reader is editing, moving cell to cell is the\n * expected behaviour. `Escape` discards; `Enter` commits and closes; blur commits,\n * because clicking away from a half-typed cell and losing the typing is a data-loss\n * bug users report as \"the table ate my edit\".\n *\n * `autoFocus` is safe here in a way it is not on page load: the editor only exists\n * because the user just clicked the cell or pressed `Tab` into it, so focus is\n * following the interaction rather than stealing it.\n *\n * The `settled` flag stops a keyboard commit from being repeated by the blur that\n * follows it. Typing clears the flag again, because after a commit the parent\n * rejected — the only case where the editor is still open and focused — the draft is\n * live once more, and dropping *that* edit on blur would be the same data loss the\n * blur-commits rule exists to prevent.\n */\nfunction CellEditor({\n initial,\n type,\n label,\n describedBy,\n invalid,\n onCommit,\n onCancel,\n}: CellEditorProps) {\n const [draft, setDraft] = useState<string>(initial);\n const settled = useRef(false);\n\n function finish(move: CellCommitMove): void {\n settled.current = true;\n onCommit(draft, move);\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finish(\"none\");\n return;\n }\n if (event.key === \"Escape\") {\n event.preventDefault();\n settled.current = true;\n onCancel();\n return;\n }\n if (event.key === \"Tab\") {\n event.preventDefault();\n finish(event.shiftKey ? \"previous\" : \"next\");\n }\n }\n\n return (\n <input\n autoFocus\n className={cn(styles.cellEditor, invalid && styles.cellEditorInvalid)}\n type={type}\n value={draft}\n aria-label={label}\n aria-invalid={invalid || undefined}\n aria-describedby={describedBy}\n onChange={(event) => {\n settled.current = false;\n setDraft(event.target.value);\n }}\n onKeyDown={handleKeyDown}\n onBlur={() => {\n if (settled.current) return;\n finish(\"none\");\n }}\n />\n );\n}\n\nexport interface EditableCellProps {\n /** Rendered value, used when the cell is closed. */\n children: ReactNode;\n /** Plain-text value, for the editor's initial draft and the trigger's name. */\n text: string;\n /** Header text of this column, for accessible names. */\n columnLabel: string;\n /** 1-based row number on the current page. */\n rowNumber: number;\n /** `<input type>` for the editor. */\n inputType: string;\n /** Whether this cell owns the open editor. */\n editing: boolean;\n /** Return focus to the trigger when the editor closes (Enter/Escape, not Tab). */\n refocus: boolean;\n /** A save is in flight for this cell. */\n saving: boolean;\n /** Validation or save error to surface, or null. */\n error: string | null;\n /** Stable id used for `aria-describedby` on the input and the trigger. */\n errorId: string;\n /** Copy for the affordances. */\n labels: DataTableEditLabels;\n /** Open the editor. */\n onOpen: () => void;\n /** Commit a draft. */\n onCommit: (raw: string, move: CellCommitMove) => void;\n /** Close without committing. */\n onCancel: () => void;\n}\n\n/**\n * One cell of a {@link DataTable} column marked `editable`.\n *\n * Closed, it is a button carrying the value — a plain `<td>` with a click handler\n * would be invisible to a keyboard and unnamed to a screen reader, and a\n * `tabIndex`-only cell announces no role. Open, it is an `<input>`.\n *\n * The button's name comes from its **contents**: a visually hidden \"Editar {coluna}:\"\n * in front of whatever the column rendered. An `aria-label` built from the raw value\n * would read \"850000\" over a cell showing `R$ 8.500,00`, which fails WCAG 2.5.3 (Label\n * in Name) and leaves voice control unable to address the cell by what it says.\n *\n * The error message is a `role=\"alert\"` tied to the input (and to the trigger once\n * the editor closes) through `aria-describedby`, so a rejected save is announced and\n * then still reachable — an optimistic update that rolls back silently leaves the\n * user believing the edit stuck.\n */\nexport function EditableCell({\n children,\n text,\n columnLabel,\n rowNumber,\n inputType,\n editing,\n refocus,\n saving,\n error,\n errorId,\n labels,\n onOpen,\n onCommit,\n onCancel,\n}: EditableCellProps) {\n const trigger = useRef<HTMLButtonElement | null>(null);\n const wasEditing = useRef(false);\n\n useEffect(() => {\n if (!editing && wasEditing.current && refocus) trigger.current?.focus();\n wasEditing.current = editing;\n }, [editing, refocus]);\n\n return (\n <span className={styles.cellWrap}>\n {editing ? (\n <CellEditor\n initial={text}\n type={inputType}\n label={labels.editor(columnLabel, rowNumber)}\n describedBy={error ? errorId : undefined}\n invalid={error !== null}\n onCommit={onCommit}\n onCancel={onCancel}\n />\n ) : (\n <button\n ref={trigger}\n type=\"button\"\n className={cn(styles.cellButton, error && styles.cellButtonInvalid)}\n aria-describedby={error ? errorId : undefined}\n aria-busy={saving || undefined}\n onClick={onOpen}\n >\n <VisuallyHidden>{labels.editCell(columnLabel)}</VisuallyHidden> {children}\n </button>\n )}\n {error && (\n <span className={styles.cellError} id={errorId} role=\"alert\">\n {error}\n </span>\n )}\n </span>\n );\n}\n"],"mappings":";;;;;;AAgDA,SAAS,EAAW,EAChB,YACA,SACA,UACA,gBACA,YACA,aACA,eACgB;CAChB,IAAM,CAAC,GAAO,KAAY,EAAiB,CAAO,GAC5C,IAAU,EAAO,EAAK;CAE5B,SAAS,EAAO,GAA4B;EAExC,AADA,EAAQ,UAAU,IAClB,EAAS,GAAO,CAAI;CACxB;CAEA,SAAS,EAAc,GAA8C;EACjE,IAAI,EAAM,QAAQ,SAAS;GAEvB,AADA,EAAM,eAAe,GACrB,EAAO,MAAM;GACb;EACJ;EACA,IAAI,EAAM,QAAQ,UAAU;GAGxB,AAFA,EAAM,eAAe,GACrB,EAAQ,UAAU,IAClB,EAAS;GACT;EACJ;EACA,AAAI,EAAM,QAAQ,UACd,EAAM,eAAe,GACrB,EAAO,EAAM,WAAW,aAAa,MAAM;CAEnD;CAEA,OACI,kBAAC,SAAD;EACI,WAAA;EACA,WAAW,EAAG,EAAO,YAAY,KAAW,EAAO,iBAAiB;EAC9D;EACN,OAAO;EACP,cAAY;EACZ,gBAAc,KAAW,KAAA;EACzB,oBAAkB;EAClB,WAAW,MAAU;GAEjB,AADA,EAAQ,UAAU,IAClB,EAAS,EAAM,OAAO,KAAK;EAC/B;EACA,WAAW;EACX,cAAc;GACN,EAAQ,WACZ,EAAO,MAAM;EACjB;CACH,CAAA;AAET;AAkDA,SAAgB,EAAa,EACzB,aACA,SACA,gBACA,cACA,cACA,YACA,YACA,WACA,UACA,YACA,WACA,WACA,aACA,eACkB;CAClB,IAAM,IAAU,EAAiC,IAAI,GAC/C,IAAa,EAAO,EAAK;CAO/B,OALA,QAAgB;EAEZ,AADI,CAAC,KAAW,EAAW,WAAW,KAAS,EAAQ,SAAS,MAAM,GACtE,EAAW,UAAU;CACzB,GAAG,CAAC,GAAS,CAAO,CAAC,GAGjB,kBAAC,QAAD;EAAM,WAAW,EAAO;YAAxB,CACK,IACG,kBAAC,GAAD;GACI,SAAS;GACT,MAAM;GACN,OAAO,EAAO,OAAO,GAAa,CAAS;GAC3C,aAAa,IAAQ,IAAU,KAAA;GAC/B,SAAS,MAAU;GACT;GACA;EACb,CAAA,IAED,kBAAC,UAAD;GACI,KAAK;GACL,MAAK;GACL,WAAW,EAAG,EAAO,YAAY,KAAS,EAAO,iBAAiB;GAClE,oBAAkB,IAAQ,IAAU,KAAA;GACpC,aAAW,KAAU,KAAA;GACrB,SAAS;aANb;IAQI,kBAAC,GAAD,EAAA,UAAiB,EAAO,SAAS,CAAW,EAAkB,CAAA;IAAC;IAAE;GAC7D;MAEX,KACG,kBAAC,QAAD;GAAM,WAAW,EAAO;GAAW,IAAI;GAAS,MAAK;aAChD;EACC,CAAA,CAER;;AAEd"}
@@ -0,0 +1,2 @@
1
+ var e={editCell:e=>`Editar ${e}:`,editor:(e,t)=>`${e}, linha ${t}`,saved:e=>`${e} salvo`,saveFailed:e=>`Não foi possível salvar ${e}.`};exports.DEFAULT_EDIT_LABELS=e;
2
+ //# sourceMappingURL=edit-labels.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-labels.cjs","names":[],"sources":["../../../src/components/DataTable/edit-labels.ts"],"sourcesContent":["/** Where a commit should send focus next. */\nexport type CellCommitMove = \"none\" | \"next\" | \"previous\";\n\n/** Copy for the inline-editing affordances. Every entry has a PT-BR default. */\nexport interface DataTableEditLabels {\n /**\n * Visually hidden prefix that turns a cell's own content into the button's name.\n *\n * A prefix and not a whole label: the accessible name has to *contain* the visible\n * text (WCAG 2.5.3, Label in Name), and a label built from the raw value would say\n * \"850000\" over a cell that reads `R$ 8.500,00` — which breaks voice control and\n * makes the screen reader disagree with the screen.\n *\n * A single space is appended when it is rendered, so the name reads\n * `\"Editar Salário: R$ 8.500,00\"`. Do not add one yourself.\n */\n editCell: (column: string) => string;\n /** Accessible name of the editor input. */\n editor: (column: string, rowNumber: number) => string;\n /** Announced (politely) once a cell saved. */\n saved: (column: string) => string;\n /** Shown in the cell when the save was rejected and no reason was thrown. */\n saveFailed: (column: string) => string;\n}\n\n/**\n * PT-BR defaults, matching the rest of the component copy in the SDK.\n *\n * In their own module rather than next to `EditableCell` so that file exports\n * components and nothing else, which is what keeps Fast Refresh working for\n * anyone editing the component.\n */\nexport const DEFAULT_EDIT_LABELS: DataTableEditLabels = {\n editCell: (column) => `Editar ${column}:`,\n editor: (column, rowNumber) => `${column}, linha ${rowNumber}`,\n saved: (column) => `${column} salvo`,\n saveFailed: (column) => `Não foi possível salvar ${column}.`,\n};\n"],"mappings":"AAgCA,IAAa,EAA2C,CACpD,SAAW,GAAW,UAAU,EAAO,GACvC,QAAS,EAAQ,IAAc,GAAG,EAAO,UAAU,IACnD,MAAQ,GAAW,GAAG,EAAO,QAC7B,WAAa,GAAW,2BAA2B,EAAO,EAC9D"}
@@ -0,0 +1,11 @@
1
+ //#region src/components/DataTable/edit-labels.ts
2
+ var e = {
3
+ editCell: (e) => `Editar ${e}:`,
4
+ editor: (e, t) => `${e}, linha ${t}`,
5
+ saved: (e) => `${e} salvo`,
6
+ saveFailed: (e) => `Não foi possível salvar ${e}.`
7
+ };
8
+ //#endregion
9
+ export { e as DEFAULT_EDIT_LABELS };
10
+
11
+ //# sourceMappingURL=edit-labels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-labels.js","names":[],"sources":["../../../src/components/DataTable/edit-labels.ts"],"sourcesContent":["/** Where a commit should send focus next. */\nexport type CellCommitMove = \"none\" | \"next\" | \"previous\";\n\n/** Copy for the inline-editing affordances. Every entry has a PT-BR default. */\nexport interface DataTableEditLabels {\n /**\n * Visually hidden prefix that turns a cell's own content into the button's name.\n *\n * A prefix and not a whole label: the accessible name has to *contain* the visible\n * text (WCAG 2.5.3, Label in Name), and a label built from the raw value would say\n * \"850000\" over a cell that reads `R$ 8.500,00` — which breaks voice control and\n * makes the screen reader disagree with the screen.\n *\n * A single space is appended when it is rendered, so the name reads\n * `\"Editar Salário: R$ 8.500,00\"`. Do not add one yourself.\n */\n editCell: (column: string) => string;\n /** Accessible name of the editor input. */\n editor: (column: string, rowNumber: number) => string;\n /** Announced (politely) once a cell saved. */\n saved: (column: string) => string;\n /** Shown in the cell when the save was rejected and no reason was thrown. */\n saveFailed: (column: string) => string;\n}\n\n/**\n * PT-BR defaults, matching the rest of the component copy in the SDK.\n *\n * In their own module rather than next to `EditableCell` so that file exports\n * components and nothing else, which is what keeps Fast Refresh working for\n * anyone editing the component.\n */\nexport const DEFAULT_EDIT_LABELS: DataTableEditLabels = {\n editCell: (column) => `Editar ${column}:`,\n editor: (column, rowNumber) => `${column}, linha ${rowNumber}`,\n saved: (column) => `${column} salvo`,\n saveFailed: (column) => `Não foi possível salvar ${column}.`,\n};\n"],"mappings":";AAgCA,IAAa,IAA2C;CACpD,WAAW,MAAW,UAAU,EAAO;CACvC,SAAS,GAAQ,MAAc,GAAG,EAAO,UAAU;CACnD,QAAQ,MAAW,GAAG,EAAO;CAC7B,aAAa,MAAW,2BAA2B,EAAO;AAC9D"}
@@ -1 +1 @@
1
- {"version":3,"file":"DateRangePicker.module.cjs","names":[],"sources":["../../../src/components/DateRangePicker/DateRangePicker.module.css"],"sourcesContent":[".root {\n display: inline-flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\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-surface);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n user-select: none;\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-2);\n}\n\n.headerSpacer {\n flex: 1;\n}\n\n.nav {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n font-size: var(--tempest-text-lg);\n line-height: 1;\n cursor: pointer;\n transition: background-color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.nav:hover {\n background-color: var(--tempest-surface-2);\n}\n\n.nav:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.months {\n display: flex;\n flex-wrap: wrap;\n gap: var(--tempest-space-5);\n}\n\n.month {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\n}\n\n.title {\n text-align: center;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n}\n\n.weekdays,\n.grid {\n display: grid;\n grid-template-columns: repeat(7, 2.25rem);\n gap: var(--tempest-space-1);\n}\n\n.weekday {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 1.5rem;\n font-size: var(--tempest-text-2xs);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text-muted);\n text-transform: uppercase;\n}\n\n.day {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.25rem;\n height: 2.25rem;\n border: 1px solid transparent;\n border-radius: var(--tempest-radius-md);\n background-color: transparent;\n color: var(--tempest-text);\n font-size: var(--tempest-text-sm);\n cursor: pointer;\n transition:\n background-color var(--tempest-duration-fast) var(--tempest-ease-out),\n color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.day:hover:not(:disabled) {\n background-color: var(--tempest-surface-2);\n}\n\n.day:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.day:disabled {\n color: var(--tempest-text-muted);\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.outside {\n /* No extra opacity: subtle-on-surface already reads as de-emphasised, and\n fading it further dropped the contrast to 2.11:1 (WCAG AA needs 4.5:1)\n for dates that are still clickable. */\n color: var(--tempest-text-subtle);\n}\n\n.today {\n font-weight: var(--tempest-weight-semibold);\n border-color: var(--tempest-primary);\n}\n\n.inRange {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-active);\n border-radius: 0;\n}\n\n.selected,\n.selected:hover {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n border-color: var(--tempest-primary);\n}\n\n.rangeStart {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.rangeEnd {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n"],"mappings":""}
1
+ {"version":3,"file":"DateRangePicker.module.cjs","names":[],"sources":["../../../src/components/DateRangePicker/DateRangePicker.module.css"],"sourcesContent":[".root {\n display: inline-flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\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-surface);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n user-select: none;\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-2);\n}\n\n.headerSpacer {\n flex: 1;\n}\n\n.nav {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n font-size: var(--tempest-text-lg);\n line-height: 1;\n cursor: pointer;\n transition: background-color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.nav:hover {\n background-color: var(--tempest-surface-2);\n}\n\n.nav:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.months {\n display: flex;\n flex-wrap: wrap;\n gap: var(--tempest-space-5);\n}\n\n.month {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\n}\n\n.title {\n text-align: center;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n}\n\n.weekdays,\n.grid {\n display: grid;\n grid-template-columns: repeat(7, 2.25rem);\n gap: var(--tempest-space-1);\n}\n\n.weekday {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 1.5rem;\n font-size: var(--tempest-text-2xs);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text-muted);\n text-transform: uppercase;\n}\n\n.day {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.25rem;\n height: 2.25rem;\n border: 1px solid transparent;\n border-radius: var(--tempest-radius-md);\n background-color: transparent;\n color: var(--tempest-text);\n font-size: var(--tempest-text-sm);\n cursor: pointer;\n transition:\n background-color var(--tempest-duration-fast) var(--tempest-ease-out),\n color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.day:hover:not(:disabled) {\n background-color: var(--tempest-surface-2);\n}\n\n.day:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.day:disabled {\n color: var(--tempest-text-muted);\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.outside {\n /* No extra opacity: subtle-on-surface already reads as de-emphasised, and\n fading it further dropped the contrast to 2.11:1 (WCAG AA needs 4.5:1)\n for dates that are still clickable. */\n color: var(--tempest-text-subtle);\n}\n\n.today {\n font-weight: var(--tempest-weight-semibold);\n border-color: var(--tempest-primary);\n}\n\n.inRange {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n border-radius: 0;\n}\n\n.selected,\n.selected:hover {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n border-color: var(--tempest-primary);\n}\n\n.rangeStart {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.rangeEnd {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"DateRangePicker.module.js","names":[],"sources":["../../../src/components/DateRangePicker/DateRangePicker.module.css"],"sourcesContent":[".root {\n display: inline-flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\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-surface);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n user-select: none;\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-2);\n}\n\n.headerSpacer {\n flex: 1;\n}\n\n.nav {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n font-size: var(--tempest-text-lg);\n line-height: 1;\n cursor: pointer;\n transition: background-color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.nav:hover {\n background-color: var(--tempest-surface-2);\n}\n\n.nav:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.months {\n display: flex;\n flex-wrap: wrap;\n gap: var(--tempest-space-5);\n}\n\n.month {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\n}\n\n.title {\n text-align: center;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n}\n\n.weekdays,\n.grid {\n display: grid;\n grid-template-columns: repeat(7, 2.25rem);\n gap: var(--tempest-space-1);\n}\n\n.weekday {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 1.5rem;\n font-size: var(--tempest-text-2xs);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text-muted);\n text-transform: uppercase;\n}\n\n.day {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.25rem;\n height: 2.25rem;\n border: 1px solid transparent;\n border-radius: var(--tempest-radius-md);\n background-color: transparent;\n color: var(--tempest-text);\n font-size: var(--tempest-text-sm);\n cursor: pointer;\n transition:\n background-color var(--tempest-duration-fast) var(--tempest-ease-out),\n color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.day:hover:not(:disabled) {\n background-color: var(--tempest-surface-2);\n}\n\n.day:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.day:disabled {\n color: var(--tempest-text-muted);\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.outside {\n /* No extra opacity: subtle-on-surface already reads as de-emphasised, and\n fading it further dropped the contrast to 2.11:1 (WCAG AA needs 4.5:1)\n for dates that are still clickable. */\n color: var(--tempest-text-subtle);\n}\n\n.today {\n font-weight: var(--tempest-weight-semibold);\n border-color: var(--tempest-primary);\n}\n\n.inRange {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-active);\n border-radius: 0;\n}\n\n.selected,\n.selected:hover {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n border-color: var(--tempest-primary);\n}\n\n.rangeStart {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.rangeEnd {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n"],"mappings":""}
1
+ {"version":3,"file":"DateRangePicker.module.js","names":[],"sources":["../../../src/components/DateRangePicker/DateRangePicker.module.css"],"sourcesContent":[".root {\n display: inline-flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\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-surface);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n user-select: none;\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-2);\n}\n\n.headerSpacer {\n flex: 1;\n}\n\n.nav {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n font-size: var(--tempest-text-lg);\n line-height: 1;\n cursor: pointer;\n transition: background-color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.nav:hover {\n background-color: var(--tempest-surface-2);\n}\n\n.nav:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.months {\n display: flex;\n flex-wrap: wrap;\n gap: var(--tempest-space-5);\n}\n\n.month {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\n}\n\n.title {\n text-align: center;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n}\n\n.weekdays,\n.grid {\n display: grid;\n grid-template-columns: repeat(7, 2.25rem);\n gap: var(--tempest-space-1);\n}\n\n.weekday {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 1.5rem;\n font-size: var(--tempest-text-2xs);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text-muted);\n text-transform: uppercase;\n}\n\n.day {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.25rem;\n height: 2.25rem;\n border: 1px solid transparent;\n border-radius: var(--tempest-radius-md);\n background-color: transparent;\n color: var(--tempest-text);\n font-size: var(--tempest-text-sm);\n cursor: pointer;\n transition:\n background-color var(--tempest-duration-fast) var(--tempest-ease-out),\n color var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.day:hover:not(:disabled) {\n background-color: var(--tempest-surface-2);\n}\n\n.day:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n.day:disabled {\n color: var(--tempest-text-muted);\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.outside {\n /* No extra opacity: subtle-on-surface already reads as de-emphasised, and\n fading it further dropped the contrast to 2.11:1 (WCAG AA needs 4.5:1)\n for dates that are still clickable. */\n color: var(--tempest-text-subtle);\n}\n\n.today {\n font-weight: var(--tempest-weight-semibold);\n border-color: var(--tempest-primary);\n}\n\n.inRange {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n border-radius: 0;\n}\n\n.selected,\n.selected:hover {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n border-color: var(--tempest-primary);\n}\n\n.rangeStart {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.rangeEnd {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"MultiSelect.module.cjs","names":[],"sources":["../../../src/components/MultiSelect/MultiSelect.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n width: 100%;\n font-family: var(--tempest-font-sans);\n position: relative;\n}\n\n.label {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n line-height: var(--tempest-leading-snug);\n}\n\n.field {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-1);\n min-height: var(--tempest-control-height-md);\n padding: 4px var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-control-radius);\n background-color: var(--tempest-bg);\n box-shadow: var(--tempest-shadow-xs);\n cursor: text;\n transition: var(--tempest-transition-color), var(--tempest-transition-shadow);\n}\n\n.field:focus-within {\n border-color: var(--tempest-primary);\n box-shadow: 0 0 0 var(--tempest-focus-ring-width) var(--tempest-focus-ring-color);\n}\n\n.field.disabled {\n background-color: var(--tempest-surface);\n cursor: not-allowed;\n}\n\n.chip {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 4px 2px 8px;\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-active);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-medium);\n line-height: 1.4;\n}\n\n.chipRemove {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: inherit;\n font-size: 14px;\n line-height: 1;\n cursor: pointer;\n}\n\n.chipRemove:hover {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.input {\n flex: 1;\n min-width: 80px;\n border: none;\n outline: none;\n background: transparent;\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-control-font-md);\n height: 24px;\n}\n\n.input::placeholder {\n color: var(--tempest-text-subtle);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\n z-index: var(--tempest-z-dropdown);\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n max-height: 280px;\n overflow-y: auto;\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n}\n\n.option {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border-radius: var(--tempest-radius-sm);\n cursor: pointer;\n color: var(--tempest-text);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n}\n\n.check {\n display: inline-flex;\n width: 16px;\n justify-content: center;\n color: var(--tempest-primary);\n font-weight: var(--tempest-weight-bold);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .option:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.option.active {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-active);\n}\n\n.option.selected {\n font-weight: var(--tempest-weight-semibold);\n}\n\n.empty {\n padding: var(--tempest-space-3);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n text-align: center;\n}\n\n.helper {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-text-muted);\n}\n\n.errorText {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-danger);\n}\n\n.wrapper.error .field {\n border-color: var(--tempest-danger);\n}\n"],"mappings":""}
1
+ {"version":3,"file":"MultiSelect.module.cjs","names":[],"sources":["../../../src/components/MultiSelect/MultiSelect.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n width: 100%;\n font-family: var(--tempest-font-sans);\n position: relative;\n}\n\n.label {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n line-height: var(--tempest-leading-snug);\n}\n\n.field {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-1);\n min-height: var(--tempest-control-height-md);\n padding: 4px var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-control-radius);\n background-color: var(--tempest-bg);\n box-shadow: var(--tempest-shadow-xs);\n cursor: text;\n transition: var(--tempest-transition-color), var(--tempest-transition-shadow);\n}\n\n.field:focus-within {\n border-color: var(--tempest-primary);\n box-shadow: 0 0 0 var(--tempest-focus-ring-width) var(--tempest-focus-ring-color);\n}\n\n.field.disabled {\n background-color: var(--tempest-surface);\n cursor: not-allowed;\n}\n\n.chip {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 4px 2px 8px;\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-medium);\n line-height: 1.4;\n}\n\n.chipRemove {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: inherit;\n font-size: 14px;\n line-height: 1;\n cursor: pointer;\n}\n\n.chipRemove:hover {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.input {\n flex: 1;\n min-width: 80px;\n border: none;\n outline: none;\n background: transparent;\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-control-font-md);\n height: 24px;\n}\n\n.input::placeholder {\n color: var(--tempest-text-subtle);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\n z-index: var(--tempest-z-dropdown);\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n max-height: 280px;\n overflow-y: auto;\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n}\n\n.option {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border-radius: var(--tempest-radius-sm);\n cursor: pointer;\n color: var(--tempest-text);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n}\n\n.check {\n display: inline-flex;\n width: 16px;\n justify-content: center;\n color: var(--tempest-primary);\n font-weight: var(--tempest-weight-bold);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .option:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.option.active {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n.option.selected {\n font-weight: var(--tempest-weight-semibold);\n}\n\n.empty {\n padding: var(--tempest-space-3);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n text-align: center;\n}\n\n.helper {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-text-muted);\n}\n\n.errorText {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-danger);\n}\n\n.wrapper.error .field {\n border-color: var(--tempest-danger);\n}\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"MultiSelect.module.js","names":[],"sources":["../../../src/components/MultiSelect/MultiSelect.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n width: 100%;\n font-family: var(--tempest-font-sans);\n position: relative;\n}\n\n.label {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n line-height: var(--tempest-leading-snug);\n}\n\n.field {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-1);\n min-height: var(--tempest-control-height-md);\n padding: 4px var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-control-radius);\n background-color: var(--tempest-bg);\n box-shadow: var(--tempest-shadow-xs);\n cursor: text;\n transition: var(--tempest-transition-color), var(--tempest-transition-shadow);\n}\n\n.field:focus-within {\n border-color: var(--tempest-primary);\n box-shadow: 0 0 0 var(--tempest-focus-ring-width) var(--tempest-focus-ring-color);\n}\n\n.field.disabled {\n background-color: var(--tempest-surface);\n cursor: not-allowed;\n}\n\n.chip {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 4px 2px 8px;\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-active);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-medium);\n line-height: 1.4;\n}\n\n.chipRemove {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: inherit;\n font-size: 14px;\n line-height: 1;\n cursor: pointer;\n}\n\n.chipRemove:hover {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.input {\n flex: 1;\n min-width: 80px;\n border: none;\n outline: none;\n background: transparent;\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-control-font-md);\n height: 24px;\n}\n\n.input::placeholder {\n color: var(--tempest-text-subtle);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\n z-index: var(--tempest-z-dropdown);\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n max-height: 280px;\n overflow-y: auto;\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n}\n\n.option {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border-radius: var(--tempest-radius-sm);\n cursor: pointer;\n color: var(--tempest-text);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n}\n\n.check {\n display: inline-flex;\n width: 16px;\n justify-content: center;\n color: var(--tempest-primary);\n font-weight: var(--tempest-weight-bold);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .option:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.option.active {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-active);\n}\n\n.option.selected {\n font-weight: var(--tempest-weight-semibold);\n}\n\n.empty {\n padding: var(--tempest-space-3);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n text-align: center;\n}\n\n.helper {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-text-muted);\n}\n\n.errorText {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-danger);\n}\n\n.wrapper.error .field {\n border-color: var(--tempest-danger);\n}\n"],"mappings":""}
1
+ {"version":3,"file":"MultiSelect.module.js","names":[],"sources":["../../../src/components/MultiSelect/MultiSelect.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n width: 100%;\n font-family: var(--tempest-font-sans);\n position: relative;\n}\n\n.label {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n line-height: var(--tempest-leading-snug);\n}\n\n.field {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-1);\n min-height: var(--tempest-control-height-md);\n padding: 4px var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-control-radius);\n background-color: var(--tempest-bg);\n box-shadow: var(--tempest-shadow-xs);\n cursor: text;\n transition: var(--tempest-transition-color), var(--tempest-transition-shadow);\n}\n\n.field:focus-within {\n border-color: var(--tempest-primary);\n box-shadow: 0 0 0 var(--tempest-focus-ring-width) var(--tempest-focus-ring-color);\n}\n\n.field.disabled {\n background-color: var(--tempest-surface);\n cursor: not-allowed;\n}\n\n.chip {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 4px 2px 8px;\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-medium);\n line-height: 1.4;\n}\n\n.chipRemove {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: inherit;\n font-size: 14px;\n line-height: 1;\n cursor: pointer;\n}\n\n.chipRemove:hover {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.input {\n flex: 1;\n min-width: 80px;\n border: none;\n outline: none;\n background: transparent;\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-control-font-md);\n height: 24px;\n}\n\n.input::placeholder {\n color: var(--tempest-text-subtle);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\n z-index: var(--tempest-z-dropdown);\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n max-height: 280px;\n overflow-y: auto;\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n}\n\n.option {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border-radius: var(--tempest-radius-sm);\n cursor: pointer;\n color: var(--tempest-text);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n}\n\n.check {\n display: inline-flex;\n width: 16px;\n justify-content: center;\n color: var(--tempest-primary);\n font-weight: var(--tempest-weight-bold);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .option:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.option.active {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n.option.selected {\n font-weight: var(--tempest-weight-semibold);\n}\n\n.empty {\n padding: var(--tempest-space-3);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n text-align: center;\n}\n\n.helper {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-text-muted);\n}\n\n.errorText {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-danger);\n}\n\n.wrapper.error .field {\n border-color: var(--tempest-danger);\n}\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationRail.module.cjs","names":[],"sources":["../../../src/components/NavigationRail/NavigationRail.module.css"],"sourcesContent":[".rail {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 80px;\n height: 100%;\n padding: var(--tempest-space-3) var(--tempest-space-2);\n background: var(--tempest-surface);\n border-right: 1px solid var(--tempest-border);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-bottom: var(--tempest-space-2);\n}\n\n.list {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: var(--tempest-space-1);\n width: 100%;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.footer {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-top: auto;\n padding-top: var(--tempest-space-2);\n}\n\n.item {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-1);\n background: none;\n border: 0;\n border-radius: var(--tempest-radius-md);\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition:\n color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.item:hover {\n color: var(--tempest-text);\n background: var(--tempest-surface-2);\n}\n\n.item:focus-visible {\n outline: var(--tempest-focus-ring-width, 2px) solid\n var(--tempest-focus-ring-color, var(--tempest-primary));\n outline-offset: var(--tempest-focus-ring-offset, 2px);\n}\n\n.item.active {\n color: var(--tempest-primary-on-soft);\n background: var(--tempest-primary-soft);\n}\n\n.item:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.iconWrap {\n position: relative;\n display: inline-flex;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n font-size: 24px;\n}\n\n.badge {\n position: absolute;\n top: -4px;\n right: -8px;\n min-width: 14px;\n height: 14px;\n padding: 0 4px;\n border-radius: var(--tempest-radius-full);\n background: var(--tempest-danger-solid);\n color: var(--tempest-danger-on, #fff);\n font-size: 10px;\n line-height: 14px;\n text-align: center;\n font-weight: var(--tempest-weight-semibold);\n}\n\n.label {\n line-height: 1;\n text-align: center;\n}\n"],"mappings":""}
1
+ {"version":3,"file":"NavigationRail.module.cjs","names":[],"sources":["../../../src/components/NavigationRail/NavigationRail.module.css"],"sourcesContent":[".rail {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 80px;\n height: 100%;\n padding: var(--tempest-space-3) var(--tempest-space-2);\n background: var(--tempest-surface);\n border-right: 1px solid var(--tempest-border);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-bottom: var(--tempest-space-2);\n}\n\n.list {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: var(--tempest-space-1);\n width: 100%;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.footer {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-top: auto;\n padding-top: var(--tempest-space-2);\n}\n\n.item {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-1);\n background: none;\n border: 0;\n border-radius: var(--tempest-radius-md);\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition:\n color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.item:hover {\n color: var(--tempest-text);\n background: var(--tempest-surface-2);\n}\n\n.item:focus-visible {\n outline: var(--tempest-focus-ring-width, 2px) solid\n var(--tempest-focus-ring-color, var(--tempest-primary));\n outline-offset: var(--tempest-focus-ring-offset, 2px);\n}\n\n.item.active {\n color: var(--tempest-primary-on-soft);\n background: var(--tempest-primary-soft);\n}\n\n.item:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.iconWrap {\n position: relative;\n display: inline-flex;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n font-size: 24px;\n}\n\n.badge {\n position: absolute;\n top: -4px;\n right: -8px;\n min-width: 14px;\n height: 14px;\n padding: 0 4px;\n border-radius: var(--tempest-radius-full);\n background: var(--tempest-danger-solid);\n color: var(--tempest-danger-on-solid);\n font-size: 10px;\n line-height: 14px;\n text-align: center;\n font-weight: var(--tempest-weight-semibold);\n}\n\n.label {\n line-height: 1;\n text-align: center;\n}\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationRail.module.js","names":[],"sources":["../../../src/components/NavigationRail/NavigationRail.module.css"],"sourcesContent":[".rail {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 80px;\n height: 100%;\n padding: var(--tempest-space-3) var(--tempest-space-2);\n background: var(--tempest-surface);\n border-right: 1px solid var(--tempest-border);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-bottom: var(--tempest-space-2);\n}\n\n.list {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: var(--tempest-space-1);\n width: 100%;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.footer {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-top: auto;\n padding-top: var(--tempest-space-2);\n}\n\n.item {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-1);\n background: none;\n border: 0;\n border-radius: var(--tempest-radius-md);\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition:\n color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.item:hover {\n color: var(--tempest-text);\n background: var(--tempest-surface-2);\n}\n\n.item:focus-visible {\n outline: var(--tempest-focus-ring-width, 2px) solid\n var(--tempest-focus-ring-color, var(--tempest-primary));\n outline-offset: var(--tempest-focus-ring-offset, 2px);\n}\n\n.item.active {\n color: var(--tempest-primary-on-soft);\n background: var(--tempest-primary-soft);\n}\n\n.item:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.iconWrap {\n position: relative;\n display: inline-flex;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n font-size: 24px;\n}\n\n.badge {\n position: absolute;\n top: -4px;\n right: -8px;\n min-width: 14px;\n height: 14px;\n padding: 0 4px;\n border-radius: var(--tempest-radius-full);\n background: var(--tempest-danger-solid);\n color: var(--tempest-danger-on, #fff);\n font-size: 10px;\n line-height: 14px;\n text-align: center;\n font-weight: var(--tempest-weight-semibold);\n}\n\n.label {\n line-height: 1;\n text-align: center;\n}\n"],"mappings":""}
1
+ {"version":3,"file":"NavigationRail.module.js","names":[],"sources":["../../../src/components/NavigationRail/NavigationRail.module.css"],"sourcesContent":[".rail {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 80px;\n height: 100%;\n padding: var(--tempest-space-3) var(--tempest-space-2);\n background: var(--tempest-surface);\n border-right: 1px solid var(--tempest-border);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-bottom: var(--tempest-space-2);\n}\n\n.list {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: var(--tempest-space-1);\n width: 100%;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.footer {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-top: auto;\n padding-top: var(--tempest-space-2);\n}\n\n.item {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-1);\n background: none;\n border: 0;\n border-radius: var(--tempest-radius-md);\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition:\n color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out),\n background var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n}\n\n.item:hover {\n color: var(--tempest-text);\n background: var(--tempest-surface-2);\n}\n\n.item:focus-visible {\n outline: var(--tempest-focus-ring-width, 2px) solid\n var(--tempest-focus-ring-color, var(--tempest-primary));\n outline-offset: var(--tempest-focus-ring-offset, 2px);\n}\n\n.item.active {\n color: var(--tempest-primary-on-soft);\n background: var(--tempest-primary-soft);\n}\n\n.item:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.iconWrap {\n position: relative;\n display: inline-flex;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n font-size: 24px;\n}\n\n.badge {\n position: absolute;\n top: -4px;\n right: -8px;\n min-width: 14px;\n height: 14px;\n padding: 0 4px;\n border-radius: var(--tempest-radius-full);\n background: var(--tempest-danger-solid);\n color: var(--tempest-danger-on-solid);\n font-size: 10px;\n line-height: 14px;\n text-align: center;\n font-weight: var(--tempest-weight-semibold);\n}\n\n.label {\n line-height: 1;\n text-align: center;\n}\n"],"mappings":""}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("../../offline/use-offline-sync.cjs"),n=require("./SyncStatusBadge.module.cjs");let r=require("react/jsx-runtime"),i=require("lucide-react");var a={idle:`Sincronizado`,syncing:`Sincronizando…`,pending:`Pendente`,offline:`Offline`,error:`Erro`},o={idle:i.Check,syncing:i.RefreshCw,pending:i.UploadCloud,offline:i.CloudOff,error:i.AlertTriangle};function s({tone:t=`idle`,pending:i=0,labels:s,showPending:c=!0,iconOnly:l=!1,className:u,...d}){let f=o[t],p=s?.[t]??a[t],m=c&&i>0&&(t===`pending`||t===`error`);return(0,r.jsxs)(`span`,{className:e.cn(n.default.badge,n.default[t],u),role:`status`,"aria-live":`polite`,title:p,...d,children:[(0,r.jsx)(f,{className:e.cn(n.default.icon,t===`syncing`&&n.default.spin),size:14,"aria-hidden":`true`}),!l&&(0,r.jsx)(`span`,{className:n.default.label,children:p}),m&&(0,r.jsx)(`span`,{className:n.default.count,children:i})]})}function c({sync:e,...n}){let{tone:i,pending:a}=t.useSyncStatus(e);return(0,r.jsx)(s,{...n,tone:i,pending:a})}function l({sync:e,...t}){return e?(0,r.jsx)(c,{sync:e,...t}):(0,r.jsx)(s,{...t})}exports.SyncStatusBadge=l;
1
+ const e=require("../../utils/cn.cjs"),t=require("../../offline/use-offline-sync.cjs"),n=require("../VisuallyHidden/VisuallyHidden.cjs"),r=require("./SyncStatusBadge.module.cjs");let i=require("react/jsx-runtime"),a=require("lucide-react");var o={idle:`Sincronizado`,syncing:`Sincronizando…`,pending:`Pendente`,offline:`Offline`,error:`Erro`},s={idle:a.Check,syncing:a.RefreshCw,pending:a.UploadCloud,offline:a.CloudOff,error:a.AlertTriangle};function c({tone:t=`idle`,pending:a=0,labels:c,showPending:l=!0,iconOnly:u=!1,className:d,...f}){let p=s[t],m=c?.[t]??o[t],h=l&&a>0&&(t===`pending`||t===`error`);return(0,i.jsxs)(`span`,{className:e.cn(r.default.badge,r.default[t],d),role:`status`,"aria-live":`polite`,title:m,...f,children:[(0,i.jsx)(p,{className:e.cn(r.default.icon,t===`syncing`&&r.default.spin),size:14,"aria-hidden":`true`}),u?(0,i.jsx)(n.VisuallyHidden,{children:m}):(0,i.jsx)(`span`,{className:r.default.label,children:m}),h&&(0,i.jsx)(`span`,{className:r.default.count,children:a})]})}function l({sync:e,...n}){let{tone:r,pending:a}=t.useSyncStatus(e);return(0,i.jsx)(c,{...n,tone:r,pending:a})}function u({sync:e,...t}){return e?(0,i.jsx)(l,{sync:e,...t}):(0,i.jsx)(c,{...t})}exports.SyncStatusBadge=u;
2
2
  //# sourceMappingURL=SyncStatusBadge.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"SyncStatusBadge.cjs","names":[],"sources":["../../../src/components/SyncStatusBadge/SyncStatusBadge.tsx"],"sourcesContent":["import type { HTMLAttributes } from \"react\";\nimport { AlertTriangle, Check, CloudOff, RefreshCw, UploadCloud } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport type { OfflineSync, SyncTone } from \"@/offline\";\nimport { useSyncStatus } from \"@/offline/use-offline-sync\";\nimport styles from \"./SyncStatusBadge.module.css\";\n\nconst DEFAULT_LABELS: Record<SyncTone, string> = {\n idle: \"Sincronizado\",\n syncing: \"Sincronizando…\",\n pending: \"Pendente\",\n offline: \"Offline\",\n error: \"Erro\",\n};\n\nconst ICONS: Record<SyncTone, typeof Check> = {\n idle: Check,\n syncing: RefreshCw,\n pending: UploadCloud,\n offline: CloudOff,\n error: AlertTriangle,\n};\n\nexport interface SyncStatusBadgeProps extends Omit<HTMLAttributes<HTMLSpanElement>, \"children\"> {\n /**\n * The dominant tone. Required when `sync` is not passed. Ignored when `sync`\n * is passed (the tone is derived from the engine).\n */\n tone?: SyncTone;\n /**\n * An `OfflineSync` engine to observe directly. When set, the badge wires\n * `useSyncStatus` internally, so `tone` and `pending` come from the engine.\n */\n sync?: OfflineSync<unknown>;\n /** Number of queued mutations, appended to the label when `> 0`. */\n pending?: number;\n /** Override the per-tone label text. */\n labels?: Partial<Record<SyncTone, string>>;\n /** Show the pending count next to the label. Default `true`. */\n showPending?: boolean;\n /** Render only the icon (no text). Default `false`. */\n iconOnly?: boolean;\n}\n\ntype PresentationalProps = Omit<SyncStatusBadgeProps, \"sync\">;\n\nfunction PresentationalBadge({\n tone = \"idle\",\n pending = 0,\n labels,\n showPending = true,\n iconOnly = false,\n className,\n ...props\n}: PresentationalProps) {\n const Icon = ICONS[tone];\n const label = labels?.[tone] ?? DEFAULT_LABELS[tone];\n const showCount = showPending && pending > 0 && (tone === \"pending\" || tone === \"error\");\n return (\n <span\n className={cn(styles.badge, styles[tone], className)}\n role=\"status\"\n aria-live=\"polite\"\n title={label}\n {...props}\n >\n <Icon\n className={cn(styles.icon, tone === \"syncing\" && styles.spin)}\n size={14}\n aria-hidden=\"true\"\n />\n {!iconOnly && <span className={styles.label}>{label}</span>}\n {showCount && <span className={styles.count}>{pending}</span>}\n </span>\n );\n}\n\nfunction ConnectedBadge({ sync, ...rest }: PresentationalProps & { sync: OfflineSync<unknown> }) {\n const { tone, pending } = useSyncStatus(sync);\n return <PresentationalBadge {...rest} tone={tone} pending={pending} />;\n}\n\n/**\n * Compact pill showing offline-sync status: a tone-colored icon plus a label\n * and optional pending count.\n *\n * Two modes: pass `sync` to have the badge observe an engine via\n * `useSyncStatus` (zero wiring), or pass an explicit `tone` (+ `pending`) to\n * keep it presentational and testable without IndexedDB.\n *\n * @example\n * // Connected — auto-wires the engine:\n * <SyncStatusBadge sync={notesSync} />\n *\n * // Presentational — you own the state:\n * const { tone, pending } = useSyncStatus(notesSync);\n * <SyncStatusBadge tone={tone} pending={pending} />\n */\nexport function SyncStatusBadge({ sync, ...rest }: SyncStatusBadgeProps) {\n if (sync) return <ConnectedBadge sync={sync} {...rest} />;\n return <PresentationalBadge {...rest} />;\n}\n"],"mappings":"6LAOA,IAAM,EAA2C,CAC7C,KAAM,eACN,QAAS,iBACT,QAAS,WACT,QAAS,UACT,MAAO,MACX,EAEM,EAAwC,CAC1C,KAAM,EAAA,MACN,QAAS,EAAA,UACT,QAAS,EAAA,YACT,QAAS,EAAA,SACT,MAAO,EAAA,aACX,EAyBA,SAAS,EAAoB,CACzB,OAAO,OACP,UAAU,EACV,SACA,cAAc,GACd,WAAW,GACX,YACA,GAAG,GACiB,CACpB,IAAM,EAAO,EAAM,GACb,EAAQ,IAAS,IAAS,EAAe,GACzC,EAAY,GAAe,EAAU,IAAM,IAAS,WAAa,IAAS,SAChF,OACI,EAAA,EAAA,KAAA,CAAC,OAAD,CACI,UAAW,EAAA,GAAG,EAAA,QAAO,MAAO,EAAA,QAAO,GAAO,CAAS,EACnD,KAAK,SACL,YAAU,SACV,MAAO,EACP,GAAI,WALR,EAOI,EAAA,EAAA,IAAA,CAAC,EAAD,CACI,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,IAAS,WAAa,EAAA,QAAO,IAAI,EAC5D,KAAM,GACN,cAAY,MACf,CAAA,EACA,CAAC,IAAY,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,eAAQ,CAAY,CAAA,EACzD,IAAa,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,eAAQ,CAAc,CAAA,CAC1D,GAEd,CAEA,SAAS,EAAe,CAAE,OAAM,GAAG,GAA8D,CAC7F,GAAM,CAAE,OAAM,WAAY,EAAA,cAAc,CAAI,EAC5C,OAAO,EAAA,EAAA,IAAA,CAAC,EAAD,CAAqB,GAAI,EAAY,OAAe,SAAU,CAAA,CACzE,CAkBA,SAAgB,EAAgB,CAAE,OAAM,GAAG,GAA8B,CAErE,OADI,GAAa,EAAA,EAAA,IAAA,CAAC,EAAD,CAAsB,OAAM,GAAI,CAAO,CAAA,GACjD,EAAA,EAAA,IAAA,CAAC,EAAD,CAAqB,GAAI,CAAO,CAAA,CAC3C"}
1
+ {"version":3,"file":"SyncStatusBadge.cjs","names":[],"sources":["../../../src/components/SyncStatusBadge/SyncStatusBadge.tsx"],"sourcesContent":["import type { HTMLAttributes } from \"react\";\nimport { AlertTriangle, Check, CloudOff, RefreshCw, UploadCloud } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport type { OfflineSync, SyncTone } from \"@/offline\";\nimport { useSyncStatus } from \"@/offline/use-offline-sync\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport styles from \"./SyncStatusBadge.module.css\";\n\nconst DEFAULT_LABELS: Record<SyncTone, string> = {\n idle: \"Sincronizado\",\n syncing: \"Sincronizando…\",\n pending: \"Pendente\",\n offline: \"Offline\",\n error: \"Erro\",\n};\n\nconst ICONS: Record<SyncTone, typeof Check> = {\n idle: Check,\n syncing: RefreshCw,\n pending: UploadCloud,\n offline: CloudOff,\n error: AlertTriangle,\n};\n\nexport interface SyncStatusBadgeProps extends Omit<HTMLAttributes<HTMLSpanElement>, \"children\"> {\n /**\n * The dominant tone. Required when `sync` is not passed. Ignored when `sync`\n * is passed (the tone is derived from the engine).\n */\n tone?: SyncTone;\n /**\n * An `OfflineSync` engine to observe directly. When set, the badge wires\n * `useSyncStatus` internally, so `tone` and `pending` come from the engine.\n */\n sync?: OfflineSync<unknown>;\n /** Number of queued mutations, appended to the label when `> 0`. */\n pending?: number;\n /** Override the per-tone label text. */\n labels?: Partial<Record<SyncTone, string>>;\n /** Show the pending count next to the label. Default `true`. */\n showPending?: boolean;\n /** Render only the icon (no text). Default `false`. */\n iconOnly?: boolean;\n}\n\ntype PresentationalProps = Omit<SyncStatusBadgeProps, \"sync\">;\n\n/**\n * The badge itself: a persistent `role=\"status\"` region.\n *\n * Left as its own live region rather than routed through `useAnnounce`, on purpose.\n * The badge is not a transient message — it is a piece of state that stays on\n * screen, which is exactly what `role=\"status\"` describes, and a reader can go back\n * and re-read it. Announcing it *again* from the shared announcer would read every\n * tone change twice.\n *\n * `iconOnly` used to break that, though: dropping the label left a live region whose\n * content never changed, so a switch to `offline` or `error` was announced as\n * nothing at all. The label is now always rendered — visually hidden when\n * `iconOnly`, since `title` is not content and is not announced on a change.\n */\nfunction PresentationalBadge({\n tone = \"idle\",\n pending = 0,\n labels,\n showPending = true,\n iconOnly = false,\n className,\n ...props\n}: PresentationalProps) {\n const Icon = ICONS[tone];\n const label = labels?.[tone] ?? DEFAULT_LABELS[tone];\n const showCount = showPending && pending > 0 && (tone === \"pending\" || tone === \"error\");\n return (\n <span\n className={cn(styles.badge, styles[tone], className)}\n role=\"status\"\n aria-live=\"polite\"\n title={label}\n {...props}\n >\n <Icon\n className={cn(styles.icon, tone === \"syncing\" && styles.spin)}\n size={14}\n aria-hidden=\"true\"\n />\n {iconOnly ? (\n <VisuallyHidden>{label}</VisuallyHidden>\n ) : (\n <span className={styles.label}>{label}</span>\n )}\n {showCount && <span className={styles.count}>{pending}</span>}\n </span>\n );\n}\n\nfunction ConnectedBadge({ sync, ...rest }: PresentationalProps & { sync: OfflineSync<unknown> }) {\n const { tone, pending } = useSyncStatus(sync);\n return <PresentationalBadge {...rest} tone={tone} pending={pending} />;\n}\n\n/**\n * Compact pill showing offline-sync status: a tone-colored icon plus a label\n * and optional pending count.\n *\n * Two modes: pass `sync` to have the badge observe an engine via\n * `useSyncStatus` (zero wiring), or pass an explicit `tone` (+ `pending`) to\n * keep it presentational and testable without IndexedDB.\n *\n * @example\n * // Connected — auto-wires the engine:\n * <SyncStatusBadge sync={notesSync} />\n *\n * // Presentational — you own the state:\n * const { tone, pending } = useSyncStatus(notesSync);\n * <SyncStatusBadge tone={tone} pending={pending} />\n */\nexport function SyncStatusBadge({ sync, ...rest }: SyncStatusBadgeProps) {\n if (sync) return <ConnectedBadge sync={sync} {...rest} />;\n return <PresentationalBadge {...rest} />;\n}\n"],"mappings":"+OAQA,IAAM,EAA2C,CAC7C,KAAM,eACN,QAAS,iBACT,QAAS,WACT,QAAS,UACT,MAAO,MACX,EAEM,EAAwC,CAC1C,KAAM,EAAA,MACN,QAAS,EAAA,UACT,QAAS,EAAA,YACT,QAAS,EAAA,SACT,MAAO,EAAA,aACX,EAuCA,SAAS,EAAoB,CACzB,OAAO,OACP,UAAU,EACV,SACA,cAAc,GACd,WAAW,GACX,YACA,GAAG,GACiB,CACpB,IAAM,EAAO,EAAM,GACb,EAAQ,IAAS,IAAS,EAAe,GACzC,EAAY,GAAe,EAAU,IAAM,IAAS,WAAa,IAAS,SAChF,OACI,EAAA,EAAA,KAAA,CAAC,OAAD,CACI,UAAW,EAAA,GAAG,EAAA,QAAO,MAAO,EAAA,QAAO,GAAO,CAAS,EACnD,KAAK,SACL,YAAU,SACV,MAAO,EACP,GAAI,WALR,EAOI,EAAA,EAAA,IAAA,CAAC,EAAD,CACI,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,IAAS,WAAa,EAAA,QAAO,IAAI,EAC5D,KAAM,GACN,cAAY,MACf,CAAA,EACA,GACG,EAAA,EAAA,IAAA,CAAC,EAAA,eAAD,CAAA,SAAiB,CAAsB,CAAA,GAEvC,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,eAAQ,CAAY,CAAA,EAE/C,IAAa,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,eAAQ,CAAc,CAAA,CAC1D,GAEd,CAEA,SAAS,EAAe,CAAE,OAAM,GAAG,GAA8D,CAC7F,GAAM,CAAE,OAAM,WAAY,EAAA,cAAc,CAAI,EAC5C,OAAO,EAAA,EAAA,IAAA,CAAC,EAAD,CAAqB,GAAI,EAAY,OAAe,SAAU,CAAA,CACzE,CAkBA,SAAgB,EAAgB,CAAE,OAAM,GAAG,GAA8B,CAErE,OADI,GAAa,EAAA,EAAA,IAAA,CAAC,EAAD,CAAsB,OAAM,GAAI,CAAO,CAAA,GACjD,EAAA,EAAA,IAAA,CAAC,EAAD,CAAqB,GAAI,CAAO,CAAA,CAC3C"}
@@ -1,62 +1,63 @@
1
1
  import { cn as e } from "../../utils/cn.js";
2
2
  import { useSyncStatus as t } from "../../offline/use-offline-sync.js";
3
- import n from "./SyncStatusBadge.module.js";
4
- import { jsx as r, jsxs as i } from "react/jsx-runtime";
5
- import { AlertTriangle as a, Check as o, CloudOff as s, RefreshCw as c, UploadCloud as l } from "lucide-react";
3
+ import { VisuallyHidden as n } from "../VisuallyHidden/VisuallyHidden.js";
4
+ import r from "./SyncStatusBadge.module.js";
5
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
6
+ import { AlertTriangle as o, Check as s, CloudOff as c, RefreshCw as l, UploadCloud as u } from "lucide-react";
6
7
  //#region src/components/SyncStatusBadge/SyncStatusBadge.tsx
7
- var u = {
8
+ var d = {
8
9
  idle: "Sincronizado",
9
10
  syncing: "Sincronizando…",
10
11
  pending: "Pendente",
11
12
  offline: "Offline",
12
13
  error: "Erro"
13
- }, d = {
14
- idle: o,
15
- syncing: c,
16
- pending: l,
17
- offline: s,
18
- error: a
14
+ }, f = {
15
+ idle: s,
16
+ syncing: l,
17
+ pending: u,
18
+ offline: c,
19
+ error: o
19
20
  };
20
- function f({ tone: t = "idle", pending: a = 0, labels: o, showPending: s = !0, iconOnly: c = !1, className: l, ...f }) {
21
- let p = d[t], m = o?.[t] ?? u[t], h = s && a > 0 && (t === "pending" || t === "error");
22
- return /* @__PURE__ */ i("span", {
23
- className: e(n.badge, n[t], l),
21
+ function p({ tone: t = "idle", pending: o = 0, labels: s, showPending: c = !0, iconOnly: l = !1, className: u, ...p }) {
22
+ let m = f[t], h = s?.[t] ?? d[t], g = c && o > 0 && (t === "pending" || t === "error");
23
+ return /* @__PURE__ */ a("span", {
24
+ className: e(r.badge, r[t], u),
24
25
  role: "status",
25
26
  "aria-live": "polite",
26
- title: m,
27
- ...f,
27
+ title: h,
28
+ ...p,
28
29
  children: [
29
- /* @__PURE__ */ r(p, {
30
- className: e(n.icon, t === "syncing" && n.spin),
30
+ /* @__PURE__ */ i(m, {
31
+ className: e(r.icon, t === "syncing" && r.spin),
31
32
  size: 14,
32
33
  "aria-hidden": "true"
33
34
  }),
34
- !c && /* @__PURE__ */ r("span", {
35
- className: n.label,
36
- children: m
35
+ l ? /* @__PURE__ */ i(n, { children: h }) : /* @__PURE__ */ i("span", {
36
+ className: r.label,
37
+ children: h
37
38
  }),
38
- h && /* @__PURE__ */ r("span", {
39
- className: n.count,
40
- children: a
39
+ g && /* @__PURE__ */ i("span", {
40
+ className: r.count,
41
+ children: o
41
42
  })
42
43
  ]
43
44
  });
44
45
  }
45
- function p({ sync: e, ...n }) {
46
- let { tone: i, pending: a } = t(e);
47
- return /* @__PURE__ */ r(f, {
46
+ function m({ sync: e, ...n }) {
47
+ let { tone: r, pending: a } = t(e);
48
+ return /* @__PURE__ */ i(p, {
48
49
  ...n,
49
- tone: i,
50
+ tone: r,
50
51
  pending: a
51
52
  });
52
53
  }
53
- function m({ sync: e, ...t }) {
54
- return e ? /* @__PURE__ */ r(p, {
54
+ function h({ sync: e, ...t }) {
55
+ return e ? /* @__PURE__ */ i(m, {
55
56
  sync: e,
56
57
  ...t
57
- }) : /* @__PURE__ */ r(f, { ...t });
58
+ }) : /* @__PURE__ */ i(p, { ...t });
58
59
  }
59
60
  //#endregion
60
- export { m as SyncStatusBadge };
61
+ export { h as SyncStatusBadge };
61
62
 
62
63
  //# sourceMappingURL=SyncStatusBadge.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SyncStatusBadge.js","names":[],"sources":["../../../src/components/SyncStatusBadge/SyncStatusBadge.tsx"],"sourcesContent":["import type { HTMLAttributes } from \"react\";\nimport { AlertTriangle, Check, CloudOff, RefreshCw, UploadCloud } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport type { OfflineSync, SyncTone } from \"@/offline\";\nimport { useSyncStatus } from \"@/offline/use-offline-sync\";\nimport styles from \"./SyncStatusBadge.module.css\";\n\nconst DEFAULT_LABELS: Record<SyncTone, string> = {\n idle: \"Sincronizado\",\n syncing: \"Sincronizando…\",\n pending: \"Pendente\",\n offline: \"Offline\",\n error: \"Erro\",\n};\n\nconst ICONS: Record<SyncTone, typeof Check> = {\n idle: Check,\n syncing: RefreshCw,\n pending: UploadCloud,\n offline: CloudOff,\n error: AlertTriangle,\n};\n\nexport interface SyncStatusBadgeProps extends Omit<HTMLAttributes<HTMLSpanElement>, \"children\"> {\n /**\n * The dominant tone. Required when `sync` is not passed. Ignored when `sync`\n * is passed (the tone is derived from the engine).\n */\n tone?: SyncTone;\n /**\n * An `OfflineSync` engine to observe directly. When set, the badge wires\n * `useSyncStatus` internally, so `tone` and `pending` come from the engine.\n */\n sync?: OfflineSync<unknown>;\n /** Number of queued mutations, appended to the label when `> 0`. */\n pending?: number;\n /** Override the per-tone label text. */\n labels?: Partial<Record<SyncTone, string>>;\n /** Show the pending count next to the label. Default `true`. */\n showPending?: boolean;\n /** Render only the icon (no text). Default `false`. */\n iconOnly?: boolean;\n}\n\ntype PresentationalProps = Omit<SyncStatusBadgeProps, \"sync\">;\n\nfunction PresentationalBadge({\n tone = \"idle\",\n pending = 0,\n labels,\n showPending = true,\n iconOnly = false,\n className,\n ...props\n}: PresentationalProps) {\n const Icon = ICONS[tone];\n const label = labels?.[tone] ?? DEFAULT_LABELS[tone];\n const showCount = showPending && pending > 0 && (tone === \"pending\" || tone === \"error\");\n return (\n <span\n className={cn(styles.badge, styles[tone], className)}\n role=\"status\"\n aria-live=\"polite\"\n title={label}\n {...props}\n >\n <Icon\n className={cn(styles.icon, tone === \"syncing\" && styles.spin)}\n size={14}\n aria-hidden=\"true\"\n />\n {!iconOnly && <span className={styles.label}>{label}</span>}\n {showCount && <span className={styles.count}>{pending}</span>}\n </span>\n );\n}\n\nfunction ConnectedBadge({ sync, ...rest }: PresentationalProps & { sync: OfflineSync<unknown> }) {\n const { tone, pending } = useSyncStatus(sync);\n return <PresentationalBadge {...rest} tone={tone} pending={pending} />;\n}\n\n/**\n * Compact pill showing offline-sync status: a tone-colored icon plus a label\n * and optional pending count.\n *\n * Two modes: pass `sync` to have the badge observe an engine via\n * `useSyncStatus` (zero wiring), or pass an explicit `tone` (+ `pending`) to\n * keep it presentational and testable without IndexedDB.\n *\n * @example\n * // Connected — auto-wires the engine:\n * <SyncStatusBadge sync={notesSync} />\n *\n * // Presentational — you own the state:\n * const { tone, pending } = useSyncStatus(notesSync);\n * <SyncStatusBadge tone={tone} pending={pending} />\n */\nexport function SyncStatusBadge({ sync, ...rest }: SyncStatusBadgeProps) {\n if (sync) return <ConnectedBadge sync={sync} {...rest} />;\n return <PresentationalBadge {...rest} />;\n}\n"],"mappings":";;;;;;AAOA,IAAM,IAA2C;CAC7C,MAAM;CACN,SAAS;CACT,SAAS;CACT,SAAS;CACT,OAAO;AACX,GAEM,IAAwC;CAC1C,MAAM;CACN,SAAS;CACT,SAAS;CACT,SAAS;CACT,OAAO;AACX;AAyBA,SAAS,EAAoB,EACzB,UAAO,QACP,aAAU,GACV,WACA,iBAAc,IACd,cAAW,IACX,cACA,GAAG,KACiB;CACpB,IAAM,IAAO,EAAM,IACb,IAAQ,IAAS,MAAS,EAAe,IACzC,IAAY,KAAe,IAAU,MAAM,MAAS,aAAa,MAAS;CAChF,OACI,kBAAC,QAAD;EACI,WAAW,EAAG,EAAO,OAAO,EAAO,IAAO,CAAS;EACnD,MAAK;EACL,aAAU;EACV,OAAO;EACP,GAAI;YALR;GAOI,kBAAC,GAAD;IACI,WAAW,EAAG,EAAO,MAAM,MAAS,aAAa,EAAO,IAAI;IAC5D,MAAM;IACN,eAAY;GACf,CAAA;GACA,CAAC,KAAY,kBAAC,QAAD;IAAM,WAAW,EAAO;cAAQ;GAAY,CAAA;GACzD,KAAa,kBAAC,QAAD;IAAM,WAAW,EAAO;cAAQ;GAAc,CAAA;EAC1D;;AAEd;AAEA,SAAS,EAAe,EAAE,SAAM,GAAG,KAA8D;CAC7F,IAAM,EAAE,SAAM,eAAY,EAAc,CAAI;CAC5C,OAAO,kBAAC,GAAD;EAAqB,GAAI;EAAY;EAAe;CAAU,CAAA;AACzE;AAkBA,SAAgB,EAAgB,EAAE,SAAM,GAAG,KAA8B;CAErE,OADI,IAAa,kBAAC,GAAD;EAAsB;EAAM,GAAI;CAAO,CAAA,IACjD,kBAAC,GAAD,EAAqB,GAAI,EAAO,CAAA;AAC3C"}
1
+ {"version":3,"file":"SyncStatusBadge.js","names":[],"sources":["../../../src/components/SyncStatusBadge/SyncStatusBadge.tsx"],"sourcesContent":["import type { HTMLAttributes } from \"react\";\nimport { AlertTriangle, Check, CloudOff, RefreshCw, UploadCloud } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport type { OfflineSync, SyncTone } from \"@/offline\";\nimport { useSyncStatus } from \"@/offline/use-offline-sync\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport styles from \"./SyncStatusBadge.module.css\";\n\nconst DEFAULT_LABELS: Record<SyncTone, string> = {\n idle: \"Sincronizado\",\n syncing: \"Sincronizando…\",\n pending: \"Pendente\",\n offline: \"Offline\",\n error: \"Erro\",\n};\n\nconst ICONS: Record<SyncTone, typeof Check> = {\n idle: Check,\n syncing: RefreshCw,\n pending: UploadCloud,\n offline: CloudOff,\n error: AlertTriangle,\n};\n\nexport interface SyncStatusBadgeProps extends Omit<HTMLAttributes<HTMLSpanElement>, \"children\"> {\n /**\n * The dominant tone. Required when `sync` is not passed. Ignored when `sync`\n * is passed (the tone is derived from the engine).\n */\n tone?: SyncTone;\n /**\n * An `OfflineSync` engine to observe directly. When set, the badge wires\n * `useSyncStatus` internally, so `tone` and `pending` come from the engine.\n */\n sync?: OfflineSync<unknown>;\n /** Number of queued mutations, appended to the label when `> 0`. */\n pending?: number;\n /** Override the per-tone label text. */\n labels?: Partial<Record<SyncTone, string>>;\n /** Show the pending count next to the label. Default `true`. */\n showPending?: boolean;\n /** Render only the icon (no text). Default `false`. */\n iconOnly?: boolean;\n}\n\ntype PresentationalProps = Omit<SyncStatusBadgeProps, \"sync\">;\n\n/**\n * The badge itself: a persistent `role=\"status\"` region.\n *\n * Left as its own live region rather than routed through `useAnnounce`, on purpose.\n * The badge is not a transient message — it is a piece of state that stays on\n * screen, which is exactly what `role=\"status\"` describes, and a reader can go back\n * and re-read it. Announcing it *again* from the shared announcer would read every\n * tone change twice.\n *\n * `iconOnly` used to break that, though: dropping the label left a live region whose\n * content never changed, so a switch to `offline` or `error` was announced as\n * nothing at all. The label is now always rendered — visually hidden when\n * `iconOnly`, since `title` is not content and is not announced on a change.\n */\nfunction PresentationalBadge({\n tone = \"idle\",\n pending = 0,\n labels,\n showPending = true,\n iconOnly = false,\n className,\n ...props\n}: PresentationalProps) {\n const Icon = ICONS[tone];\n const label = labels?.[tone] ?? DEFAULT_LABELS[tone];\n const showCount = showPending && pending > 0 && (tone === \"pending\" || tone === \"error\");\n return (\n <span\n className={cn(styles.badge, styles[tone], className)}\n role=\"status\"\n aria-live=\"polite\"\n title={label}\n {...props}\n >\n <Icon\n className={cn(styles.icon, tone === \"syncing\" && styles.spin)}\n size={14}\n aria-hidden=\"true\"\n />\n {iconOnly ? (\n <VisuallyHidden>{label}</VisuallyHidden>\n ) : (\n <span className={styles.label}>{label}</span>\n )}\n {showCount && <span className={styles.count}>{pending}</span>}\n </span>\n );\n}\n\nfunction ConnectedBadge({ sync, ...rest }: PresentationalProps & { sync: OfflineSync<unknown> }) {\n const { tone, pending } = useSyncStatus(sync);\n return <PresentationalBadge {...rest} tone={tone} pending={pending} />;\n}\n\n/**\n * Compact pill showing offline-sync status: a tone-colored icon plus a label\n * and optional pending count.\n *\n * Two modes: pass `sync` to have the badge observe an engine via\n * `useSyncStatus` (zero wiring), or pass an explicit `tone` (+ `pending`) to\n * keep it presentational and testable without IndexedDB.\n *\n * @example\n * // Connected — auto-wires the engine:\n * <SyncStatusBadge sync={notesSync} />\n *\n * // Presentational — you own the state:\n * const { tone, pending } = useSyncStatus(notesSync);\n * <SyncStatusBadge tone={tone} pending={pending} />\n */\nexport function SyncStatusBadge({ sync, ...rest }: SyncStatusBadgeProps) {\n if (sync) return <ConnectedBadge sync={sync} {...rest} />;\n return <PresentationalBadge {...rest} />;\n}\n"],"mappings":";;;;;;;AAQA,IAAM,IAA2C;CAC7C,MAAM;CACN,SAAS;CACT,SAAS;CACT,SAAS;CACT,OAAO;AACX,GAEM,IAAwC;CAC1C,MAAM;CACN,SAAS;CACT,SAAS;CACT,SAAS;CACT,OAAO;AACX;AAuCA,SAAS,EAAoB,EACzB,UAAO,QACP,aAAU,GACV,WACA,iBAAc,IACd,cAAW,IACX,cACA,GAAG,KACiB;CACpB,IAAM,IAAO,EAAM,IACb,IAAQ,IAAS,MAAS,EAAe,IACzC,IAAY,KAAe,IAAU,MAAM,MAAS,aAAa,MAAS;CAChF,OACI,kBAAC,QAAD;EACI,WAAW,EAAG,EAAO,OAAO,EAAO,IAAO,CAAS;EACnD,MAAK;EACL,aAAU;EACV,OAAO;EACP,GAAI;YALR;GAOI,kBAAC,GAAD;IACI,WAAW,EAAG,EAAO,MAAM,MAAS,aAAa,EAAO,IAAI;IAC5D,MAAM;IACN,eAAY;GACf,CAAA;GACA,IACG,kBAAC,GAAD,EAAA,UAAiB,EAAsB,CAAA,IAEvC,kBAAC,QAAD;IAAM,WAAW,EAAO;cAAQ;GAAY,CAAA;GAE/C,KAAa,kBAAC,QAAD;IAAM,WAAW,EAAO;cAAQ;GAAc,CAAA;EAC1D;;AAEd;AAEA,SAAS,EAAe,EAAE,SAAM,GAAG,KAA8D;CAC7F,IAAM,EAAE,SAAM,eAAY,EAAc,CAAI;CAC5C,OAAO,kBAAC,GAAD;EAAqB,GAAI;EAAY;EAAe;CAAU,CAAA;AACzE;AAkBA,SAAgB,EAAgB,EAAE,SAAM,GAAG,KAA8B;CAErE,OADI,IAAa,kBAAC,GAAD;EAAsB;EAAM,GAAI;CAAO,CAAA,IACjD,kBAAC,GAAD,EAAqB,GAAI,EAAO,CAAA;AAC3C"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("./Toast.module.cjs");let n=require("react"),r=require("react/jsx-runtime"),i=require("react-dom");var a=(0,n.createContext)(null);function o(){let e=(0,n.useContext)(a);if(!e)throw Error(`useToast must be used inside a <ToastProvider>`);return e}function s({children:e,defaultDuration:t=4e3,position:i=`top-right`}){let[o,s]=(0,n.useState)([]),c=(0,n.useRef)(0),u=(0,n.useCallback)(e=>{s(t=>t.filter(t=>t.id!==e))},[]),d=(0,n.useCallback)(e=>{let n=e.id??`toast-${++c.current}`,r={id:n,title:e.title,description:e.description,variant:e.variant??`info`,duration:e.duration??t};return s(e=>[...e,r]),n},[t]),f=(0,n.useMemo)(()=>({show:d,dismiss:u,success:(e,t)=>d({...t,description:e,variant:`success`}),error:(e,t)=>d({...t,description:e,variant:`error`}),warning:(e,t)=>d({...t,description:e,variant:`warning`}),info:(e,t)=>d({...t,description:e,variant:`info`})}),[d,u]);return(0,r.jsxs)(a.Provider,{value:f,children:[e,(0,r.jsx)(l,{toasts:o,onDismiss:u,position:i})]})}function c(e){switch(e){case`top-left`:return t.default.positionTopLeft;case`top-center`:return t.default.positionTopCenter;case`bottom-right`:return t.default.positionBottomRight;case`bottom-left`:return t.default.positionBottomLeft;case`bottom-center`:return t.default.positionBottomCenter;default:return t.default.positionTopRight}}function l({toasts:n,onDismiss:a,position:o}){return typeof document>`u`?null:(0,i.createPortal)((0,r.jsx)(`div`,{className:e.cn(t.default.container,c(o)),"aria-live":`polite`,"aria-atomic":`true`,children:n.map(e=>(0,r.jsx)(u,{toast:e,onDismiss:a},e.id))}),document.body)}function u({toast:i,onDismiss:a}){return(0,n.useEffect)(()=>{if(!i.duration)return;let e=setTimeout(()=>a(i.id),i.duration);return()=>clearTimeout(e)},[i.id,i.duration,a]),(0,r.jsxs)(`div`,{className:e.cn(t.default.toast,t.default[i.variant]),role:`status`,children:[(0,r.jsxs)(`div`,{children:[i.title&&(0,r.jsx)(`p`,{className:t.default.title,children:i.title}),i.description&&(0,r.jsx)(`p`,{className:t.default.description,children:i.description})]}),(0,r.jsx)(`button`,{type:`button`,className:t.default.close,"aria-label":`Fechar notificação`,onClick:()=>a(i.id),children:(0,r.jsx)(d,{})})]})}function d(){return(0,r.jsx)(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,children:(0,r.jsx)(`path`,{d:`M6 6l12 12M6 18L18 6`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`})})}exports.ToastProvider=s,exports.useToast=o;
1
+ const e=require("../../utils/cn.cjs"),t=require("./Toast.module.cjs");let n=require("react"),r=require("react/jsx-runtime"),i=require("react-dom");var a=(0,n.createContext)(null);function o(){let e=(0,n.useContext)(a);if(!e)throw Error(`useToast must be used inside a <ToastProvider>`);return e}function s({children:e,defaultDuration:t=4e3,position:i=`top-right`}){let[o,s]=(0,n.useState)([]),c=(0,n.useRef)(0),u=(0,n.useCallback)(e=>{s(t=>t.filter(t=>t.id!==e))},[]),d=(0,n.useCallback)(e=>{let n=e.id??`toast-${++c.current}`,r={id:n,title:e.title,description:e.description,variant:e.variant??`info`,duration:e.duration??t};return s(e=>[...e,r]),n},[t]),f=(0,n.useMemo)(()=>({show:d,dismiss:u,success:(e,t)=>d({...t,description:e,variant:`success`}),error:(e,t)=>d({...t,description:e,variant:`error`}),warning:(e,t)=>d({...t,description:e,variant:`warning`}),info:(e,t)=>d({...t,description:e,variant:`info`})}),[d,u]);return(0,r.jsxs)(a.Provider,{value:f,children:[e,(0,r.jsx)(l,{toasts:o,onDismiss:u,position:i})]})}function c(e){switch(e){case`top-left`:return t.default.positionTopLeft;case`top-center`:return t.default.positionTopCenter;case`bottom-right`:return t.default.positionBottomRight;case`bottom-left`:return t.default.positionBottomLeft;case`bottom-center`:return t.default.positionBottomCenter;default:return t.default.positionTopRight}}function l({toasts:n,onDismiss:a,position:o}){return typeof document>`u`?null:(0,i.createPortal)((0,r.jsx)(`div`,{className:e.cn(t.default.container,c(o)),"aria-live":`polite`,"aria-atomic":`false`,children:n.map(e=>(0,r.jsx)(u,{toast:e,onDismiss:a},e.id))}),document.body)}function u({toast:i,onDismiss:a}){return(0,n.useEffect)(()=>{if(!i.duration)return;let e=setTimeout(()=>a(i.id),i.duration);return()=>clearTimeout(e)},[i.id,i.duration,a]),(0,r.jsxs)(`div`,{className:e.cn(t.default.toast,t.default[i.variant]),children:[(0,r.jsxs)(`div`,{children:[i.title&&(0,r.jsx)(`p`,{className:t.default.title,children:i.title}),i.description&&(0,r.jsx)(`p`,{className:t.default.description,children:i.description})]}),(0,r.jsx)(`button`,{type:`button`,className:t.default.close,"aria-label":`Fechar notificação`,onClick:()=>a(i.id),children:(0,r.jsx)(d,{})})]})}function d(){return(0,r.jsx)(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,children:(0,r.jsx)(`path`,{d:`M6 6l12 12M6 18L18 6`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`})})}exports.ToastProvider=s,exports.useToast=o;
2
2
  //# sourceMappingURL=ToastProvider.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ToastProvider.cjs","names":[],"sources":["../../../src/components/Toast/ToastProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Toast.module.css\";\n\nexport type ToastVariant = \"success\" | \"warning\" | \"error\" | \"info\";\n\nexport interface ToastOptions {\n id?: string;\n title?: string;\n description?: string;\n variant?: ToastVariant;\n /** Auto-dismiss timeout in ms. Pass `0` to keep the toast until dismissed manually. */\n duration?: number;\n}\n\ninterface ToastEntry extends Required<Omit<ToastOptions, \"id\" | \"title\" | \"description\">> {\n id: string;\n title?: string;\n description?: string;\n}\n\nexport interface ToastApi {\n show: (options: ToastOptions) => string;\n dismiss: (id: string) => void;\n success: (\n description: string,\n options?: Omit<ToastOptions, \"variant\" | \"description\">,\n ) => string;\n error: (description: string, options?: Omit<ToastOptions, \"variant\" | \"description\">) => string;\n warning: (\n description: string,\n options?: Omit<ToastOptions, \"variant\" | \"description\">,\n ) => string;\n info: (description: string, options?: Omit<ToastOptions, \"variant\" | \"description\">) => string;\n}\n\nconst ToastContext = createContext<ToastApi | null>(null);\n\n/**\n * Access the toast API. Must be used inside a {@link ToastProvider}.\n *\n * @returns Methods to show and dismiss toasts.\n */\nexport function useToast(): ToastApi {\n const ctx = useContext(ToastContext);\n if (!ctx) throw new Error(\"useToast must be used inside a <ToastProvider>\");\n return ctx;\n}\n\nexport type ToastPosition =\n \"top-right\" | \"top-left\" | \"top-center\" | \"bottom-right\" | \"bottom-left\" | \"bottom-center\";\n\nexport interface ToastProviderProps {\n children: ReactNode;\n /** Default auto-dismiss duration (ms). Default 4000. */\n defaultDuration?: number;\n /** Stack position on screen. Default `\"top-right\"`. */\n position?: ToastPosition;\n}\n\n/**\n * Renders a portalled toast container and exposes the imperative {@link useToast} API.\n */\nexport function ToastProvider({\n children,\n defaultDuration = 4000,\n position = \"top-right\",\n}: ToastProviderProps) {\n const [toasts, setToasts] = useState<ToastEntry[]>([]);\n const counter = useRef<number>(0);\n\n const dismiss = useCallback((id: string): void => {\n setToasts((current) => current.filter((toast) => toast.id !== id));\n }, []);\n\n const show = useCallback(\n (options: ToastOptions): string => {\n const id = options.id ?? `toast-${++counter.current}`;\n const entry: ToastEntry = {\n id,\n title: options.title,\n description: options.description,\n variant: options.variant ?? \"info\",\n duration: options.duration ?? defaultDuration,\n };\n setToasts((current) => [...current, entry]);\n return id;\n },\n [defaultDuration],\n );\n\n const api = useMemo<ToastApi>(\n () => ({\n show,\n dismiss,\n success: (description, options) =>\n show({ ...options, description, variant: \"success\" }),\n error: (description, options) => show({ ...options, description, variant: \"error\" }),\n warning: (description, options) =>\n show({ ...options, description, variant: \"warning\" }),\n info: (description, options) => show({ ...options, description, variant: \"info\" }),\n }),\n [show, dismiss],\n );\n\n return (\n <ToastContext.Provider value={api}>\n {children}\n <ToastContainer toasts={toasts} onDismiss={dismiss} position={position} />\n </ToastContext.Provider>\n );\n}\n\ninterface ContainerProps {\n toasts: ToastEntry[];\n onDismiss: (id: string) => void;\n position: ToastPosition;\n}\n\nfunction positionClass(position: ToastPosition): string {\n switch (position) {\n case \"top-left\":\n return styles.positionTopLeft;\n case \"top-center\":\n return styles.positionTopCenter;\n case \"bottom-right\":\n return styles.positionBottomRight;\n case \"bottom-left\":\n return styles.positionBottomLeft;\n case \"bottom-center\":\n return styles.positionBottomCenter;\n case \"top-right\":\n default:\n return styles.positionTopRight;\n }\n}\n\nfunction ToastContainer({ toasts, onDismiss, position }: ContainerProps) {\n if (typeof document === \"undefined\") return null;\n return createPortal(\n <div\n className={cn(styles.container, positionClass(position))}\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >\n {toasts.map((toast) => (\n <ToastItem key={toast.id} toast={toast} onDismiss={onDismiss} />\n ))}\n </div>,\n document.body,\n );\n}\n\ninterface ItemProps {\n toast: ToastEntry;\n onDismiss: (id: string) => void;\n}\n\nfunction ToastItem({ toast, onDismiss }: ItemProps) {\n useEffect(() => {\n if (!toast.duration) return;\n const timer = setTimeout(() => onDismiss(toast.id), toast.duration);\n return () => clearTimeout(timer);\n }, [toast.id, toast.duration, onDismiss]);\n\n return (\n <div className={cn(styles.toast, styles[toast.variant])} role=\"status\">\n <div>\n {toast.title && <p className={styles.title}>{toast.title}</p>}\n {toast.description && <p className={styles.description}>{toast.description}</p>}\n </div>\n <button\n type=\"button\"\n className={styles.close}\n aria-label=\"Fechar notificação\"\n onClick={() => onDismiss(toast.id)}\n >\n <CloseIcon />\n </button>\n </div>\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ToastProvider.cjs","names":[],"sources":["../../../src/components/Toast/ToastProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Toast.module.css\";\n\nexport type ToastVariant = \"success\" | \"warning\" | \"error\" | \"info\";\n\nexport interface ToastOptions {\n id?: string;\n title?: string;\n description?: string;\n variant?: ToastVariant;\n /** Auto-dismiss timeout in ms. Pass `0` to keep the toast until dismissed manually. */\n duration?: number;\n}\n\ninterface ToastEntry extends Required<Omit<ToastOptions, \"id\" | \"title\" | \"description\">> {\n id: string;\n title?: string;\n description?: string;\n}\n\nexport interface ToastApi {\n show: (options: ToastOptions) => string;\n dismiss: (id: string) => void;\n success: (\n description: string,\n options?: Omit<ToastOptions, \"variant\" | \"description\">,\n ) => string;\n error: (description: string, options?: Omit<ToastOptions, \"variant\" | \"description\">) => string;\n warning: (\n description: string,\n options?: Omit<ToastOptions, \"variant\" | \"description\">,\n ) => string;\n info: (description: string, options?: Omit<ToastOptions, \"variant\" | \"description\">) => string;\n}\n\nconst ToastContext = createContext<ToastApi | null>(null);\n\n/**\n * Access the toast API. Must be used inside a {@link ToastProvider}.\n *\n * @returns Methods to show and dismiss toasts.\n */\nexport function useToast(): ToastApi {\n const ctx = useContext(ToastContext);\n if (!ctx) throw new Error(\"useToast must be used inside a <ToastProvider>\");\n return ctx;\n}\n\nexport type ToastPosition =\n \"top-right\" | \"top-left\" | \"top-center\" | \"bottom-right\" | \"bottom-left\" | \"bottom-center\";\n\nexport interface ToastProviderProps {\n children: ReactNode;\n /** Default auto-dismiss duration (ms). Default 4000. */\n defaultDuration?: number;\n /** Stack position on screen. Default `\"top-right\"`. */\n position?: ToastPosition;\n}\n\n/**\n * Renders a portalled toast container and exposes the imperative {@link useToast} API.\n */\nexport function ToastProvider({\n children,\n defaultDuration = 4000,\n position = \"top-right\",\n}: ToastProviderProps) {\n const [toasts, setToasts] = useState<ToastEntry[]>([]);\n const counter = useRef<number>(0);\n\n const dismiss = useCallback((id: string): void => {\n setToasts((current) => current.filter((toast) => toast.id !== id));\n }, []);\n\n const show = useCallback(\n (options: ToastOptions): string => {\n const id = options.id ?? `toast-${++counter.current}`;\n const entry: ToastEntry = {\n id,\n title: options.title,\n description: options.description,\n variant: options.variant ?? \"info\",\n duration: options.duration ?? defaultDuration,\n };\n setToasts((current) => [...current, entry]);\n return id;\n },\n [defaultDuration],\n );\n\n const api = useMemo<ToastApi>(\n () => ({\n show,\n dismiss,\n success: (description, options) =>\n show({ ...options, description, variant: \"success\" }),\n error: (description, options) => show({ ...options, description, variant: \"error\" }),\n warning: (description, options) =>\n show({ ...options, description, variant: \"warning\" }),\n info: (description, options) => show({ ...options, description, variant: \"info\" }),\n }),\n [show, dismiss],\n );\n\n return (\n <ToastContext.Provider value={api}>\n {children}\n <ToastContainer toasts={toasts} onDismiss={dismiss} position={position} />\n </ToastContext.Provider>\n );\n}\n\ninterface ContainerProps {\n toasts: ToastEntry[];\n onDismiss: (id: string) => void;\n position: ToastPosition;\n}\n\nfunction positionClass(position: ToastPosition): string {\n switch (position) {\n case \"top-left\":\n return styles.positionTopLeft;\n case \"top-center\":\n return styles.positionTopCenter;\n case \"bottom-right\":\n return styles.positionBottomRight;\n case \"bottom-left\":\n return styles.positionBottomLeft;\n case \"bottom-center\":\n return styles.positionBottomCenter;\n case \"top-right\":\n default:\n return styles.positionTopRight;\n }\n}\n\n/**\n * The portalled stack, and the page's live region for toasts.\n *\n * `aria-atomic=\"false\"` matters: with `\"true\"` a screen reader re-reads the\n * **entire** stack on every change, so the third toast of a batch is announced as\n * all three, and dismissing one re-announces the survivors. `\"false\"` announces only\n * the node that was added, which is what the user needs.\n *\n * The stack stays the live region rather than delegating to `useAnnounce`, because\n * routing it through the shared announcer would put the same text in the document\n * twice — once visible, once hidden — and every `getByText(\"Salvo\")` in a consuming\n * app's test suite would start matching two nodes. The announcer is for messages\n * that have no on-screen home; a toast has one.\n */\nfunction ToastContainer({ toasts, onDismiss, position }: ContainerProps) {\n if (typeof document === \"undefined\") return null;\n return createPortal(\n <div\n className={cn(styles.container, positionClass(position))}\n aria-live=\"polite\"\n aria-atomic=\"false\"\n >\n {toasts.map((toast) => (\n <ToastItem key={toast.id} toast={toast} onDismiss={onDismiss} />\n ))}\n </div>,\n document.body,\n );\n}\n\ninterface ItemProps {\n toast: ToastEntry;\n onDismiss: (id: string) => void;\n}\n\n/**\n * One toast.\n *\n * No `role=\"status\"` of its own: it already lives inside the container's live\n * region, and a live region nested in a live region is announced twice by some\n * screen readers.\n */\nfunction ToastItem({ toast, onDismiss }: ItemProps) {\n useEffect(() => {\n if (!toast.duration) return;\n const timer = setTimeout(() => onDismiss(toast.id), toast.duration);\n return () => clearTimeout(timer);\n }, [toast.id, toast.duration, onDismiss]);\n\n return (\n <div className={cn(styles.toast, styles[toast.variant])}>\n <div>\n {toast.title && <p className={styles.title}>{toast.title}</p>}\n {toast.description && <p className={styles.description}>{toast.description}</p>}\n </div>\n <button\n type=\"button\"\n className={styles.close}\n aria-label=\"Fechar notificação\"\n onClick={() => onDismiss(toast.id)}\n >\n <CloseIcon />\n </button>\n </div>\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":"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"}
@@ -69,7 +69,7 @@ function g({ toasts: n, onDismiss: r, position: i }) {
69
69
  return typeof document > "u" ? null : d(/* @__PURE__ */ l("div", {
70
70
  className: e(t.container, h(i)),
71
71
  "aria-live": "polite",
72
- "aria-atomic": "true",
72
+ "aria-atomic": "false",
73
73
  children: n.map((e) => /* @__PURE__ */ l(_, {
74
74
  toast: e,
75
75
  onDismiss: r
@@ -87,7 +87,6 @@ function _({ toast: n, onDismiss: r }) {
87
87
  r
88
88
  ]), /* @__PURE__ */ u("div", {
89
89
  className: e(t.toast, t[n.variant]),
90
- role: "status",
91
90
  children: [/* @__PURE__ */ u("div", { children: [n.title && /* @__PURE__ */ l("p", {
92
91
  className: t.title,
93
92
  children: n.title
@@ -1 +1 @@
1
- {"version":3,"file":"ToastProvider.js","names":[],"sources":["../../../src/components/Toast/ToastProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Toast.module.css\";\n\nexport type ToastVariant = \"success\" | \"warning\" | \"error\" | \"info\";\n\nexport interface ToastOptions {\n id?: string;\n title?: string;\n description?: string;\n variant?: ToastVariant;\n /** Auto-dismiss timeout in ms. Pass `0` to keep the toast until dismissed manually. */\n duration?: number;\n}\n\ninterface ToastEntry extends Required<Omit<ToastOptions, \"id\" | \"title\" | \"description\">> {\n id: string;\n title?: string;\n description?: string;\n}\n\nexport interface ToastApi {\n show: (options: ToastOptions) => string;\n dismiss: (id: string) => void;\n success: (\n description: string,\n options?: Omit<ToastOptions, \"variant\" | \"description\">,\n ) => string;\n error: (description: string, options?: Omit<ToastOptions, \"variant\" | \"description\">) => string;\n warning: (\n description: string,\n options?: Omit<ToastOptions, \"variant\" | \"description\">,\n ) => string;\n info: (description: string, options?: Omit<ToastOptions, \"variant\" | \"description\">) => string;\n}\n\nconst ToastContext = createContext<ToastApi | null>(null);\n\n/**\n * Access the toast API. Must be used inside a {@link ToastProvider}.\n *\n * @returns Methods to show and dismiss toasts.\n */\nexport function useToast(): ToastApi {\n const ctx = useContext(ToastContext);\n if (!ctx) throw new Error(\"useToast must be used inside a <ToastProvider>\");\n return ctx;\n}\n\nexport type ToastPosition =\n \"top-right\" | \"top-left\" | \"top-center\" | \"bottom-right\" | \"bottom-left\" | \"bottom-center\";\n\nexport interface ToastProviderProps {\n children: ReactNode;\n /** Default auto-dismiss duration (ms). Default 4000. */\n defaultDuration?: number;\n /** Stack position on screen. Default `\"top-right\"`. */\n position?: ToastPosition;\n}\n\n/**\n * Renders a portalled toast container and exposes the imperative {@link useToast} API.\n */\nexport function ToastProvider({\n children,\n defaultDuration = 4000,\n position = \"top-right\",\n}: ToastProviderProps) {\n const [toasts, setToasts] = useState<ToastEntry[]>([]);\n const counter = useRef<number>(0);\n\n const dismiss = useCallback((id: string): void => {\n setToasts((current) => current.filter((toast) => toast.id !== id));\n }, []);\n\n const show = useCallback(\n (options: ToastOptions): string => {\n const id = options.id ?? `toast-${++counter.current}`;\n const entry: ToastEntry = {\n id,\n title: options.title,\n description: options.description,\n variant: options.variant ?? \"info\",\n duration: options.duration ?? defaultDuration,\n };\n setToasts((current) => [...current, entry]);\n return id;\n },\n [defaultDuration],\n );\n\n const api = useMemo<ToastApi>(\n () => ({\n show,\n dismiss,\n success: (description, options) =>\n show({ ...options, description, variant: \"success\" }),\n error: (description, options) => show({ ...options, description, variant: \"error\" }),\n warning: (description, options) =>\n show({ ...options, description, variant: \"warning\" }),\n info: (description, options) => show({ ...options, description, variant: \"info\" }),\n }),\n [show, dismiss],\n );\n\n return (\n <ToastContext.Provider value={api}>\n {children}\n <ToastContainer toasts={toasts} onDismiss={dismiss} position={position} />\n </ToastContext.Provider>\n );\n}\n\ninterface ContainerProps {\n toasts: ToastEntry[];\n onDismiss: (id: string) => void;\n position: ToastPosition;\n}\n\nfunction positionClass(position: ToastPosition): string {\n switch (position) {\n case \"top-left\":\n return styles.positionTopLeft;\n case \"top-center\":\n return styles.positionTopCenter;\n case \"bottom-right\":\n return styles.positionBottomRight;\n case \"bottom-left\":\n return styles.positionBottomLeft;\n case \"bottom-center\":\n return styles.positionBottomCenter;\n case \"top-right\":\n default:\n return styles.positionTopRight;\n }\n}\n\nfunction ToastContainer({ toasts, onDismiss, position }: ContainerProps) {\n if (typeof document === \"undefined\") return null;\n return createPortal(\n <div\n className={cn(styles.container, positionClass(position))}\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >\n {toasts.map((toast) => (\n <ToastItem key={toast.id} toast={toast} onDismiss={onDismiss} />\n ))}\n </div>,\n document.body,\n );\n}\n\ninterface ItemProps {\n toast: ToastEntry;\n onDismiss: (id: string) => void;\n}\n\nfunction ToastItem({ toast, onDismiss }: ItemProps) {\n useEffect(() => {\n if (!toast.duration) return;\n const timer = setTimeout(() => onDismiss(toast.id), toast.duration);\n return () => clearTimeout(timer);\n }, [toast.id, toast.duration, onDismiss]);\n\n return (\n <div className={cn(styles.toast, styles[toast.variant])} role=\"status\">\n <div>\n {toast.title && <p className={styles.title}>{toast.title}</p>}\n {toast.description && <p className={styles.description}>{toast.description}</p>}\n </div>\n <button\n type=\"button\"\n className={styles.close}\n aria-label=\"Fechar notificação\"\n onClick={() => onDismiss(toast.id)}\n >\n <CloseIcon />\n </button>\n </div>\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ToastProvider.js","names":[],"sources":["../../../src/components/Toast/ToastProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Toast.module.css\";\n\nexport type ToastVariant = \"success\" | \"warning\" | \"error\" | \"info\";\n\nexport interface ToastOptions {\n id?: string;\n title?: string;\n description?: string;\n variant?: ToastVariant;\n /** Auto-dismiss timeout in ms. Pass `0` to keep the toast until dismissed manually. */\n duration?: number;\n}\n\ninterface ToastEntry extends Required<Omit<ToastOptions, \"id\" | \"title\" | \"description\">> {\n id: string;\n title?: string;\n description?: string;\n}\n\nexport interface ToastApi {\n show: (options: ToastOptions) => string;\n dismiss: (id: string) => void;\n success: (\n description: string,\n options?: Omit<ToastOptions, \"variant\" | \"description\">,\n ) => string;\n error: (description: string, options?: Omit<ToastOptions, \"variant\" | \"description\">) => string;\n warning: (\n description: string,\n options?: Omit<ToastOptions, \"variant\" | \"description\">,\n ) => string;\n info: (description: string, options?: Omit<ToastOptions, \"variant\" | \"description\">) => string;\n}\n\nconst ToastContext = createContext<ToastApi | null>(null);\n\n/**\n * Access the toast API. Must be used inside a {@link ToastProvider}.\n *\n * @returns Methods to show and dismiss toasts.\n */\nexport function useToast(): ToastApi {\n const ctx = useContext(ToastContext);\n if (!ctx) throw new Error(\"useToast must be used inside a <ToastProvider>\");\n return ctx;\n}\n\nexport type ToastPosition =\n \"top-right\" | \"top-left\" | \"top-center\" | \"bottom-right\" | \"bottom-left\" | \"bottom-center\";\n\nexport interface ToastProviderProps {\n children: ReactNode;\n /** Default auto-dismiss duration (ms). Default 4000. */\n defaultDuration?: number;\n /** Stack position on screen. Default `\"top-right\"`. */\n position?: ToastPosition;\n}\n\n/**\n * Renders a portalled toast container and exposes the imperative {@link useToast} API.\n */\nexport function ToastProvider({\n children,\n defaultDuration = 4000,\n position = \"top-right\",\n}: ToastProviderProps) {\n const [toasts, setToasts] = useState<ToastEntry[]>([]);\n const counter = useRef<number>(0);\n\n const dismiss = useCallback((id: string): void => {\n setToasts((current) => current.filter((toast) => toast.id !== id));\n }, []);\n\n const show = useCallback(\n (options: ToastOptions): string => {\n const id = options.id ?? `toast-${++counter.current}`;\n const entry: ToastEntry = {\n id,\n title: options.title,\n description: options.description,\n variant: options.variant ?? \"info\",\n duration: options.duration ?? defaultDuration,\n };\n setToasts((current) => [...current, entry]);\n return id;\n },\n [defaultDuration],\n );\n\n const api = useMemo<ToastApi>(\n () => ({\n show,\n dismiss,\n success: (description, options) =>\n show({ ...options, description, variant: \"success\" }),\n error: (description, options) => show({ ...options, description, variant: \"error\" }),\n warning: (description, options) =>\n show({ ...options, description, variant: \"warning\" }),\n info: (description, options) => show({ ...options, description, variant: \"info\" }),\n }),\n [show, dismiss],\n );\n\n return (\n <ToastContext.Provider value={api}>\n {children}\n <ToastContainer toasts={toasts} onDismiss={dismiss} position={position} />\n </ToastContext.Provider>\n );\n}\n\ninterface ContainerProps {\n toasts: ToastEntry[];\n onDismiss: (id: string) => void;\n position: ToastPosition;\n}\n\nfunction positionClass(position: ToastPosition): string {\n switch (position) {\n case \"top-left\":\n return styles.positionTopLeft;\n case \"top-center\":\n return styles.positionTopCenter;\n case \"bottom-right\":\n return styles.positionBottomRight;\n case \"bottom-left\":\n return styles.positionBottomLeft;\n case \"bottom-center\":\n return styles.positionBottomCenter;\n case \"top-right\":\n default:\n return styles.positionTopRight;\n }\n}\n\n/**\n * The portalled stack, and the page's live region for toasts.\n *\n * `aria-atomic=\"false\"` matters: with `\"true\"` a screen reader re-reads the\n * **entire** stack on every change, so the third toast of a batch is announced as\n * all three, and dismissing one re-announces the survivors. `\"false\"` announces only\n * the node that was added, which is what the user needs.\n *\n * The stack stays the live region rather than delegating to `useAnnounce`, because\n * routing it through the shared announcer would put the same text in the document\n * twice — once visible, once hidden — and every `getByText(\"Salvo\")` in a consuming\n * app's test suite would start matching two nodes. The announcer is for messages\n * that have no on-screen home; a toast has one.\n */\nfunction ToastContainer({ toasts, onDismiss, position }: ContainerProps) {\n if (typeof document === \"undefined\") return null;\n return createPortal(\n <div\n className={cn(styles.container, positionClass(position))}\n aria-live=\"polite\"\n aria-atomic=\"false\"\n >\n {toasts.map((toast) => (\n <ToastItem key={toast.id} toast={toast} onDismiss={onDismiss} />\n ))}\n </div>,\n document.body,\n );\n}\n\ninterface ItemProps {\n toast: ToastEntry;\n onDismiss: (id: string) => void;\n}\n\n/**\n * One toast.\n *\n * No `role=\"status\"` of its own: it already lives inside the container's live\n * region, and a live region nested in a live region is announced twice by some\n * screen readers.\n */\nfunction ToastItem({ toast, onDismiss }: ItemProps) {\n useEffect(() => {\n if (!toast.duration) return;\n const timer = setTimeout(() => onDismiss(toast.id), toast.duration);\n return () => clearTimeout(timer);\n }, [toast.id, toast.duration, onDismiss]);\n\n return (\n <div className={cn(styles.toast, styles[toast.variant])}>\n <div>\n {toast.title && <p className={styles.title}>{toast.title}</p>}\n {toast.description && <p className={styles.description}>{toast.description}</p>}\n </div>\n <button\n type=\"button\"\n className={styles.close}\n aria-label=\"Fechar notificação\"\n onClick={() => onDismiss(toast.id)}\n >\n <CloseIcon />\n </button>\n </div>\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ let e=require("react");var t=7e3,n={position:`absolute`,width:`1px`,height:`1px`,margin:`-1px`,padding:`0`,border:`0`,overflow:`hidden`,clip:`rect(0 0 0 0)`,clipPath:`inset(50%)`,whiteSpace:`nowrap`},r=null,i=new Set;function a(e){let t=document.createElement(`div`);return e===`polite`&&t.setAttribute(`role`,`status`),t.setAttribute(`aria-live`,e),t.setAttribute(`aria-atomic`,`true`),t.setAttribute(`data-tempest-announcer`,e),Object.assign(t.style,n),document.body.appendChild(t),t}function o(){return typeof document>`u`?null:r&&r.polite.isConnected&&r.assertive.isConnected?r:(r?.polite.remove(),r?.assertive.remove(),r={polite:a(`polite`),assertive:a(`assertive`)},r)}function s(e,n=`polite`){if(!e)return;let r=o();if(!r)return;let a=n===`assertive`?r.assertive:r.polite;a.replaceChildren();let s=document.createElement(`div`);s.textContent=e,a.appendChild(s);let c=setTimeout(()=>{i.delete(c),s.isConnected&&s.remove()},t);i.add(c)}function c(){for(let e of i)clearTimeout(e);i.clear(),r?.polite.remove(),r?.assertive.remove(),r=null}function l(){return(0,e.useEffect)(()=>{o()},[]),(0,e.useCallback)((e,t=`polite`)=>s(e,t),[])}exports.announce=s,exports.clearAnnouncer=c,exports.useAnnounce=l;
2
+ //# sourceMappingURL=use-announce.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-announce.cjs","names":[],"sources":["../../src/hooks/use-announce.ts"],"sourcesContent":["import { useCallback, useEffect } from \"react\";\n\n/**\n * How urgently a screen reader should interrupt.\n *\n * `\"polite\"` waits for a pause in what is being read. `\"assertive\"` cuts in\n * immediately, which is right for an error the user must act on and wrong for\n * everything else — an assertive announcement can truncate the sentence the user\n * was in the middle of.\n */\nexport type AnnouncePoliteness = \"polite\" | \"assertive\";\n\n/** How long an announcement stays in the DOM before it is cleaned up. */\nconst CLEAR_AFTER_MS = 7_000;\n\n/**\n * Inline styles that hide an element visually while leaving it readable.\n *\n * Inline and not a CSS module on purpose: these nodes are created imperatively by\n * a hook, and a hook that only works when the app remembered to import\n * `tempest-react-sdk/styles.css` would drop two visible, unstyled paragraphs into\n * the page — a visual bug caused by an accessibility feature.\n */\nconst HIDDEN_STYLE: Partial<CSSStyleDeclaration> = {\n position: \"absolute\",\n width: \"1px\",\n height: \"1px\",\n margin: \"-1px\",\n padding: \"0\",\n border: \"0\",\n overflow: \"hidden\",\n clip: \"rect(0 0 0 0)\",\n clipPath: \"inset(50%)\",\n whiteSpace: \"nowrap\",\n};\n\ninterface Regions {\n polite: HTMLElement;\n assertive: HTMLElement;\n}\n\nlet regions: Regions | null = null;\nconst timers = new Set<ReturnType<typeof setTimeout>>();\n\n/**\n * Build one live region.\n *\n * The polite region carries `role=\"status\"`; the assertive one carries only\n * `aria-live=\"assertive\"`. `role=\"alert\"` is deliberately **not** used: these\n * regions live in the document for the page's whole life, and an empty element\n * claiming to be an alert is both a lie about its content and a trap for every\n * `getByRole(\"alert\")` in a consuming app's test suite, which would suddenly match\n * two nodes. A bare `aria-live` region is announced by every screen reader that\n * supports live regions at all.\n *\n * @param politeness - Which region to build.\n * @returns The region, already appended to `<body>`.\n */\nfunction createRegion(politeness: AnnouncePoliteness): HTMLElement {\n const node = document.createElement(\"div\");\n if (politeness === \"polite\") node.setAttribute(\"role\", \"status\");\n node.setAttribute(\"aria-live\", politeness);\n node.setAttribute(\"aria-atomic\", \"true\");\n node.setAttribute(\"data-tempest-announcer\", politeness);\n Object.assign(node.style, HIDDEN_STYLE);\n document.body.appendChild(node);\n return node;\n}\n\n/**\n * Get the two shared live regions, creating them on first use.\n *\n * **Two regions, not one with a switchable `aria-live`.** Politeness is a property\n * of the region, read when the assistive technology first registers it — flipping\n * the attribute later is honoured by some screen readers, ignored by others, and\n * in the worst case drops the announcement entirely. Two regions that never change\n * are the only version that behaves the same everywhere.\n *\n * **Shared, not one per component.** Every live region on the page is polled;\n * several of them mutating at once is how announcements get dropped or doubled.\n * One pair per document keeps the order deterministic.\n *\n * A pair is replaced as a **pair**, discarding the old one: if only one region was\n * torn out of the DOM (a router that replaced `<body>`, a micro-frontend unmount),\n * keeping its still-attached sibling would leave two regions of that politeness on\n * the page — the exact duplication the shared pair exists to prevent.\n *\n * @returns The polite and assertive regions, or `null` outside a document.\n */\nfunction ensureRegions(): Regions | null {\n if (typeof document === \"undefined\") return null;\n if (regions && regions.polite.isConnected && regions.assertive.isConnected) return regions;\n regions?.polite.remove();\n regions?.assertive.remove();\n regions = { polite: createRegion(\"polite\"), assertive: createRegion(\"assertive\") };\n return regions;\n}\n\n/**\n * Announce a message to screen readers, from anywhere — a hook, an event handler,\n * a plain function outside React.\n *\n * ## Why the same string announces twice\n *\n * Screen readers announce a live region when its **content changes**. Writing the\n * same text again is not a change, so \"Item removido\" twice in a row is read once —\n * the classic reason these announcers are quietly broken. Instead of mutating text,\n * every call replaces the region's child with a **new element**. The DOM mutation is\n * real even when the string is identical, so the second announcement happens, and\n * the reader hears the exact message with no padding characters bolted on.\n *\n * @param message - Text to read out. Empty strings are ignored.\n * @param politeness - `\"polite\"` (default) or `\"assertive\"`.\n *\n * @example\n * announce(`${count} pedidos encontrados`);\n * announce(\"Falha ao salvar\", \"assertive\");\n */\nexport function announce(message: string, politeness: AnnouncePoliteness = \"polite\"): void {\n if (!message) return;\n const pair = ensureRegions();\n if (!pair) return;\n\n const region = politeness === \"assertive\" ? pair.assertive : pair.polite;\n region.replaceChildren();\n const item = document.createElement(\"div\");\n item.textContent = message;\n region.appendChild(item);\n\n const timer = setTimeout(() => {\n timers.delete(timer);\n if (item.isConnected) item.remove();\n }, CLEAR_AFTER_MS);\n timers.add(timer);\n}\n\n/**\n * Remove the shared regions and cancel pending cleanups.\n *\n * For test teardown and for a micro-frontend being unmounted from a page it does\n * not own. Regular apps never need it — two empty hidden `div`s cost nothing, and\n * tearing them down while another component still announces would lose messages.\n */\nexport function clearAnnouncer(): void {\n for (const timer of timers) clearTimeout(timer);\n timers.clear();\n regions?.polite.remove();\n regions?.assertive.remove();\n regions = null;\n}\n\n/**\n * Announce transient messages to screen readers through one shared live region\n * pair.\n *\n * Reach for this when something happened that a sighted user can see and a screen\n * reader user cannot: a filter narrowed a list, a row saved, a copy succeeded, an\n * upload failed. It is **not** for content that is already on screen inside a\n * region with a role — a status pill, a toast that renders as `role=\"status\"`,\n * a form error tied to its input — announcing those again reads them twice.\n *\n * !!! warning \"Never wrap streaming text in a live region\"\n * A live region over text that grows token by token makes the reader start the\n * whole answer again on every token. Announce the **edges** instead — \"gerando\n * resposta\" and \"resposta concluída\" — and leave the transcript in a plain\n * `role=\"log\"` the user reads at their own pace. `AIChat` does exactly that.\n *\n * Mounting the hook creates the regions even before the first message. That is not\n * tidiness: a live region inserted into the DOM in the same frame as its first\n * content routinely loses that announcement, because the assistive technology has\n * to have registered the region before it can notice a change inside it.\n *\n * @returns A stable `announce(message, politeness?)` function.\n *\n * @example\n * const announce = useAnnounce();\n *\n * function onFilter(rows: Row[]) {\n * announce(`${rows.length} resultados`);\n * }\n */\nexport function useAnnounce(): (message: string, politeness?: AnnouncePoliteness) => void {\n useEffect(() => {\n ensureRegions();\n }, []);\n\n return useCallback(\n (message: string, politeness: AnnouncePoliteness = \"polite\") =>\n announce(message, politeness),\n [],\n );\n}\n"],"mappings":"uBAaA,IAAM,EAAiB,IAUjB,EAA6C,CAC/C,SAAU,WACV,MAAO,MACP,OAAQ,MACR,OAAQ,OACR,QAAS,IACT,OAAQ,IACR,SAAU,SACV,KAAM,gBACN,SAAU,aACV,WAAY,QAChB,EAOI,EAA0B,KACxB,EAAS,IAAI,IAgBnB,SAAS,EAAa,EAA6C,CAC/D,IAAM,EAAO,SAAS,cAAc,KAAK,EAOzC,OANI,IAAe,UAAU,EAAK,aAAa,OAAQ,QAAQ,EAC/D,EAAK,aAAa,YAAa,CAAU,EACzC,EAAK,aAAa,cAAe,MAAM,EACvC,EAAK,aAAa,yBAA0B,CAAU,EACtD,OAAO,OAAO,EAAK,MAAO,CAAY,EACtC,SAAS,KAAK,YAAY,CAAI,EACvB,CACX,CAsBA,SAAS,GAAgC,CAMrC,OALI,OAAO,SAAa,IAAoB,KACxC,GAAW,EAAQ,OAAO,aAAe,EAAQ,UAAU,YAAoB,GACnF,GAAS,OAAO,OAAO,EACvB,GAAS,UAAU,OAAO,EAC1B,EAAU,CAAE,OAAQ,EAAa,QAAQ,EAAG,UAAW,EAAa,WAAW,CAAE,EAC1E,EACX,CAsBA,SAAgB,EAAS,EAAiB,EAAiC,SAAgB,CACvF,GAAI,CAAC,EAAS,OACd,IAAM,EAAO,EAAc,EAC3B,GAAI,CAAC,EAAM,OAEX,IAAM,EAAS,IAAe,YAAc,EAAK,UAAY,EAAK,OAClE,EAAO,gBAAgB,EACvB,IAAM,EAAO,SAAS,cAAc,KAAK,EACzC,EAAK,YAAc,EACnB,EAAO,YAAY,CAAI,EAEvB,IAAM,EAAQ,eAAiB,CAC3B,EAAO,OAAO,CAAK,EACf,EAAK,aAAa,EAAK,OAAO,CACtC,EAAG,CAAc,EACjB,EAAO,IAAI,CAAK,CACpB,CASA,SAAgB,GAAuB,CACnC,IAAK,IAAM,KAAS,EAAQ,aAAa,CAAK,EAC9C,EAAO,MAAM,EACb,GAAS,OAAO,OAAO,EACvB,GAAS,UAAU,OAAO,EAC1B,EAAU,IACd,CAgCA,SAAgB,GAA0E,CAKtF,OAJA,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAc,CAClB,EAAG,CAAC,CAAC,GAEL,EAAA,EAAA,YAAA,EACK,EAAiB,EAAiC,WAC/C,EAAS,EAAS,CAAU,EAChC,CAAC,CACL,CACJ"}