tempest-react-sdk 0.50.0 → 0.51.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 (45) hide show
  1. package/dist/audio/sfx-pool.cjs +1 -1
  2. package/dist/audio/sfx-pool.cjs.map +1 -1
  3. package/dist/audio/sfx-pool.js +36 -36
  4. package/dist/audio/sfx-pool.js.map +1 -1
  5. package/dist/components/BarList/BarList.cjs +1 -1
  6. package/dist/components/BarList/BarList.cjs.map +1 -1
  7. package/dist/components/BarList/BarList.js +6 -1
  8. package/dist/components/BarList/BarList.js.map +1 -1
  9. package/dist/components/BarList/bar-list-model.cjs +1 -1
  10. package/dist/components/BarList/bar-list-model.cjs.map +1 -1
  11. package/dist/components/BarList/bar-list-model.js +8 -8
  12. package/dist/components/BarList/bar-list-model.js.map +1 -1
  13. package/dist/components/Markdown/markdown-parse.cjs +5 -5
  14. package/dist/components/Markdown/markdown-parse.cjs.map +1 -1
  15. package/dist/components/Markdown/markdown-parse.js +66 -63
  16. package/dist/components/Markdown/markdown-parse.js.map +1 -1
  17. package/dist/components/Sidebar/Sidebar.cjs +1 -1
  18. package/dist/components/Sidebar/Sidebar.cjs.map +1 -1
  19. package/dist/components/Sidebar/Sidebar.js +25 -45
  20. package/dist/components/Sidebar/Sidebar.js.map +1 -1
  21. package/dist/components/Sidebar/Sidebar.module.cjs.map +1 -1
  22. package/dist/components/Sidebar/Sidebar.module.js.map +1 -1
  23. package/dist/components/Sidebar/SidebarEntry.cjs +2 -0
  24. package/dist/components/Sidebar/SidebarEntry.cjs.map +1 -0
  25. package/dist/components/Sidebar/SidebarEntry.js +40 -0
  26. package/dist/components/Sidebar/SidebarEntry.js.map +1 -0
  27. package/dist/http/api-client.cjs +1 -1
  28. package/dist/http/api-client.cjs.map +1 -1
  29. package/dist/http/api-client.js +84 -71
  30. package/dist/http/api-client.js.map +1 -1
  31. package/dist/http/timeout.cjs +2 -0
  32. package/dist/http/timeout.cjs.map +1 -0
  33. package/dist/http/timeout.js +22 -0
  34. package/dist/http/timeout.js.map +1 -0
  35. package/dist/icons/material-symbols.cjs.map +1 -1
  36. package/dist/icons/material-symbols.js.map +1 -1
  37. package/dist/styles.css +1 -1
  38. package/dist/tempest-react-sdk.d.ts +68 -6
  39. package/dist/utils/csv.cjs.map +1 -1
  40. package/dist/utils/csv.js.map +1 -1
  41. package/dist/vite/tempest-pwa-dev-sw.cjs +1 -1
  42. package/dist/vite/tempest-pwa-dev-sw.cjs.map +1 -1
  43. package/dist/vite/tempest-pwa-dev-sw.js +49 -41
  44. package/dist/vite/tempest-pwa-dev-sw.js.map +1 -1
  45. package/package.json +1 -1
@@ -1,90 +1,93 @@
1
1
  import { safeImageUrl as e, safeLinkUrl as t } from "./markdown-url.js";
2
2
  //#region src/components/Markdown/markdown-parse.ts
3
- var n = /^\s{0,3}(`{3,}|~{3,})\s*([\w+-]*)\s*$/, r = /^\s{0,3}(#{1,6})\s+(.*?)\s*#*\s*$/, i = /^\s{0,3}([-*_])\s*(?:\1\s*){2,}$/, a = /^\s{0,3}>\s?(.*)$/, o = /^(\s*)([-*+])\s+(.*)$/, s = /^(\s*)(\d{1,9})[.)]\s+(.*)$/, c = /^\s{0,3}\|[\s|:-]*$|^\s{0,3}:?-+:?(\s*\|\s*:?-+:?)+\s*\|?\s*$/;
4
- function l(e) {
5
- return u(e.replace(/\r\n?/g, "\n").split("\n"));
6
- }
3
+ var n = /^\s{0,3}(`{3,}|~{3,})\s*([\w+-]*)\s*$/, r = /^\s{0,3}(#{1,6})\s+(.*?)\s*#*\s*$/, i = /^\s{0,3}([-*_])\s*(?:\1\s*){2,}$/, a = /^\s{0,3}>\s?(.*)$/, o = /^(\s*)([-*+])\s+(.*)$/, s = /^(\s*)(\d{1,9})[.)]\s+(.*)$/, c = / {2,}$/, l = /^\s{0,3}\|[\s|:-]*$|^\s{0,3}:?-+:?(\s*\|\s*:?-+:?)+\s*\|?\s*$/;
7
4
  function u(e) {
8
- let t = [], l = 0;
9
- for (; l < e.length;) {
10
- let f = e[l];
11
- if (f.trim() === "") {
12
- l += 1;
5
+ return d(e.replace(/\r\n?/g, "\n").split("\n"));
6
+ }
7
+ function d(e) {
8
+ let t = [], u = 0;
9
+ for (; u < e.length;) {
10
+ let m = e[u];
11
+ if (m.trim() === "") {
12
+ u += 1;
13
13
  continue;
14
14
  }
15
- let m = n.exec(f);
16
- if (m) {
17
- let n = m[1][0], r = [];
18
- for (l += 1; l < e.length && !RegExp(`^\\s{0,3}${n}{3,}\\s*$`).test(e[l]);) r.push(e[l]), l += 1;
19
- l += 1, t.push({
15
+ let g = n.exec(m);
16
+ if (g) {
17
+ let n = g[1][0], r = [];
18
+ for (u += 1; u < e.length && !RegExp(`^\\s{0,3}${n}{3,}\\s*$`).test(e[u]);) r.push(e[u]), u += 1;
19
+ u += 1, t.push({
20
20
  type: "code",
21
- language: m[2] || null,
21
+ language: g[2] || null,
22
22
  value: r.join("\n")
23
23
  });
24
24
  continue;
25
25
  }
26
- let g = r.exec(f);
27
- if (g) {
26
+ let v = r.exec(m);
27
+ if (v) {
28
28
  t.push({
29
29
  type: "heading",
30
- level: g[1].length,
31
- children: h(g[2])
32
- }), l += 1;
30
+ level: v[1].length,
31
+ children: _(v[2])
32
+ }), u += 1;
33
33
  continue;
34
34
  }
35
- if (i.test(f)) {
36
- t.push({ type: "rule" }), l += 1;
35
+ if (i.test(m)) {
36
+ t.push({ type: "rule" }), u += 1;
37
37
  continue;
38
38
  }
39
- if (a.test(f)) {
39
+ if (a.test(m)) {
40
40
  let n = [];
41
- for (; l < e.length;) {
42
- let t = a.exec(e[l]);
41
+ for (; u < e.length;) {
42
+ let t = a.exec(e[u]);
43
43
  if (t) {
44
- n.push(t[1]), l += 1;
44
+ n.push(t[1]), u += 1;
45
45
  continue;
46
46
  }
47
- if (e[l].trim() === "") break;
48
- n.push(e[l]), l += 1;
47
+ if (e[u].trim() === "") break;
48
+ n.push(e[u]), u += 1;
49
49
  }
50
50
  t.push({
51
51
  type: "quote",
52
- children: u(n)
52
+ children: d(n)
53
53
  });
54
54
  continue;
55
55
  }
56
- if (o.test(f) || s.test(f)) {
57
- let [n, r] = d(e, l);
58
- t.push(n), l = r;
56
+ if (o.test(m) || s.test(m)) {
57
+ let [n, r] = p(e, u);
58
+ t.push(n), u = r;
59
59
  continue;
60
60
  }
61
- if (l + 1 < e.length && f.includes("|") && c.test(e[l + 1])) {
62
- let [n, r] = p(e, l);
63
- t.push(n), l = r;
61
+ if (u + 1 < e.length && m.includes("|") && l.test(e[u + 1])) {
62
+ let [n, r] = h(e, u);
63
+ t.push(n), u = r;
64
64
  continue;
65
65
  }
66
- let _ = [];
67
- for (; l < e.length && e[l].trim() !== "";) {
68
- let t = e[l];
66
+ let y = [], b = [];
67
+ for (; u < e.length && e[u].trim() !== "";) {
68
+ let t = e[u];
69
69
  if (r.test(t) || i.test(t) || n.test(t) || a.test(t) || o.test(t) || s.test(t)) break;
70
- _.push(t.trim()), l += 1;
70
+ y.push(t.trim()), b.push(c.test(t)), u += 1;
71
71
  }
72
- _.length > 0 && t.push({
72
+ y.length > 0 && t.push({
73
73
  type: "paragraph",
74
- children: h(_.join("\n"))
74
+ children: _(f(y, b))
75
75
  });
76
76
  }
77
77
  return t;
78
78
  }
79
- function d(e, t) {
80
- let n = o.exec(e[t]), r = s.exec(e[t]), i = r !== null, a = ((r ?? n)?.[1] ?? "").length, c = i ? Number(r?.[2] ?? 1) : 1, l = [], d = [], f = t, p = () => {
81
- d.length !== 0 && (l.push(u(d)), d = []);
79
+ function f(e, t) {
80
+ return e.map((n, r) => t[r] && r < e.length - 1 ? `${n} ` : n).join("\n");
81
+ }
82
+ function p(e, t) {
83
+ let n = o.exec(e[t]), r = s.exec(e[t]), i = r !== null, a = ((r ?? n)?.[1] ?? "").length, c = i ? Number(r?.[2] ?? 1) : 1, l = [], u = [], f = t, p = () => {
84
+ u.length !== 0 && (l.push(d(u)), u = []);
82
85
  };
83
86
  for (; f < e.length;) {
84
87
  let t = e[f];
85
88
  if (t.trim() === "") {
86
89
  if (f + 1 < e.length && e[f + 1].trim() !== "") {
87
- d.push(""), f += 1;
90
+ u.push(""), f += 1;
88
91
  continue;
89
92
  }
90
93
  break;
@@ -92,15 +95,15 @@ function d(e, t) {
92
95
  let n = o.exec(t), r = s.exec(t), c = n ?? r;
93
96
  if (c && c[1].length <= a) {
94
97
  if (!(i ? r : n)) break;
95
- p(), d.push(c[3]), f += 1;
98
+ p(), u.push(c[3]), f += 1;
96
99
  continue;
97
100
  }
98
101
  if (c) {
99
- d.push(t.slice(a)), f += 1;
102
+ u.push(t.slice(a)), f += 1;
100
103
  continue;
101
104
  }
102
105
  if (t.search(/\S/) > a) {
103
- d.push(t.trim()), f += 1;
106
+ u.push(t.trim()), f += 1;
104
107
  continue;
105
108
  }
106
109
  break;
@@ -112,7 +115,7 @@ function d(e, t) {
112
115
  items: l
113
116
  }, f];
114
117
  }
115
- function f(e) {
118
+ function m(e) {
116
119
  let t = e.trim().replace(/^\|/, "").replace(/\|$/, ""), n = [], r = "";
117
120
  for (let e = 0; e < t.length; e += 1) {
118
121
  if (t[e] === "\\" && t[e + 1] === "|") {
@@ -127,20 +130,20 @@ function f(e) {
127
130
  }
128
131
  return n.push(r), n.map((e) => e.trim());
129
132
  }
130
- function p(e, t) {
131
- let n = f(e[t]), r = f(e[t + 1]).map((e) => {
133
+ function h(e, t) {
134
+ let n = m(e[t]), r = m(e[t + 1]).map((e) => {
132
135
  let t = e.startsWith(":"), n = e.endsWith(":");
133
136
  return t && n ? "center" : n ? "right" : t ? "left" : null;
134
137
  }), i = [], a = t + 2;
135
- for (; a < e.length && e[a].trim() !== "" && e[a].includes("|");) i.push(f(e[a]).map(h)), a += 1;
138
+ for (; a < e.length && e[a].trim() !== "" && e[a].includes("|");) i.push(m(e[a]).map(_)), a += 1;
136
139
  return [{
137
140
  type: "table",
138
141
  align: r,
139
- head: n.map(h),
142
+ head: n.map(_),
140
143
  rows: i
141
144
  }, a];
142
145
  }
143
- var m = [
146
+ var g = [
144
147
  {
145
148
  pattern: /^!\[([^\]]*)\]\(((?:[^()\s]|\([^()\s]*\))+)(?:\s+"[^"]*")?\)/,
146
149
  build: (t) => {
@@ -158,7 +161,7 @@ var m = [
158
161
  {
159
162
  pattern: /^\[([^\]]*)\]\(((?:[^()\s]|\([^()\s]*\))+)(?:\s+"[^"]*")?\)/,
160
163
  build: (e) => {
161
- let n = t(e[2]), r = h(e[1]);
164
+ let n = t(e[2]), r = _(e[1]);
162
165
  return n ? {
163
166
  type: "link",
164
167
  href: n,
@@ -180,35 +183,35 @@ var m = [
180
183
  pattern: /^\*\*([\s\S]+?)\*\*/,
181
184
  build: (e) => ({
182
185
  type: "strong",
183
- children: h(e[1])
186
+ children: _(e[1])
184
187
  })
185
188
  },
186
189
  {
187
190
  pattern: /^__([\s\S]+?)__/,
188
191
  build: (e) => ({
189
192
  type: "strong",
190
- children: h(e[1])
193
+ children: _(e[1])
191
194
  })
192
195
  },
193
196
  {
194
197
  pattern: /^~~([\s\S]+?)~~/,
195
198
  build: (e) => ({
196
199
  type: "del",
197
- children: h(e[1])
200
+ children: _(e[1])
198
201
  })
199
202
  },
200
203
  {
201
204
  pattern: /^\*([^*\n]+)\*/,
202
205
  build: (e) => ({
203
206
  type: "em",
204
- children: h(e[1])
207
+ children: _(e[1])
205
208
  })
206
209
  },
207
210
  {
208
211
  pattern: /^_([^_\n]+)_/,
209
212
  build: (e) => ({
210
213
  type: "em",
211
- children: h(e[1])
214
+ children: _(e[1])
212
215
  })
213
216
  },
214
217
  {
@@ -237,7 +240,7 @@ var m = [
237
240
  build: () => ({ type: "break" })
238
241
  }
239
242
  ];
240
- function h(e) {
243
+ function _(e) {
241
244
  let t = [], n = "", r = 0, i = () => {
242
245
  n && t.push({
243
246
  type: "text",
@@ -250,7 +253,7 @@ function h(e) {
250
253
  continue;
251
254
  }
252
255
  let a = e.slice(r), o = !1;
253
- for (let e of m) {
256
+ for (let e of g) {
254
257
  let n = e.pattern.exec(a);
255
258
  if (!n) continue;
256
259
  let s = e.build(n);
@@ -264,6 +267,6 @@ function h(e) {
264
267
  return i(), t;
265
268
  }
266
269
  //#endregion
267
- export { h as parseInline, l as parseMarkdown };
270
+ export { _ as parseInline, u as parseMarkdown };
268
271
 
269
272
  //# sourceMappingURL=markdown-parse.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-parse.js","names":[],"sources":["../../../src/components/Markdown/markdown-parse.ts"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — a Markdown block parser is a state\n * machine over line kinds: paragraph, heading, fence, list, quote, table and\n * thematic break each need to see the lines around them to know where they end.\n * parseBlocks is that loop, and cutting it into per-kind functions would hand each\n * one the same lookahead cursor.\n */\n// A small Markdown parser for the subset an app actually renders from untrusted\n// text: comments, ticket bodies, release notes, a product description.\n//\n// It produces a node tree, never an HTML string, and the renderer turns that into\n// React elements. No `dangerouslySetInnerHTML` exists anywhere in this component —\n// which is the property that makes rendering somebody else's Markdown safe, and it\n// is structural rather than a promise about escaping.\n//\n// Raw HTML in the input is therefore not \"sanitized\", it is **text**: `<script>` in\n// a comment renders as the four characters somebody typed. That is the whole point,\n// and it is also the documented limit — this is not a CommonMark implementation and\n// does not try to be.\n\nimport { safeImageUrl, safeLinkUrl } from \"./markdown-url\";\n\n/** Inline content. */\nexport type MarkdownInline =\n | { type: \"text\"; value: string }\n | { type: \"strong\"; children: MarkdownInline[] }\n | { type: \"em\"; children: MarkdownInline[] }\n | { type: \"del\"; children: MarkdownInline[] }\n | { type: \"code\"; value: string }\n | { type: \"link\"; href: string; children: MarkdownInline[] }\n | { type: \"image\"; src: string; alt: string }\n | { type: \"break\" };\n\n/** Column alignment of a table, from the delimiter row. */\nexport type MarkdownAlign = \"left\" | \"center\" | \"right\" | null;\n\n/** Block content. */\nexport type MarkdownBlock =\n | { type: \"heading\"; level: number; children: MarkdownInline[] }\n | { type: \"paragraph\"; children: MarkdownInline[] }\n | { type: \"code\"; language: string | null; value: string }\n | { type: \"quote\"; children: MarkdownBlock[] }\n | { type: \"list\"; ordered: boolean; start: number; items: MarkdownBlock[][] }\n | {\n type: \"table\";\n align: MarkdownAlign[];\n head: MarkdownInline[][];\n rows: MarkdownInline[][][];\n }\n | { type: \"rule\" };\n\nconst FENCE = /^\\s{0,3}(`{3,}|~{3,})\\s*([\\w+-]*)\\s*$/;\nconst HEADING = /^\\s{0,3}(#{1,6})\\s+(.*?)\\s*#*\\s*$/;\nconst RULE = /^\\s{0,3}([-*_])\\s*(?:\\1\\s*){2,}$/;\nconst QUOTE = /^\\s{0,3}>\\s?(.*)$/;\nconst BULLET = /^(\\s*)([-*+])\\s+(.*)$/;\nconst ORDERED = /^(\\s*)(\\d{1,9})[.)]\\s+(.*)$/;\n/**\n * A table's delimiter row.\n *\n * The `|` is required somewhere in the line, and that is what tells a one-column\n * table (`| --- |`) apart from a thematic break (`---`) — a single column is a\n * legitimate table, and the first version of this regex demanded two.\n */\nconst TABLE_DELIMITER = /^\\s{0,3}\\|[\\s|:-]*$|^\\s{0,3}:?-+:?(\\s*\\|\\s*:?-+:?)+\\s*\\|?\\s*$/;\n\n/**\n * Parse a Markdown document into blocks.\n *\n * @param source - Raw Markdown.\n * @returns Block nodes in document order.\n */\nexport function parseMarkdown(source: string): MarkdownBlock[] {\n const lines = source.replace(/\\r\\n?/g, \"\\n\").split(\"\\n\");\n return parseBlocks(lines);\n}\n\n/** Parse a run of lines into blocks. */\nfunction parseBlocks(lines: string[]): MarkdownBlock[] {\n const blocks: MarkdownBlock[] = [];\n let i = 0;\n\n while (i < lines.length) {\n const line = lines[i];\n\n if (line.trim() === \"\") {\n i += 1;\n continue;\n }\n\n const fence = FENCE.exec(line);\n if (fence) {\n const marker = fence[1][0];\n const body: string[] = [];\n i += 1;\n while (i < lines.length && !new RegExp(`^\\\\s{0,3}${marker}{3,}\\\\s*$`).test(lines[i])) {\n body.push(lines[i]);\n i += 1;\n }\n // An unterminated fence still yields a code block: the alternative is to\n // render the rest of the document as prose full of backticks.\n i += 1;\n blocks.push({ type: \"code\", language: fence[2] || null, value: body.join(\"\\n\") });\n continue;\n }\n\n const heading = HEADING.exec(line);\n if (heading) {\n blocks.push({\n type: \"heading\",\n level: heading[1].length,\n children: parseInline(heading[2]),\n });\n i += 1;\n continue;\n }\n\n if (RULE.test(line)) {\n blocks.push({ type: \"rule\" });\n i += 1;\n continue;\n }\n\n if (QUOTE.test(line)) {\n const inner: string[] = [];\n while (i < lines.length) {\n const match = QUOTE.exec(lines[i]);\n if (match) {\n inner.push(match[1]);\n i += 1;\n continue;\n }\n // A blank line ends the quote; a plain line continues it (lazy\n // continuation), which is how people actually write quotes.\n if (lines[i].trim() === \"\") break;\n inner.push(lines[i]);\n i += 1;\n }\n blocks.push({ type: \"quote\", children: parseBlocks(inner) });\n continue;\n }\n\n if (BULLET.test(line) || ORDERED.test(line)) {\n const [list, next] = parseList(lines, i);\n blocks.push(list);\n i = next;\n continue;\n }\n\n if (i + 1 < lines.length && line.includes(\"|\") && TABLE_DELIMITER.test(lines[i + 1])) {\n const [table, next] = parseTable(lines, i);\n blocks.push(table);\n i = next;\n continue;\n }\n\n const paragraph: string[] = [];\n while (i < lines.length && lines[i].trim() !== \"\") {\n const current = lines[i];\n if (\n HEADING.test(current) ||\n RULE.test(current) ||\n FENCE.test(current) ||\n QUOTE.test(current) ||\n BULLET.test(current) ||\n ORDERED.test(current)\n ) {\n break;\n }\n paragraph.push(current.trim());\n i += 1;\n }\n if (paragraph.length > 0) {\n blocks.push({ type: \"paragraph\", children: parseInline(paragraph.join(\"\\n\")) });\n }\n }\n\n return blocks;\n}\n\n/**\n * Parse a list, including nested lists and multi-line items.\n *\n * Nesting is decided by indentation relative to the **first** item's marker, so a\n * list indented inside a quote or another list still reads correctly.\n *\n * @returns The list node and the index of the first line after it.\n */\nfunction parseList(lines: string[], start: number): [MarkdownBlock, number] {\n const bulletStart = BULLET.exec(lines[start]);\n const orderedStart = ORDERED.exec(lines[start]);\n // The two markers are mutually exclusive — `-*+` versus digits — so whichever\n // matched decides the list kind.\n const ordered = orderedStart !== null;\n const first = orderedStart ?? bulletStart;\n const baseIndent = (first?.[1] ?? \"\").length;\n const startNumber = ordered ? Number(orderedStart?.[2] ?? 1) : 1;\n\n const items: MarkdownBlock[][] = [];\n let buffer: string[] = [];\n let i = start;\n\n const flush = (): void => {\n if (buffer.length === 0) return;\n items.push(parseBlocks(buffer));\n buffer = [];\n };\n\n while (i < lines.length) {\n const line = lines[i];\n if (line.trim() === \"\") {\n // A single blank line inside a list is a loose item, not the end. Two in\n // a row end it, which is what a blank-then-paragraph document means.\n if (i + 1 < lines.length && lines[i + 1].trim() !== \"\") {\n buffer.push(\"\");\n i += 1;\n continue;\n }\n break;\n }\n const bullet = BULLET.exec(line);\n const numbered = ORDERED.exec(line);\n const match = bullet ?? numbered;\n\n if (match && match[1].length <= baseIndent) {\n const sameKind = ordered ? Boolean(numbered) : Boolean(bullet);\n if (!sameKind) break;\n flush();\n buffer.push(match[3]);\n i += 1;\n continue;\n }\n if (match) {\n // Deeper marker: keep the indentation so the recursive call sees a list.\n buffer.push(line.slice(baseIndent));\n i += 1;\n continue;\n }\n if (line.search(/\\S/) > baseIndent) {\n buffer.push(line.trim());\n i += 1;\n continue;\n }\n break;\n }\n flush();\n\n return [{ type: \"list\", ordered, start: startNumber, items }, i];\n}\n\n/** Split a table row on unescaped pipes. */\nfunction splitRow(line: string): string[] {\n const trimmed = line.trim().replace(/^\\|/, \"\").replace(/\\|$/, \"\");\n const cells: string[] = [];\n let current = \"\";\n for (let i = 0; i < trimmed.length; i += 1) {\n if (trimmed[i] === \"\\\\\" && trimmed[i + 1] === \"|\") {\n current += \"|\";\n i += 1;\n continue;\n }\n if (trimmed[i] === \"|\") {\n cells.push(current);\n current = \"\";\n continue;\n }\n current += trimmed[i];\n }\n cells.push(current);\n return cells.map((cell) => cell.trim());\n}\n\n/**\n * Parse a GFM pipe table.\n *\n * @returns The table node and the index of the first line after it.\n */\nfunction parseTable(lines: string[], start: number): [MarkdownBlock, number] {\n const head = splitRow(lines[start]);\n const align: MarkdownAlign[] = splitRow(lines[start + 1]).map((cell) => {\n const left = cell.startsWith(\":\");\n const right = cell.endsWith(\":\");\n if (left && right) return \"center\";\n if (right) return \"right\";\n if (left) return \"left\";\n return null;\n });\n\n const rows: MarkdownInline[][][] = [];\n let i = start + 2;\n while (i < lines.length && lines[i].trim() !== \"\" && lines[i].includes(\"|\")) {\n rows.push(splitRow(lines[i]).map(parseInline));\n i += 1;\n }\n\n return [{ type: \"table\", align, head: head.map(parseInline), rows }, i];\n}\n\n/** Inline delimiters, longest marker first so `**` wins over `*`. */\nconst INLINE_RULES: Array<{\n pattern: RegExp;\n build: (match: RegExpExecArray) => MarkdownInline | null;\n}> = [\n {\n pattern: /^!\\[([^\\]]*)\\]\\(((?:[^()\\s]|\\([^()\\s]*\\))+)(?:\\s+\"[^\"]*\")?\\)/,\n build: (match) => {\n const src = safeImageUrl(match[2]);\n return src ? { type: \"image\", src, alt: match[1] } : { type: \"text\", value: match[1] };\n },\n },\n {\n pattern: /^\\[([^\\]]*)\\]\\(((?:[^()\\s]|\\([^()\\s]*\\))+)(?:\\s+\"[^\"]*\")?\\)/,\n build: (match) => {\n const href = safeLinkUrl(match[2]);\n const children = parseInline(match[1]);\n // A rejected URL keeps the label as plain text: dropping the text too\n // would silently delete words somebody wrote.\n return href ? { type: \"link\", href, children } : { type: \"text\", value: match[1] };\n },\n },\n { pattern: /^`([^`]+)`/, build: (match) => ({ type: \"code\", value: match[1] }) },\n {\n pattern: /^\\*\\*([\\s\\S]+?)\\*\\*/,\n build: (match) => ({ type: \"strong\", children: parseInline(match[1]) }),\n },\n {\n pattern: /^__([\\s\\S]+?)__/,\n build: (match) => ({ type: \"strong\", children: parseInline(match[1]) }),\n },\n {\n pattern: /^~~([\\s\\S]+?)~~/,\n build: (match) => ({ type: \"del\", children: parseInline(match[1]) }),\n },\n {\n pattern: /^\\*([^*\\n]+)\\*/,\n build: (match) => ({ type: \"em\", children: parseInline(match[1]) }),\n },\n {\n pattern: /^_([^_\\n]+)_/,\n build: (match) => ({ type: \"em\", children: parseInline(match[1]) }),\n },\n {\n pattern: /^<((?:https?:\\/\\/|mailto:)[^>\\s]+)>/,\n build: (match) => {\n const href = safeLinkUrl(match[1]);\n return href\n ? { type: \"link\", href, children: [{ type: \"text\", value: match[1] }] }\n : { type: \"text\", value: match[1] };\n },\n },\n { pattern: /^ {2,}\\n/, build: () => ({ type: \"break\" }) },\n { pattern: /^\\\\\\n/, build: () => ({ type: \"break\" }) },\n];\n\n/**\n * Parse inline Markdown.\n *\n * A backslash escapes the next character, so `\\*not italic\\*` renders with the\n * asterisks. Anything that matches no rule is text — an unclosed `**` is two\n * asterisks, not a bold run to the end of the paragraph.\n *\n * @param source - One block's text.\n * @returns Inline nodes.\n */\nexport function parseInline(source: string): MarkdownInline[] {\n const nodes: MarkdownInline[] = [];\n let text = \"\";\n let i = 0;\n\n const flush = (): void => {\n if (text) nodes.push({ type: \"text\", value: text });\n text = \"\";\n };\n\n while (i < source.length) {\n if (source[i] === \"\\\\\" && i + 1 < source.length && source[i + 1] !== \"\\n\") {\n text += source[i + 1];\n i += 2;\n continue;\n }\n\n const rest = source.slice(i);\n let matched = false;\n for (const rule of INLINE_RULES) {\n const match = rule.pattern.exec(rest);\n if (!match) continue;\n const node = rule.build(match);\n if (!node) continue;\n flush();\n nodes.push(node);\n i += match[0].length;\n matched = true;\n break;\n }\n if (matched) continue;\n\n text += source[i];\n i += 1;\n }\n\n flush();\n return nodes;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"markdown-parse.js","names":[],"sources":["../../../src/components/Markdown/markdown-parse.ts"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — a Markdown block parser is a state\n * machine over line kinds: paragraph, heading, fence, list, quote, table and\n * thematic break each need to see the lines around them to know where they end.\n * parseBlocks is that loop, and cutting it into per-kind functions would hand each\n * one the same lookahead cursor.\n */\n// A small Markdown parser for the subset an app actually renders from untrusted\n// text: comments, ticket bodies, release notes, a product description.\n//\n// It produces a node tree, never an HTML string, and the renderer turns that into\n// React elements. No `dangerouslySetInnerHTML` exists anywhere in this component —\n// which is the property that makes rendering somebody else's Markdown safe, and it\n// is structural rather than a promise about escaping.\n//\n// Raw HTML in the input is therefore not \"sanitized\", it is **text**: `<script>` in\n// a comment renders as the four characters somebody typed. That is the whole point,\n// and it is also the documented limit — this is not a CommonMark implementation and\n// does not try to be.\n\nimport { safeImageUrl, safeLinkUrl } from \"./markdown-url\";\n\n/** Inline content. */\nexport type MarkdownInline =\n | { type: \"text\"; value: string }\n | { type: \"strong\"; children: MarkdownInline[] }\n | { type: \"em\"; children: MarkdownInline[] }\n | { type: \"del\"; children: MarkdownInline[] }\n | { type: \"code\"; value: string }\n | { type: \"link\"; href: string; children: MarkdownInline[] }\n | { type: \"image\"; src: string; alt: string }\n | { type: \"break\" };\n\n/** Column alignment of a table, from the delimiter row. */\nexport type MarkdownAlign = \"left\" | \"center\" | \"right\" | null;\n\n/** Block content. */\nexport type MarkdownBlock =\n | { type: \"heading\"; level: number; children: MarkdownInline[] }\n | { type: \"paragraph\"; children: MarkdownInline[] }\n | { type: \"code\"; language: string | null; value: string }\n | { type: \"quote\"; children: MarkdownBlock[] }\n | { type: \"list\"; ordered: boolean; start: number; items: MarkdownBlock[][] }\n | {\n type: \"table\";\n align: MarkdownAlign[];\n head: MarkdownInline[][];\n rows: MarkdownInline[][][];\n }\n | { type: \"rule\" };\n\nconst FENCE = /^\\s{0,3}(`{3,}|~{3,})\\s*([\\w+-]*)\\s*$/;\nconst HEADING = /^\\s{0,3}(#{1,6})\\s+(.*?)\\s*#*\\s*$/;\nconst RULE = /^\\s{0,3}([-*_])\\s*(?:\\1\\s*){2,}$/;\nconst QUOTE = /^\\s{0,3}>\\s?(.*)$/;\nconst BULLET = /^(\\s*)([-*+])\\s+(.*)$/;\nconst ORDERED = /^(\\s*)(\\d{1,9})[.)]\\s+(.*)$/;\nconst HARD_BREAK = / {2,}$/;\n/**\n * A table's delimiter row.\n *\n * The `|` is required somewhere in the line, and that is what tells a one-column\n * table (`| --- |`) apart from a thematic break (`---`) — a single column is a\n * legitimate table, and the first version of this regex demanded two.\n */\nconst TABLE_DELIMITER = /^\\s{0,3}\\|[\\s|:-]*$|^\\s{0,3}:?-+:?(\\s*\\|\\s*:?-+:?)+\\s*\\|?\\s*$/;\n\n/**\n * Parse a Markdown document into blocks.\n *\n * @param source - Raw Markdown.\n * @returns Block nodes in document order.\n */\nexport function parseMarkdown(source: string): MarkdownBlock[] {\n const lines = source.replace(/\\r\\n?/g, \"\\n\").split(\"\\n\");\n return parseBlocks(lines);\n}\n\n/** Parse a run of lines into blocks. */\nfunction parseBlocks(lines: string[]): MarkdownBlock[] {\n const blocks: MarkdownBlock[] = [];\n let i = 0;\n\n while (i < lines.length) {\n const line = lines[i];\n\n if (line.trim() === \"\") {\n i += 1;\n continue;\n }\n\n const fence = FENCE.exec(line);\n if (fence) {\n const marker = fence[1][0];\n const body: string[] = [];\n i += 1;\n while (i < lines.length && !new RegExp(`^\\\\s{0,3}${marker}{3,}\\\\s*$`).test(lines[i])) {\n body.push(lines[i]);\n i += 1;\n }\n // An unterminated fence still yields a code block: the alternative is to\n // render the rest of the document as prose full of backticks.\n i += 1;\n blocks.push({ type: \"code\", language: fence[2] || null, value: body.join(\"\\n\") });\n continue;\n }\n\n const heading = HEADING.exec(line);\n if (heading) {\n blocks.push({\n type: \"heading\",\n level: heading[1].length,\n children: parseInline(heading[2]),\n });\n i += 1;\n continue;\n }\n\n if (RULE.test(line)) {\n blocks.push({ type: \"rule\" });\n i += 1;\n continue;\n }\n\n if (QUOTE.test(line)) {\n const inner: string[] = [];\n while (i < lines.length) {\n const match = QUOTE.exec(lines[i]);\n if (match) {\n inner.push(match[1]);\n i += 1;\n continue;\n }\n // A blank line ends the quote; a plain line continues it (lazy\n // continuation), which is how people actually write quotes.\n if (lines[i].trim() === \"\") break;\n inner.push(lines[i]);\n i += 1;\n }\n blocks.push({ type: \"quote\", children: parseBlocks(inner) });\n continue;\n }\n\n if (BULLET.test(line) || ORDERED.test(line)) {\n const [list, next] = parseList(lines, i);\n blocks.push(list);\n i = next;\n continue;\n }\n\n if (i + 1 < lines.length && line.includes(\"|\") && TABLE_DELIMITER.test(lines[i + 1])) {\n const [table, next] = parseTable(lines, i);\n blocks.push(table);\n i = next;\n continue;\n }\n\n const paragraph: string[] = [];\n const hardBreaks: boolean[] = [];\n while (i < lines.length && lines[i].trim() !== \"\") {\n const current = lines[i];\n if (\n HEADING.test(current) ||\n RULE.test(current) ||\n FENCE.test(current) ||\n QUOTE.test(current) ||\n BULLET.test(current) ||\n ORDERED.test(current)\n ) {\n break;\n }\n paragraph.push(current.trim());\n hardBreaks.push(HARD_BREAK.test(current));\n i += 1;\n }\n if (paragraph.length > 0) {\n blocks.push({\n type: \"paragraph\",\n children: parseInline(joinParagraph(paragraph, hardBreaks)),\n });\n }\n }\n\n return blocks;\n}\n\n/**\n * Re-join the lines of a paragraph, keeping the two-space hard break.\n *\n * Every line is trimmed before it is parsed, because leading indentation is not\n * content and a trailing space is invisible noise in almost every line. The one\n * exception is the CommonMark hard break: two or more trailing spaces mean\n * \"break the line here\", so trimming first would delete the only mark that says\n * so and the ` \\n` inline rule could never match. The flag is recorded before\n * the trim and the marker put back here, on every line but the last — a break\n * after the final line of a paragraph would break into nothing.\n *\n * @param lines - The trimmed lines, in order.\n * @param hardBreaks - Whether each line ended in two or more spaces.\n * @returns The paragraph source the inline parser reads.\n */\nfunction joinParagraph(lines: readonly string[], hardBreaks: readonly boolean[]): string {\n return lines\n .map((line, index) => (hardBreaks[index] && index < lines.length - 1 ? `${line} ` : line))\n .join(\"\\n\");\n}\n\n/**\n * Parse a list, including nested lists and multi-line items.\n *\n * Nesting is decided by indentation relative to the **first** item's marker, so a\n * list indented inside a quote or another list still reads correctly.\n *\n * @returns The list node and the index of the first line after it.\n */\nfunction parseList(lines: string[], start: number): [MarkdownBlock, number] {\n const bulletStart = BULLET.exec(lines[start]);\n const orderedStart = ORDERED.exec(lines[start]);\n // The two markers are mutually exclusive — `-*+` versus digits — so whichever\n // matched decides the list kind.\n const ordered = orderedStart !== null;\n const first = orderedStart ?? bulletStart;\n const baseIndent = (first?.[1] ?? \"\").length;\n const startNumber = ordered ? Number(orderedStart?.[2] ?? 1) : 1;\n\n const items: MarkdownBlock[][] = [];\n let buffer: string[] = [];\n let i = start;\n\n const flush = (): void => {\n if (buffer.length === 0) return;\n items.push(parseBlocks(buffer));\n buffer = [];\n };\n\n while (i < lines.length) {\n const line = lines[i];\n if (line.trim() === \"\") {\n // A single blank line inside a list is a loose item, not the end. Two in\n // a row end it, which is what a blank-then-paragraph document means.\n if (i + 1 < lines.length && lines[i + 1].trim() !== \"\") {\n buffer.push(\"\");\n i += 1;\n continue;\n }\n break;\n }\n const bullet = BULLET.exec(line);\n const numbered = ORDERED.exec(line);\n const match = bullet ?? numbered;\n\n if (match && match[1].length <= baseIndent) {\n const sameKind = ordered ? Boolean(numbered) : Boolean(bullet);\n if (!sameKind) break;\n flush();\n buffer.push(match[3]);\n i += 1;\n continue;\n }\n if (match) {\n // Deeper marker: keep the indentation so the recursive call sees a list.\n buffer.push(line.slice(baseIndent));\n i += 1;\n continue;\n }\n if (line.search(/\\S/) > baseIndent) {\n buffer.push(line.trim());\n i += 1;\n continue;\n }\n break;\n }\n flush();\n\n return [{ type: \"list\", ordered, start: startNumber, items }, i];\n}\n\n/** Split a table row on unescaped pipes. */\nfunction splitRow(line: string): string[] {\n const trimmed = line.trim().replace(/^\\|/, \"\").replace(/\\|$/, \"\");\n const cells: string[] = [];\n let current = \"\";\n for (let i = 0; i < trimmed.length; i += 1) {\n if (trimmed[i] === \"\\\\\" && trimmed[i + 1] === \"|\") {\n current += \"|\";\n i += 1;\n continue;\n }\n if (trimmed[i] === \"|\") {\n cells.push(current);\n current = \"\";\n continue;\n }\n current += trimmed[i];\n }\n cells.push(current);\n return cells.map((cell) => cell.trim());\n}\n\n/**\n * Parse a GFM pipe table.\n *\n * @returns The table node and the index of the first line after it.\n */\nfunction parseTable(lines: string[], start: number): [MarkdownBlock, number] {\n const head = splitRow(lines[start]);\n const align: MarkdownAlign[] = splitRow(lines[start + 1]).map((cell) => {\n const left = cell.startsWith(\":\");\n const right = cell.endsWith(\":\");\n if (left && right) return \"center\";\n if (right) return \"right\";\n if (left) return \"left\";\n return null;\n });\n\n const rows: MarkdownInline[][][] = [];\n let i = start + 2;\n while (i < lines.length && lines[i].trim() !== \"\" && lines[i].includes(\"|\")) {\n rows.push(splitRow(lines[i]).map(parseInline));\n i += 1;\n }\n\n return [{ type: \"table\", align, head: head.map(parseInline), rows }, i];\n}\n\n/** Inline delimiters, longest marker first so `**` wins over `*`. */\nconst INLINE_RULES: Array<{\n pattern: RegExp;\n build: (match: RegExpExecArray) => MarkdownInline | null;\n}> = [\n {\n pattern: /^!\\[([^\\]]*)\\]\\(((?:[^()\\s]|\\([^()\\s]*\\))+)(?:\\s+\"[^\"]*\")?\\)/,\n build: (match) => {\n const src = safeImageUrl(match[2]);\n return src ? { type: \"image\", src, alt: match[1] } : { type: \"text\", value: match[1] };\n },\n },\n {\n pattern: /^\\[([^\\]]*)\\]\\(((?:[^()\\s]|\\([^()\\s]*\\))+)(?:\\s+\"[^\"]*\")?\\)/,\n build: (match) => {\n const href = safeLinkUrl(match[2]);\n const children = parseInline(match[1]);\n // A rejected URL keeps the label as plain text: dropping the text too\n // would silently delete words somebody wrote.\n return href ? { type: \"link\", href, children } : { type: \"text\", value: match[1] };\n },\n },\n { pattern: /^`([^`]+)`/, build: (match) => ({ type: \"code\", value: match[1] }) },\n {\n pattern: /^\\*\\*([\\s\\S]+?)\\*\\*/,\n build: (match) => ({ type: \"strong\", children: parseInline(match[1]) }),\n },\n {\n pattern: /^__([\\s\\S]+?)__/,\n build: (match) => ({ type: \"strong\", children: parseInline(match[1]) }),\n },\n {\n pattern: /^~~([\\s\\S]+?)~~/,\n build: (match) => ({ type: \"del\", children: parseInline(match[1]) }),\n },\n {\n pattern: /^\\*([^*\\n]+)\\*/,\n build: (match) => ({ type: \"em\", children: parseInline(match[1]) }),\n },\n {\n pattern: /^_([^_\\n]+)_/,\n build: (match) => ({ type: \"em\", children: parseInline(match[1]) }),\n },\n {\n pattern: /^<((?:https?:\\/\\/|mailto:)[^>\\s]+)>/,\n build: (match) => {\n const href = safeLinkUrl(match[1]);\n return href\n ? { type: \"link\", href, children: [{ type: \"text\", value: match[1] }] }\n : { type: \"text\", value: match[1] };\n },\n },\n { pattern: /^ {2,}\\n/, build: () => ({ type: \"break\" }) },\n { pattern: /^\\\\\\n/, build: () => ({ type: \"break\" }) },\n];\n\n/**\n * Parse inline Markdown.\n *\n * A backslash escapes the next character, so `\\*not italic\\*` renders with the\n * asterisks. Anything that matches no rule is text — an unclosed `**` is two\n * asterisks, not a bold run to the end of the paragraph.\n *\n * @param source - One block's text.\n * @returns Inline nodes.\n */\nexport function parseInline(source: string): MarkdownInline[] {\n const nodes: MarkdownInline[] = [];\n let text = \"\";\n let i = 0;\n\n const flush = (): void => {\n if (text) nodes.push({ type: \"text\", value: text });\n text = \"\";\n };\n\n while (i < source.length) {\n if (source[i] === \"\\\\\" && i + 1 < source.length && source[i + 1] !== \"\\n\") {\n text += source[i + 1];\n i += 2;\n continue;\n }\n\n const rest = source.slice(i);\n let matched = false;\n for (const rule of INLINE_RULES) {\n const match = rule.pattern.exec(rest);\n if (!match) continue;\n const node = rule.build(match);\n if (!node) continue;\n flush();\n nodes.push(node);\n i += match[0].length;\n matched = true;\n break;\n }\n if (matched) continue;\n\n text += source[i];\n i += 1;\n }\n\n flush();\n return nodes;\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("./Sidebar.module.cjs");let n=require("react"),r=require("react/jsx-runtime");function i(e){let t=[],n;for(let r of e){if(r.type===`separator`){t.push({kind:`separator`,key:r.key}),n=void 0;continue}if(r.type===`section`){n={kind:`items`,key:r.key,section:r,items:[]},t.push(n);continue}n||(n={kind:`items`,key:`loose-${r.key}`,items:[]},t.push(n)),n.items.push(r)}return t}function a({header:a,items:o,value:s,onChange:c,footer:l,collapsed:u=!1,width:d=240,collapsedWidth:f=64,className:p,style:m,...h}){let g=(0,n.useId)(),_=i(o),v=typeof(u?f:d)==`number`?`${u?f:d}px`:u?f:d,y=n=>{let i=n.key===s,a=u&&typeof n.label==`string`?n.label:void 0,o=(0,r.jsxs)(r.Fragment,{children:[n.icon&&(0,r.jsx)(`span`,{className:t.default.icon,children:n.icon}),!u&&(0,r.jsx)(`span`,{className:t.default.label,children:n.label}),!u&&n.badge!==void 0&&(0,r.jsx)(`span`,{className:t.default.badge,children:n.badge})]});return n.href&&!n.disabled?(0,r.jsx)(`a`,{href:n.href,className:e.cn(t.default.item,i&&t.default.active),"aria-current":i?`page`:void 0,onClick:()=>c?.(n.key),title:a,children:o},n.key):(0,r.jsx)(`button`,{type:`button`,className:e.cn(t.default.item,i&&t.default.active),"aria-current":i?`page`:void 0,disabled:n.disabled,onClick:()=>c?.(n.key),title:a,children:o},n.key)};return(0,r.jsxs)(`aside`,{className:e.cn(t.default.sidebar,u&&t.default.collapsed,p),style:{width:v,...m},...h,children:[a&&(0,r.jsx)(`div`,{className:t.default.header,children:a}),(0,r.jsx)(`nav`,{className:t.default.nav,"aria-label":`Navegação lateral`,children:_.map(n=>{if(n.kind===`separator`)return(0,r.jsx)(`hr`,{className:t.default.separator},n.key);if(!n.section)return(0,r.jsx)(`div`,{className:t.default.group,children:n.items.map(y)},n.key);let i=`${g}-${n.key}`;return(0,r.jsxs)(`div`,{role:`group`,"aria-labelledby":i,className:t.default.group,children:[(0,r.jsx)(`div`,{id:i,role:`presentation`,className:e.cn(t.default.section,u&&t.default.sectionCollapsed),children:n.section.label}),n.items.map(y)]},n.key)})}),l&&(0,r.jsx)(`div`,{className:t.default.footer,children:l})]})}exports.Sidebar=a;
1
+ const e=require("../../utils/cn.cjs"),t=require("./Sidebar.module.cjs"),n=require("./SidebarEntry.cjs");let r=require("react"),i=require("react/jsx-runtime");function a(e){let t=[],n;for(let r of e){if(r.type===`separator`){t.push({kind:`separator`,key:r.key}),n=void 0;continue}if(r.type===`section`){n={kind:`items`,key:r.key,section:r,items:[]},t.push(n);continue}n||(n={kind:`items`,key:`loose-${r.key}`,items:[]},t.push(n)),n.items.push(r)}return t}function o({header:o,items:s,value:c,onChange:l,footer:u,collapsed:d=!1,width:f=240,collapsedWidth:p=64,className:m,style:h,...g}){let _=(0,r.useId)(),v=a(s),y=typeof(d?p:f)==`number`?`${d?p:f}px`:d?p:f;return(0,i.jsxs)(`aside`,{className:e.cn(t.default.sidebar,d&&t.default.collapsed,m),style:{width:y,...h},...g,children:[o&&(0,i.jsx)(`div`,{className:t.default.header,children:o}),(0,i.jsx)(`nav`,{className:t.default.nav,"aria-label":`Navegação lateral`,children:v.map(r=>{if(r.kind===`separator`)return(0,i.jsx)(`hr`,{className:t.default.separator},r.key);if(!r.section)return(0,i.jsx)(`div`,{className:t.default.group,children:r.items.map(e=>(0,i.jsx)(n.SidebarEntry,{item:e,active:e.key===c,collapsed:d,onSelect:l},e.key))},r.key);let a=`${_}-${r.key}`;return(0,i.jsxs)(`div`,{role:`group`,"aria-labelledby":a,className:t.default.group,children:[(0,i.jsx)(`div`,{id:a,role:`presentation`,className:e.cn(t.default.section,d&&t.default.sectionCollapsed),children:r.section.label}),r.items.map(e=>(0,i.jsx)(n.SidebarEntry,{item:e,active:e.key===c,collapsed:d,onSelect:l},e.key))]},r.key)})}),u&&(0,i.jsx)(`div`,{className:t.default.footer,children:u})]})}exports.Sidebar=o;
2
2
  //# sourceMappingURL=Sidebar.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Sidebar.cjs","names":[],"sources":["../../../src/components/Sidebar/Sidebar.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — header, items, footer are the three regions,\n * value/onChange the selection, and collapsed/width/collapsedWidth the two layout\n * modes it switches between.\n */\nimport { useId, type HTMLAttributes, type ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Sidebar.module.css\";\n\nexport interface SidebarItem {\n key: string;\n label: ReactNode;\n icon?: ReactNode;\n badge?: ReactNode;\n disabled?: boolean;\n /**\n * Renders the entry as a link to this URL instead of a button.\n *\n * `onChange` still fires on click, so an app that tracks the active key keeps\n * working — the difference is that the entry now behaves like navigation:\n * middle-click, ctrl-click and \"copy link address\" do what the user expects,\n * and a screen reader announces a link rather than a button.\n *\n * A `disabled` entry ignores this and stays a `<button disabled>`: there is no\n * disabled state for an anchor, and dropping the `href` to fake one leaves a\n * link that announces itself as actionable and is not.\n */\n href?: string;\n}\n\n/**\n * A section heading. Every item after it belongs to the section, until the next\n * section or separator.\n */\nexport interface SidebarSection {\n type: \"section\";\n key: string;\n label: ReactNode;\n}\n\n/** A plain divider, for splitting the list without naming the parts. */\nexport interface SidebarSeparator {\n type: \"separator\";\n key: string;\n}\n\n/**\n * One entry of the navigation list.\n *\n * An entry with no `type` is an item, which is what keeps a plain\n * `SidebarItem[]` a valid `SidebarEntry[]`: adding sections to this component\n * needed no change at any existing call site.\n */\nexport type SidebarEntry = ({ type?: \"item\" } & SidebarItem) | SidebarSection | SidebarSeparator;\n\nexport interface SidebarProps extends Omit<HTMLAttributes<HTMLElement>, \"onChange\"> {\n /** Top slot — typically the logo + brand. */\n header?: ReactNode;\n /** Navigation entries. An entry with no `type` is an item. */\n items: SidebarEntry[];\n /** Active item key. */\n value?: string;\n /** Fires when an item is clicked. Receives the item's `key`. */\n onChange?: (key: string) => void;\n /** Bottom slot — typically settings/profile/logout. */\n footer?: ReactNode;\n /** Collapsed mode — only icons visible. Default `false`. */\n collapsed?: boolean;\n /** Width when expanded, in pixels or any CSS length. Default `240px`. */\n width?: number | string;\n /** Width when collapsed, in pixels or any CSS length. Default `64px`. */\n collapsedWidth?: number | string;\n}\n\n/** A run of items, optionally under a section heading. */\ninterface ItemBlock {\n kind: \"items\";\n key: string;\n section?: SidebarSection;\n items: SidebarItem[];\n}\n\n/** A standalone divider between blocks. */\ninterface SeparatorBlock {\n kind: \"separator\";\n key: string;\n}\n\ntype Block = ItemBlock | SeparatorBlock;\n\n/**\n * Fold the flat entry list into the blocks that get rendered.\n *\n * A section opens a block that swallows every following item; a separator closes\n * whatever is open, so items after it are loose again. Items before the first\n * section are loose too — which is the whole existing behaviour, and why a list\n * with no sections comes out of here as one unnamed block.\n *\n * @param entries - The `items` prop, as given.\n * @returns Blocks in render order.\n */\nfunction toBlocks(entries: readonly SidebarEntry[]): Block[] {\n const blocks: Block[] = [];\n let open: ItemBlock | undefined;\n\n for (const entry of entries) {\n if (entry.type === \"separator\") {\n blocks.push({ kind: \"separator\", key: entry.key });\n open = undefined;\n continue;\n }\n if (entry.type === \"section\") {\n open = { kind: \"items\", key: entry.key, section: entry, items: [] };\n blocks.push(open);\n continue;\n }\n if (!open) {\n open = { kind: \"items\", key: `loose-${entry.key}`, items: [] };\n blocks.push(open);\n }\n open.items.push(entry);\n }\n return blocks;\n}\n\n/**\n * Desktop sidebar navigation. Pair with `<Show above=\"md\">` and a `Drawer`\n * for mobile.\n *\n * Sixteen screens in one flat list is a wall, so `items` also takes section\n * headings and separators. A section renders as a labelled `role=\"group\"`, which\n * is what tells a screen reader \"Monitoring, group, 3 items\" — the reason it is\n * not a styled `disabled` item, which would announce an unavailable button and\n * stay in the navigation order.\n *\n * @example\n * const [tab, setTab] = useState(\"home\");\n * <Show above=\"md\">\n * <Sidebar\n * header={<Brand />}\n * items={[{ key: \"home\", label: \"Home\", icon: <Home /> }]}\n * value={tab}\n * onChange={setTab}\n * />\n * </Show>\n *\n * @example\n * <Sidebar\n * items={[\n * { type: \"section\", key: \"monitoring\", label: \"Monitoring\" },\n * { key: \"overview\", label: \"Overview\", href: \"/overview\" },\n * { key: \"activity\", label: \"Activity\", href: \"/activity\" },\n * { type: \"section\", key: \"admin\", label: \"Administration\" },\n * { key: \"settings\", label: \"Settings\", href: \"/settings\" },\n * ]}\n * value={tab}\n * onChange={setTab}\n * />\n */\nexport function Sidebar({\n header,\n items,\n value,\n onChange,\n footer,\n collapsed = false,\n width = 240,\n collapsedWidth = 64,\n className,\n style,\n ...props\n}: SidebarProps) {\n const baseId = useId();\n const blocks = toBlocks(items);\n const finalWidth =\n typeof (collapsed ? collapsedWidth : width) === \"number\"\n ? `${collapsed ? collapsedWidth : width}px`\n : collapsed\n ? collapsedWidth\n : width;\n\n /**\n * Render one navigation entry.\n *\n * @param item - The entry to render.\n * @returns The button, or the anchor when the entry carries an `href`.\n */\n const renderItem = (item: SidebarItem): ReactNode => {\n const active = item.key === value;\n const title = collapsed && typeof item.label === \"string\" ? item.label : undefined;\n const body = (\n <>\n {item.icon && <span className={styles.icon}>{item.icon}</span>}\n {!collapsed && <span className={styles.label}>{item.label}</span>}\n {!collapsed && item.badge !== undefined && (\n <span className={styles.badge}>{item.badge}</span>\n )}\n </>\n );\n\n if (item.href && !item.disabled) {\n return (\n <a\n key={item.key}\n href={item.href}\n className={cn(styles.item, active && styles.active)}\n aria-current={active ? \"page\" : undefined}\n onClick={() => onChange?.(item.key)}\n title={title}\n >\n {body}\n </a>\n );\n }\n\n return (\n <button\n key={item.key}\n type=\"button\"\n className={cn(styles.item, active && styles.active)}\n aria-current={active ? \"page\" : undefined}\n disabled={item.disabled}\n onClick={() => onChange?.(item.key)}\n title={title}\n >\n {body}\n </button>\n );\n };\n\n return (\n <aside\n className={cn(styles.sidebar, collapsed && styles.collapsed, className)}\n style={{ width: finalWidth, ...style }}\n {...props}\n >\n {header && <div className={styles.header}>{header}</div>}\n <nav className={styles.nav} aria-label=\"Navegação lateral\">\n {blocks.map((block) => {\n if (block.kind === \"separator\") {\n return <hr key={block.key} className={styles.separator} />;\n }\n if (!block.section) {\n return (\n <div key={block.key} className={styles.group}>\n {block.items.map(renderItem)}\n </div>\n );\n }\n const labelId = `${baseId}-${block.key}`;\n return (\n <div\n key={block.key}\n role=\"group\"\n aria-labelledby={labelId}\n className={styles.group}\n >\n <div\n id={labelId}\n role=\"presentation\"\n className={cn(styles.section, collapsed && styles.sectionCollapsed)}\n >\n {block.section.label}\n </div>\n {block.items.map(renderItem)}\n </div>\n );\n })}\n </nav>\n {footer && <div className={styles.footer}>{footer}</div>}\n </aside>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Sidebar.cjs","names":[],"sources":["../../../src/components/Sidebar/Sidebar.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — header, items, footer are the three regions,\n * value/onChange the selection, and collapsed/width/collapsedWidth the two layout\n * modes it switches between.\n */\nimport { useId, type HTMLAttributes, type ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Sidebar.module.css\";\nimport { SidebarEntry } from \"./SidebarEntry\";\n\nexport interface SidebarItem {\n key: string;\n label: ReactNode;\n icon?: ReactNode;\n badge?: ReactNode;\n disabled?: boolean;\n /**\n * Renders the entry as a link to this URL instead of a button.\n *\n * `onChange` still fires on click, so an app that tracks the active key keeps\n * working — the difference is that the entry now behaves like navigation:\n * middle-click, ctrl-click and \"copy link address\" do what the user expects,\n * and a screen reader announces a link rather than a button.\n *\n * A `disabled` entry ignores this and stays a `<button disabled>`: there is no\n * disabled state for an anchor, and dropping the `href` to fake one leaves a\n * link that announces itself as actionable and is not.\n */\n href?: string;\n}\n\n/**\n * A section heading. Every item after it belongs to the section, until the next\n * section or separator.\n */\nexport interface SidebarSection {\n type: \"section\";\n key: string;\n label: ReactNode;\n}\n\n/** A plain divider, for splitting the list without naming the parts. */\nexport interface SidebarSeparator {\n type: \"separator\";\n key: string;\n}\n\n/**\n * One entry of the navigation list.\n *\n * An entry with no `type` is an item, which is what keeps a plain\n * `SidebarItem[]` a valid `SidebarEntry[]`: adding sections to this component\n * needed no change at any existing call site.\n */\nexport type SidebarEntry = ({ type?: \"item\" } & SidebarItem) | SidebarSection | SidebarSeparator;\n\nexport interface SidebarProps extends Omit<HTMLAttributes<HTMLElement>, \"onChange\"> {\n /** Top slot — typically the logo + brand. */\n header?: ReactNode;\n /** Navigation entries. An entry with no `type` is an item. */\n items: SidebarEntry[];\n /** Active item key. */\n value?: string;\n /** Fires when an item is clicked. Receives the item's `key`. */\n onChange?: (key: string) => void;\n /** Bottom slot — typically settings/profile/logout. */\n footer?: ReactNode;\n /** Collapsed mode — only icons visible. Default `false`. */\n collapsed?: boolean;\n /** Width when expanded, in pixels or any CSS length. Default `240px`. */\n width?: number | string;\n /** Width when collapsed, in pixels or any CSS length. Default `64px`. */\n collapsedWidth?: number | string;\n}\n\n/** A run of items, optionally under a section heading. */\ninterface ItemBlock {\n kind: \"items\";\n key: string;\n section?: SidebarSection;\n items: SidebarItem[];\n}\n\n/** A standalone divider between blocks. */\ninterface SeparatorBlock {\n kind: \"separator\";\n key: string;\n}\n\ntype Block = ItemBlock | SeparatorBlock;\n\n/**\n * Fold the flat entry list into the blocks that get rendered.\n *\n * A section opens a block that swallows every following item; a separator closes\n * whatever is open, so items after it are loose again. Items before the first\n * section are loose too — which is the whole existing behaviour, and why a list\n * with no sections comes out of here as one unnamed block.\n *\n * @param entries - The `items` prop, as given.\n * @returns Blocks in render order.\n */\nfunction toBlocks(entries: readonly SidebarEntry[]): Block[] {\n const blocks: Block[] = [];\n let open: ItemBlock | undefined;\n\n for (const entry of entries) {\n if (entry.type === \"separator\") {\n blocks.push({ kind: \"separator\", key: entry.key });\n open = undefined;\n continue;\n }\n if (entry.type === \"section\") {\n open = { kind: \"items\", key: entry.key, section: entry, items: [] };\n blocks.push(open);\n continue;\n }\n if (!open) {\n open = { kind: \"items\", key: `loose-${entry.key}`, items: [] };\n blocks.push(open);\n }\n open.items.push(entry);\n }\n return blocks;\n}\n\n/**\n * Desktop sidebar navigation. Pair with `<Show above=\"md\">` and a `Drawer`\n * for mobile.\n *\n * Sixteen screens in one flat list is a wall, so `items` also takes section\n * headings and separators. A section renders as a labelled `role=\"group\"`, which\n * is what tells a screen reader \"Monitoring, group, 3 items\" — the reason it is\n * not a styled `disabled` item, which would announce an unavailable button and\n * stay in the navigation order.\n *\n * @example\n * const [tab, setTab] = useState(\"home\");\n * <Show above=\"md\">\n * <Sidebar\n * header={<Brand />}\n * items={[{ key: \"home\", label: \"Home\", icon: <Home /> }]}\n * value={tab}\n * onChange={setTab}\n * />\n * </Show>\n *\n * @example\n * <Sidebar\n * items={[\n * { type: \"section\", key: \"monitoring\", label: \"Monitoring\" },\n * { key: \"overview\", label: \"Overview\", href: \"/overview\" },\n * { key: \"activity\", label: \"Activity\", href: \"/activity\" },\n * { type: \"section\", key: \"admin\", label: \"Administration\" },\n * { key: \"settings\", label: \"Settings\", href: \"/settings\" },\n * ]}\n * value={tab}\n * onChange={setTab}\n * />\n */\nexport function Sidebar({\n header,\n items,\n value,\n onChange,\n footer,\n collapsed = false,\n width = 240,\n collapsedWidth = 64,\n className,\n style,\n ...props\n}: SidebarProps) {\n const baseId = useId();\n const blocks = toBlocks(items);\n const finalWidth =\n typeof (collapsed ? collapsedWidth : width) === \"number\"\n ? `${collapsed ? collapsedWidth : width}px`\n : collapsed\n ? collapsedWidth\n : width;\n\n return (\n <aside\n className={cn(styles.sidebar, collapsed && styles.collapsed, className)}\n style={{ width: finalWidth, ...style }}\n {...props}\n >\n {header && <div className={styles.header}>{header}</div>}\n <nav className={styles.nav} aria-label=\"Navegação lateral\">\n {blocks.map((block) => {\n if (block.kind === \"separator\") {\n return <hr key={block.key} className={styles.separator} />;\n }\n if (!block.section) {\n return (\n <div key={block.key} className={styles.group}>\n {block.items.map((item) => (\n <SidebarEntry\n key={item.key}\n item={item}\n active={item.key === value}\n collapsed={collapsed}\n onSelect={onChange}\n />\n ))}\n </div>\n );\n }\n const labelId = `${baseId}-${block.key}`;\n return (\n <div\n key={block.key}\n role=\"group\"\n aria-labelledby={labelId}\n className={styles.group}\n >\n <div\n id={labelId}\n role=\"presentation\"\n className={cn(styles.section, collapsed && styles.sectionCollapsed)}\n >\n {block.section.label}\n </div>\n {block.items.map((item) => (\n <SidebarEntry\n key={item.key}\n item={item}\n active={item.key === value}\n collapsed={collapsed}\n onSelect={onChange}\n />\n ))}\n </div>\n );\n })}\n </nav>\n {footer && <div className={styles.footer}>{footer}</div>}\n </aside>\n );\n}\n"],"mappings":"8JAsGA,SAAS,EAAS,EAA2C,CACzD,IAAM,EAAkB,CAAC,EACrB,EAEJ,IAAK,IAAM,KAAS,EAAS,CACzB,GAAI,EAAM,OAAS,YAAa,CAC5B,EAAO,KAAK,CAAE,KAAM,YAAa,IAAK,EAAM,GAAI,CAAC,EACjD,EAAO,IAAA,GACP,QACJ,CACA,GAAI,EAAM,OAAS,UAAW,CAC1B,EAAO,CAAE,KAAM,QAAS,IAAK,EAAM,IAAK,QAAS,EAAO,MAAO,CAAC,CAAE,EAClE,EAAO,KAAK,CAAI,EAChB,QACJ,CACK,IACD,EAAO,CAAE,KAAM,QAAS,IAAK,SAAS,EAAM,MAAO,MAAO,CAAC,CAAE,EAC7D,EAAO,KAAK,CAAI,GAEpB,EAAK,MAAM,KAAK,CAAK,CACzB,CACA,OAAO,CACX,CAoCA,SAAgB,EAAQ,CACpB,SACA,QACA,QACA,WACA,SACA,YAAY,GACZ,QAAQ,IACR,iBAAiB,GACjB,YACA,QACA,GAAG,GACU,CACb,IAAM,GAAA,EAAS,EAAA,MAAA,CAAM,EACf,EAAS,EAAS,CAAK,EACvB,EACF,OAAQ,EAAY,EAAiB,IAAW,SAC1C,GAAG,EAAY,EAAiB,EAAM,IACtC,EACE,EACA,EAEZ,OACI,EAAA,EAAA,KAAA,CAAC,QAAD,CACI,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,GAAa,EAAA,QAAO,UAAW,CAAS,EACtE,MAAO,CAAE,MAAO,EAAY,GAAG,CAAM,EACrC,GAAI,EAHR,SAAA,CAKK,IAAU,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,OAAS,SAAA,CAAY,CAAA,GACvD,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,IAAK,aAAW,oBAClC,SAAA,EAAO,IAAK,GAAU,CACnB,GAAI,EAAM,OAAS,YACf,OAAO,EAAA,EAAA,IAAA,CAAC,KAAD,CAAoB,UAAW,EAAA,QAAO,SAAY,EAAzC,EAAM,GAAmC,EAE7D,GAAI,CAAC,EAAM,QACP,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAqB,UAAW,EAAA,QAAO,MAClC,SAAA,EAAM,MAAM,IAAK,IACd,EAAA,EAAA,IAAA,CAAC,EAAA,aAAD,CAEU,OACN,OAAQ,EAAK,MAAQ,EACV,YACX,SAAU,CACb,EALQ,EAAK,GAKb,CACJ,CACA,EAVK,EAAM,GAUX,EAGb,IAAM,EAAU,GAAG,EAAO,GAAG,EAAM,MACnC,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAEI,KAAK,QACL,kBAAiB,EACjB,UAAW,EAAA,QAAO,MAJtB,SAAA,EAMI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,GAAI,EACJ,KAAK,eACL,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,GAAa,EAAA,QAAO,gBAAgB,EAEjE,SAAA,EAAM,QAAQ,KACd,CAAA,EACJ,EAAM,MAAM,IAAK,IACd,EAAA,EAAA,IAAA,CAAC,EAAA,aAAD,CAEU,OACN,OAAQ,EAAK,MAAQ,EACV,YACX,SAAU,CACb,EALQ,EAAK,GAKb,CACJ,CACA,CArBI,EAAA,EAAM,GAqBV,CAEb,CAAC,CACA,CAAA,EACJ,IAAU,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,OAAS,SAAA,CAAY,CAAA,CACpD,GAEf"}
@@ -1,7 +1,8 @@
1
1
  import { cn as e } from "../../utils/cn.js";
2
2
  import t from "./Sidebar.module.js";
3
- import { useId as n } from "react";
4
- import { Fragment as r, jsx as i, jsxs as a } from "react/jsx-runtime";
3
+ import { SidebarEntry as n } from "./SidebarEntry.js";
4
+ import { useId as r } from "react";
5
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
5
6
  //#region src/components/Sidebar/Sidebar.tsx
6
7
  function o(e) {
7
8
  let t = [], n;
@@ -31,38 +32,7 @@ function o(e) {
31
32
  return t;
32
33
  }
33
34
  function s({ header: s, items: c, value: l, onChange: u, footer: d, collapsed: f = !1, width: p = 240, collapsedWidth: m = 64, className: h, style: g, ..._ }) {
34
- let v = n(), y = o(c), b = typeof (f ? m : p) == "number" ? `${f ? m : p}px` : f ? m : p, x = (n) => {
35
- let o = n.key === l, s = f && typeof n.label == "string" ? n.label : void 0, c = /* @__PURE__ */ a(r, { children: [
36
- n.icon && /* @__PURE__ */ i("span", {
37
- className: t.icon,
38
- children: n.icon
39
- }),
40
- !f && /* @__PURE__ */ i("span", {
41
- className: t.label,
42
- children: n.label
43
- }),
44
- !f && n.badge !== void 0 && /* @__PURE__ */ i("span", {
45
- className: t.badge,
46
- children: n.badge
47
- })
48
- ] });
49
- return n.href && !n.disabled ? /* @__PURE__ */ i("a", {
50
- href: n.href,
51
- className: e(t.item, o && t.active),
52
- "aria-current": o ? "page" : void 0,
53
- onClick: () => u?.(n.key),
54
- title: s,
55
- children: c
56
- }, n.key) : /* @__PURE__ */ i("button", {
57
- type: "button",
58
- className: e(t.item, o && t.active),
59
- "aria-current": o ? "page" : void 0,
60
- disabled: n.disabled,
61
- onClick: () => u?.(n.key),
62
- title: s,
63
- children: c
64
- }, n.key);
65
- };
35
+ let v = r(), y = o(c), b = typeof (f ? m : p) == "number" ? `${f ? m : p}px` : f ? m : p;
66
36
  return /* @__PURE__ */ a("aside", {
67
37
  className: e(t.sidebar, f && t.collapsed, h),
68
38
  style: {
@@ -78,24 +48,34 @@ function s({ header: s, items: c, value: l, onChange: u, footer: d, collapsed: f
78
48
  /* @__PURE__ */ i("nav", {
79
49
  className: t.nav,
80
50
  "aria-label": "Navegação lateral",
81
- children: y.map((n) => {
82
- if (n.kind === "separator") return /* @__PURE__ */ i("hr", { className: t.separator }, n.key);
83
- if (!n.section) return /* @__PURE__ */ i("div", {
51
+ children: y.map((r) => {
52
+ if (r.kind === "separator") return /* @__PURE__ */ i("hr", { className: t.separator }, r.key);
53
+ if (!r.section) return /* @__PURE__ */ i("div", {
84
54
  className: t.group,
85
- children: n.items.map(x)
86
- }, n.key);
87
- let r = `${v}-${n.key}`;
55
+ children: r.items.map((e) => /* @__PURE__ */ i(n, {
56
+ item: e,
57
+ active: e.key === l,
58
+ collapsed: f,
59
+ onSelect: u
60
+ }, e.key))
61
+ }, r.key);
62
+ let o = `${v}-${r.key}`;
88
63
  return /* @__PURE__ */ a("div", {
89
64
  role: "group",
90
- "aria-labelledby": r,
65
+ "aria-labelledby": o,
91
66
  className: t.group,
92
67
  children: [/* @__PURE__ */ i("div", {
93
- id: r,
68
+ id: o,
94
69
  role: "presentation",
95
70
  className: e(t.section, f && t.sectionCollapsed),
96
- children: n.section.label
97
- }), n.items.map(x)]
98
- }, n.key);
71
+ children: r.section.label
72
+ }), r.items.map((e) => /* @__PURE__ */ i(n, {
73
+ item: e,
74
+ active: e.key === l,
75
+ collapsed: f,
76
+ onSelect: u
77
+ }, e.key))]
78
+ }, r.key);
99
79
  })
100
80
  }),
101
81
  d && /* @__PURE__ */ i("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"Sidebar.js","names":[],"sources":["../../../src/components/Sidebar/Sidebar.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — header, items, footer are the three regions,\n * value/onChange the selection, and collapsed/width/collapsedWidth the two layout\n * modes it switches between.\n */\nimport { useId, type HTMLAttributes, type ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Sidebar.module.css\";\n\nexport interface SidebarItem {\n key: string;\n label: ReactNode;\n icon?: ReactNode;\n badge?: ReactNode;\n disabled?: boolean;\n /**\n * Renders the entry as a link to this URL instead of a button.\n *\n * `onChange` still fires on click, so an app that tracks the active key keeps\n * working — the difference is that the entry now behaves like navigation:\n * middle-click, ctrl-click and \"copy link address\" do what the user expects,\n * and a screen reader announces a link rather than a button.\n *\n * A `disabled` entry ignores this and stays a `<button disabled>`: there is no\n * disabled state for an anchor, and dropping the `href` to fake one leaves a\n * link that announces itself as actionable and is not.\n */\n href?: string;\n}\n\n/**\n * A section heading. Every item after it belongs to the section, until the next\n * section or separator.\n */\nexport interface SidebarSection {\n type: \"section\";\n key: string;\n label: ReactNode;\n}\n\n/** A plain divider, for splitting the list without naming the parts. */\nexport interface SidebarSeparator {\n type: \"separator\";\n key: string;\n}\n\n/**\n * One entry of the navigation list.\n *\n * An entry with no `type` is an item, which is what keeps a plain\n * `SidebarItem[]` a valid `SidebarEntry[]`: adding sections to this component\n * needed no change at any existing call site.\n */\nexport type SidebarEntry = ({ type?: \"item\" } & SidebarItem) | SidebarSection | SidebarSeparator;\n\nexport interface SidebarProps extends Omit<HTMLAttributes<HTMLElement>, \"onChange\"> {\n /** Top slot — typically the logo + brand. */\n header?: ReactNode;\n /** Navigation entries. An entry with no `type` is an item. */\n items: SidebarEntry[];\n /** Active item key. */\n value?: string;\n /** Fires when an item is clicked. Receives the item's `key`. */\n onChange?: (key: string) => void;\n /** Bottom slot — typically settings/profile/logout. */\n footer?: ReactNode;\n /** Collapsed mode — only icons visible. Default `false`. */\n collapsed?: boolean;\n /** Width when expanded, in pixels or any CSS length. Default `240px`. */\n width?: number | string;\n /** Width when collapsed, in pixels or any CSS length. Default `64px`. */\n collapsedWidth?: number | string;\n}\n\n/** A run of items, optionally under a section heading. */\ninterface ItemBlock {\n kind: \"items\";\n key: string;\n section?: SidebarSection;\n items: SidebarItem[];\n}\n\n/** A standalone divider between blocks. */\ninterface SeparatorBlock {\n kind: \"separator\";\n key: string;\n}\n\ntype Block = ItemBlock | SeparatorBlock;\n\n/**\n * Fold the flat entry list into the blocks that get rendered.\n *\n * A section opens a block that swallows every following item; a separator closes\n * whatever is open, so items after it are loose again. Items before the first\n * section are loose too — which is the whole existing behaviour, and why a list\n * with no sections comes out of here as one unnamed block.\n *\n * @param entries - The `items` prop, as given.\n * @returns Blocks in render order.\n */\nfunction toBlocks(entries: readonly SidebarEntry[]): Block[] {\n const blocks: Block[] = [];\n let open: ItemBlock | undefined;\n\n for (const entry of entries) {\n if (entry.type === \"separator\") {\n blocks.push({ kind: \"separator\", key: entry.key });\n open = undefined;\n continue;\n }\n if (entry.type === \"section\") {\n open = { kind: \"items\", key: entry.key, section: entry, items: [] };\n blocks.push(open);\n continue;\n }\n if (!open) {\n open = { kind: \"items\", key: `loose-${entry.key}`, items: [] };\n blocks.push(open);\n }\n open.items.push(entry);\n }\n return blocks;\n}\n\n/**\n * Desktop sidebar navigation. Pair with `<Show above=\"md\">` and a `Drawer`\n * for mobile.\n *\n * Sixteen screens in one flat list is a wall, so `items` also takes section\n * headings and separators. A section renders as a labelled `role=\"group\"`, which\n * is what tells a screen reader \"Monitoring, group, 3 items\" — the reason it is\n * not a styled `disabled` item, which would announce an unavailable button and\n * stay in the navigation order.\n *\n * @example\n * const [tab, setTab] = useState(\"home\");\n * <Show above=\"md\">\n * <Sidebar\n * header={<Brand />}\n * items={[{ key: \"home\", label: \"Home\", icon: <Home /> }]}\n * value={tab}\n * onChange={setTab}\n * />\n * </Show>\n *\n * @example\n * <Sidebar\n * items={[\n * { type: \"section\", key: \"monitoring\", label: \"Monitoring\" },\n * { key: \"overview\", label: \"Overview\", href: \"/overview\" },\n * { key: \"activity\", label: \"Activity\", href: \"/activity\" },\n * { type: \"section\", key: \"admin\", label: \"Administration\" },\n * { key: \"settings\", label: \"Settings\", href: \"/settings\" },\n * ]}\n * value={tab}\n * onChange={setTab}\n * />\n */\nexport function Sidebar({\n header,\n items,\n value,\n onChange,\n footer,\n collapsed = false,\n width = 240,\n collapsedWidth = 64,\n className,\n style,\n ...props\n}: SidebarProps) {\n const baseId = useId();\n const blocks = toBlocks(items);\n const finalWidth =\n typeof (collapsed ? collapsedWidth : width) === \"number\"\n ? `${collapsed ? collapsedWidth : width}px`\n : collapsed\n ? collapsedWidth\n : width;\n\n /**\n * Render one navigation entry.\n *\n * @param item - The entry to render.\n * @returns The button, or the anchor when the entry carries an `href`.\n */\n const renderItem = (item: SidebarItem): ReactNode => {\n const active = item.key === value;\n const title = collapsed && typeof item.label === \"string\" ? item.label : undefined;\n const body = (\n <>\n {item.icon && <span className={styles.icon}>{item.icon}</span>}\n {!collapsed && <span className={styles.label}>{item.label}</span>}\n {!collapsed && item.badge !== undefined && (\n <span className={styles.badge}>{item.badge}</span>\n )}\n </>\n );\n\n if (item.href && !item.disabled) {\n return (\n <a\n key={item.key}\n href={item.href}\n className={cn(styles.item, active && styles.active)}\n aria-current={active ? \"page\" : undefined}\n onClick={() => onChange?.(item.key)}\n title={title}\n >\n {body}\n </a>\n );\n }\n\n return (\n <button\n key={item.key}\n type=\"button\"\n className={cn(styles.item, active && styles.active)}\n aria-current={active ? \"page\" : undefined}\n disabled={item.disabled}\n onClick={() => onChange?.(item.key)}\n title={title}\n >\n {body}\n </button>\n );\n };\n\n return (\n <aside\n className={cn(styles.sidebar, collapsed && styles.collapsed, className)}\n style={{ width: finalWidth, ...style }}\n {...props}\n >\n {header && <div className={styles.header}>{header}</div>}\n <nav className={styles.nav} aria-label=\"Navegação lateral\">\n {blocks.map((block) => {\n if (block.kind === \"separator\") {\n return <hr key={block.key} className={styles.separator} />;\n }\n if (!block.section) {\n return (\n <div key={block.key} className={styles.group}>\n {block.items.map(renderItem)}\n </div>\n );\n }\n const labelId = `${baseId}-${block.key}`;\n return (\n <div\n key={block.key}\n role=\"group\"\n aria-labelledby={labelId}\n className={styles.group}\n >\n <div\n id={labelId}\n role=\"presentation\"\n className={cn(styles.section, collapsed && styles.sectionCollapsed)}\n >\n {block.section.label}\n </div>\n {block.items.map(renderItem)}\n </div>\n );\n })}\n </nav>\n {footer && <div className={styles.footer}>{footer}</div>}\n </aside>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Sidebar.js","names":[],"sources":["../../../src/components/Sidebar/Sidebar.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — header, items, footer are the three regions,\n * value/onChange the selection, and collapsed/width/collapsedWidth the two layout\n * modes it switches between.\n */\nimport { useId, type HTMLAttributes, type ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Sidebar.module.css\";\nimport { SidebarEntry } from \"./SidebarEntry\";\n\nexport interface SidebarItem {\n key: string;\n label: ReactNode;\n icon?: ReactNode;\n badge?: ReactNode;\n disabled?: boolean;\n /**\n * Renders the entry as a link to this URL instead of a button.\n *\n * `onChange` still fires on click, so an app that tracks the active key keeps\n * working — the difference is that the entry now behaves like navigation:\n * middle-click, ctrl-click and \"copy link address\" do what the user expects,\n * and a screen reader announces a link rather than a button.\n *\n * A `disabled` entry ignores this and stays a `<button disabled>`: there is no\n * disabled state for an anchor, and dropping the `href` to fake one leaves a\n * link that announces itself as actionable and is not.\n */\n href?: string;\n}\n\n/**\n * A section heading. Every item after it belongs to the section, until the next\n * section or separator.\n */\nexport interface SidebarSection {\n type: \"section\";\n key: string;\n label: ReactNode;\n}\n\n/** A plain divider, for splitting the list without naming the parts. */\nexport interface SidebarSeparator {\n type: \"separator\";\n key: string;\n}\n\n/**\n * One entry of the navigation list.\n *\n * An entry with no `type` is an item, which is what keeps a plain\n * `SidebarItem[]` a valid `SidebarEntry[]`: adding sections to this component\n * needed no change at any existing call site.\n */\nexport type SidebarEntry = ({ type?: \"item\" } & SidebarItem) | SidebarSection | SidebarSeparator;\n\nexport interface SidebarProps extends Omit<HTMLAttributes<HTMLElement>, \"onChange\"> {\n /** Top slot — typically the logo + brand. */\n header?: ReactNode;\n /** Navigation entries. An entry with no `type` is an item. */\n items: SidebarEntry[];\n /** Active item key. */\n value?: string;\n /** Fires when an item is clicked. Receives the item's `key`. */\n onChange?: (key: string) => void;\n /** Bottom slot — typically settings/profile/logout. */\n footer?: ReactNode;\n /** Collapsed mode — only icons visible. Default `false`. */\n collapsed?: boolean;\n /** Width when expanded, in pixels or any CSS length. Default `240px`. */\n width?: number | string;\n /** Width when collapsed, in pixels or any CSS length. Default `64px`. */\n collapsedWidth?: number | string;\n}\n\n/** A run of items, optionally under a section heading. */\ninterface ItemBlock {\n kind: \"items\";\n key: string;\n section?: SidebarSection;\n items: SidebarItem[];\n}\n\n/** A standalone divider between blocks. */\ninterface SeparatorBlock {\n kind: \"separator\";\n key: string;\n}\n\ntype Block = ItemBlock | SeparatorBlock;\n\n/**\n * Fold the flat entry list into the blocks that get rendered.\n *\n * A section opens a block that swallows every following item; a separator closes\n * whatever is open, so items after it are loose again. Items before the first\n * section are loose too — which is the whole existing behaviour, and why a list\n * with no sections comes out of here as one unnamed block.\n *\n * @param entries - The `items` prop, as given.\n * @returns Blocks in render order.\n */\nfunction toBlocks(entries: readonly SidebarEntry[]): Block[] {\n const blocks: Block[] = [];\n let open: ItemBlock | undefined;\n\n for (const entry of entries) {\n if (entry.type === \"separator\") {\n blocks.push({ kind: \"separator\", key: entry.key });\n open = undefined;\n continue;\n }\n if (entry.type === \"section\") {\n open = { kind: \"items\", key: entry.key, section: entry, items: [] };\n blocks.push(open);\n continue;\n }\n if (!open) {\n open = { kind: \"items\", key: `loose-${entry.key}`, items: [] };\n blocks.push(open);\n }\n open.items.push(entry);\n }\n return blocks;\n}\n\n/**\n * Desktop sidebar navigation. Pair with `<Show above=\"md\">` and a `Drawer`\n * for mobile.\n *\n * Sixteen screens in one flat list is a wall, so `items` also takes section\n * headings and separators. A section renders as a labelled `role=\"group\"`, which\n * is what tells a screen reader \"Monitoring, group, 3 items\" — the reason it is\n * not a styled `disabled` item, which would announce an unavailable button and\n * stay in the navigation order.\n *\n * @example\n * const [tab, setTab] = useState(\"home\");\n * <Show above=\"md\">\n * <Sidebar\n * header={<Brand />}\n * items={[{ key: \"home\", label: \"Home\", icon: <Home /> }]}\n * value={tab}\n * onChange={setTab}\n * />\n * </Show>\n *\n * @example\n * <Sidebar\n * items={[\n * { type: \"section\", key: \"monitoring\", label: \"Monitoring\" },\n * { key: \"overview\", label: \"Overview\", href: \"/overview\" },\n * { key: \"activity\", label: \"Activity\", href: \"/activity\" },\n * { type: \"section\", key: \"admin\", label: \"Administration\" },\n * { key: \"settings\", label: \"Settings\", href: \"/settings\" },\n * ]}\n * value={tab}\n * onChange={setTab}\n * />\n */\nexport function Sidebar({\n header,\n items,\n value,\n onChange,\n footer,\n collapsed = false,\n width = 240,\n collapsedWidth = 64,\n className,\n style,\n ...props\n}: SidebarProps) {\n const baseId = useId();\n const blocks = toBlocks(items);\n const finalWidth =\n typeof (collapsed ? collapsedWidth : width) === \"number\"\n ? `${collapsed ? collapsedWidth : width}px`\n : collapsed\n ? collapsedWidth\n : width;\n\n return (\n <aside\n className={cn(styles.sidebar, collapsed && styles.collapsed, className)}\n style={{ width: finalWidth, ...style }}\n {...props}\n >\n {header && <div className={styles.header}>{header}</div>}\n <nav className={styles.nav} aria-label=\"Navegação lateral\">\n {blocks.map((block) => {\n if (block.kind === \"separator\") {\n return <hr key={block.key} className={styles.separator} />;\n }\n if (!block.section) {\n return (\n <div key={block.key} className={styles.group}>\n {block.items.map((item) => (\n <SidebarEntry\n key={item.key}\n item={item}\n active={item.key === value}\n collapsed={collapsed}\n onSelect={onChange}\n />\n ))}\n </div>\n );\n }\n const labelId = `${baseId}-${block.key}`;\n return (\n <div\n key={block.key}\n role=\"group\"\n aria-labelledby={labelId}\n className={styles.group}\n >\n <div\n id={labelId}\n role=\"presentation\"\n className={cn(styles.section, collapsed && styles.sectionCollapsed)}\n >\n {block.section.label}\n </div>\n {block.items.map((item) => (\n <SidebarEntry\n key={item.key}\n item={item}\n active={item.key === value}\n collapsed={collapsed}\n onSelect={onChange}\n />\n ))}\n </div>\n );\n })}\n </nav>\n {footer && <div className={styles.footer}>{footer}</div>}\n </aside>\n );\n}\n"],"mappings":";;;;;;AAsGA,SAAS,EAAS,GAA2C;CACzD,IAAM,IAAkB,CAAC,GACrB;CAEJ,KAAK,IAAM,KAAS,GAAS;EACzB,IAAI,EAAM,SAAS,aAAa;GAE5B,AADA,EAAO,KAAK;IAAE,MAAM;IAAa,KAAK,EAAM;GAAI,CAAC,GACjD,IAAO,KAAA;GACP;EACJ;EACA,IAAI,EAAM,SAAS,WAAW;GAE1B,AADA,IAAO;IAAE,MAAM;IAAS,KAAK,EAAM;IAAK,SAAS;IAAO,OAAO,CAAC;GAAE,GAClE,EAAO,KAAK,CAAI;GAChB;EACJ;EAKA,AAJK,MACD,IAAO;GAAE,MAAM;GAAS,KAAK,SAAS,EAAM;GAAO,OAAO,CAAC;EAAE,GAC7D,EAAO,KAAK,CAAI,IAEpB,EAAK,MAAM,KAAK,CAAK;CACzB;CACA,OAAO;AACX;AAoCA,SAAgB,EAAQ,EACpB,WACA,UACA,UACA,aACA,WACA,eAAY,IACZ,WAAQ,KACR,oBAAiB,IACjB,cACA,UACA,GAAG,KACU;CACb,IAAM,IAAS,EAAM,GACf,IAAS,EAAS,CAAK,GACvB,IACF,QAAQ,IAAY,IAAiB,MAAW,WAC1C,GAAG,IAAY,IAAiB,EAAM,MACtC,IACE,IACA;CAEZ,OACI,kBAAC,SAAD;EACI,WAAW,EAAG,EAAO,SAAS,KAAa,EAAO,WAAW,CAAS;EACtE,OAAO;GAAE,OAAO;GAAY,GAAG;EAAM;EACrC,GAAI;EAHR,UAAA;GAKK,KAAU,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAS,UAAA;GAAY,CAAA;GACvD,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAK,cAAW;IAClC,UAAA,EAAO,KAAK,MAAU;KACnB,IAAI,EAAM,SAAS,aACf,OAAO,kBAAC,MAAD,EAAoB,WAAW,EAAO,UAAY,GAAzC,EAAM,GAAmC;KAE7D,IAAI,CAAC,EAAM,SACP,OACI,kBAAC,OAAD;MAAqB,WAAW,EAAO;MAClC,UAAA,EAAM,MAAM,KAAK,MACd,kBAAC,GAAD;OAEU;OACN,QAAQ,EAAK,QAAQ;OACV;OACX,UAAU;MACb,GALQ,EAAK,GAKb,CACJ;KACA,GAVK,EAAM,GAUX;KAGb,IAAM,IAAU,GAAG,EAAO,GAAG,EAAM;KACnC,OACI,kBAAC,OAAD;MAEI,MAAK;MACL,mBAAiB;MACjB,WAAW,EAAO;MAJtB,UAAA,CAMI,kBAAC,OAAD;OACI,IAAI;OACJ,MAAK;OACL,WAAW,EAAG,EAAO,SAAS,KAAa,EAAO,gBAAgB;OAEjE,UAAA,EAAM,QAAQ;MACd,CAAA,GACJ,EAAM,MAAM,KAAK,MACd,kBAAC,GAAD;OAEU;OACN,QAAQ,EAAK,QAAQ;OACV;OACX,UAAU;MACb,GALQ,EAAK,GAKb,CACJ,CACA;KArBI,GAAA,EAAM,GAqBV;IAEb,CAAC;GACA,CAAA;GACJ,KAAU,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAS,UAAA;GAAY,CAAA;EACpD;;AAEf"}
@@ -1 +1 @@
1
- {"version":3,"file":"Sidebar.module.cjs","names":[],"sources":["../../../src/components/Sidebar/Sidebar.module.css"],"sourcesContent":[".sidebar {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--tempest-surface);\n border-right: 1px solid var(--tempest-border);\n font-family: var(--tempest-font-sans);\n transition: width var(--tempest-duration-base, 200ms) var(--tempest-ease-out, ease-out);\n overflow: hidden;\n}\n\n.header {\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n min-height: 56px;\n display: flex;\n align-items: center;\n}\n\n.nav {\n flex: 1 1 auto;\n overflow-y: auto;\n padding: var(--tempest-space-2);\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.group {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.group + .group {\n margin-top: var(--tempest-space-3);\n}\n\n.collapsed .group + .group {\n margin-top: var(--tempest-space-2);\n padding-top: var(--tempest-space-2);\n border-top: 1px solid var(--tempest-border);\n}\n\n.section {\n padding: var(--tempest-space-2) var(--tempest-space-3) var(--tempest-space-1);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-semibold);\n letter-spacing: 0.04em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sectionCollapsed {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n}\n\n.separator {\n flex: 0 0 auto;\n margin: var(--tempest-space-2) var(--tempest-space-1);\n border: 0;\n border-top: 1px solid var(--tempest-border);\n}\n\n.item {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: 0;\n border-radius: var(--tempest-radius-md);\n color: var(--tempest-text);\n font-size: var(--tempest-text-sm);\n text-align: left;\n cursor: pointer;\n transition: background var(--tempest-duration-fast, 120ms);\n min-height: 36px;\n width: 100%;\n}\n\n.collapsed .item {\n justify-content: center;\n padding-left: 0;\n padding-right: 0;\n}\n\n.item {\n text-decoration: none;\n}\n\n.item:hover {\n background: var(--tempest-gray-100);\n}\n\n.item.active {\n background: var(--tempest-primary-100);\n color: var(--tempest-primary-700);\n font-weight: var(--tempest-weight-medium);\n}\n\n.item:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n font-size: 18px;\n line-height: 1;\n flex: 0 0 auto;\n}\n\n.label {\n flex: 1 1 auto;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.badge {\n flex: 0 0 auto;\n min-width: 18px;\n height: 18px;\n padding: 0 6px;\n border-radius: var(--tempest-radius-full);\n background: var(--tempest-gray-200);\n color: var(--tempest-text);\n font-size: 10px;\n line-height: 18px;\n text-align: center;\n}\n\n.footer {\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-top: 1px solid var(--tempest-border);\n}\n"],"mappings":""}
1
+ {"version":3,"file":"Sidebar.module.cjs","names":[],"sources":["../../../src/components/Sidebar/Sidebar.module.css"],"sourcesContent":[".sidebar {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--tempest-surface);\n border-right: 1px solid var(--tempest-border);\n font-family: var(--tempest-font-sans);\n transition: width var(--tempest-duration-base, 200ms) var(--tempest-ease-out, ease-out);\n overflow: hidden;\n}\n\n.header {\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n min-height: 56px;\n display: flex;\n align-items: center;\n}\n\n.nav {\n flex: 1 1 auto;\n overflow-y: auto;\n padding: var(--tempest-space-2);\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.group {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.group + .group {\n margin-top: var(--tempest-space-3);\n}\n\n.collapsed .group + .group {\n margin-top: var(--tempest-space-2);\n padding-top: var(--tempest-space-2);\n border-top: 1px solid var(--tempest-border);\n}\n\n.section {\n padding: var(--tempest-space-2) var(--tempest-space-3) var(--tempest-space-1);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-semibold);\n letter-spacing: 0.04em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sectionCollapsed {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n}\n\n.separator {\n flex: 0 0 auto;\n margin: var(--tempest-space-2) var(--tempest-space-1);\n border: 0;\n border-top: 1px solid var(--tempest-border);\n}\n\n.item {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: 0;\n border-radius: var(--tempest-radius-md);\n color: var(--tempest-text);\n font-size: var(--tempest-text-sm);\n text-align: left;\n cursor: pointer;\n transition: background var(--tempest-duration-fast, 120ms);\n min-height: 36px;\n width: 100%;\n text-decoration: none;\n}\n\n.collapsed .item {\n justify-content: center;\n padding-left: 0;\n padding-right: 0;\n}\n\n.item:hover {\n background: var(--tempest-gray-100);\n}\n\n.item.active {\n background: var(--tempest-primary-100);\n color: var(--tempest-primary-700);\n font-weight: var(--tempest-weight-medium);\n}\n\n.item:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n font-size: 18px;\n line-height: 1;\n flex: 0 0 auto;\n}\n\n.label {\n flex: 1 1 auto;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.badge {\n flex: 0 0 auto;\n min-width: 18px;\n height: 18px;\n padding: 0 6px;\n border-radius: var(--tempest-radius-full);\n background: var(--tempest-gray-200);\n color: var(--tempest-text);\n font-size: 10px;\n line-height: 18px;\n text-align: center;\n}\n\n.footer {\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-top: 1px solid var(--tempest-border);\n}\n"],"mappings":""}