tempest-react-sdk 0.27.0 → 0.28.1

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 (111) hide show
  1. package/README.md +26 -2
  2. package/bin/lib/css/analyze.mjs +149 -0
  3. package/bin/lib/css/analyze.test.mjs +136 -0
  4. package/bin/lib/css/collect.mjs +152 -0
  5. package/bin/lib/css/findings.mjs +79 -0
  6. package/bin/lib/css/fix.e2e.test.mjs +99 -0
  7. package/bin/lib/css/fix.mjs +138 -0
  8. package/bin/lib/css/fix.test.mjs +108 -0
  9. package/bin/lib/css/index.mjs +16 -0
  10. package/bin/lib/css/parse.mjs +398 -0
  11. package/bin/lib/css/parse.test.mjs +146 -0
  12. package/bin/lib/css/properties.mjs +399 -0
  13. package/bin/lib/css/repetition.mjs +213 -0
  14. package/bin/lib/css/repetition.test.mjs +166 -0
  15. package/bin/lib/css/semantic.mjs +327 -0
  16. package/bin/lib/css/semantic.test.mjs +201 -0
  17. package/bin/lib/css/tokens.mjs +119 -0
  18. package/bin/lib/doctor/doctor.e2e.test.mjs +45 -0
  19. package/bin/tempest.mjs +169 -7
  20. package/dist/charts/scales.cjs +2 -0
  21. package/dist/charts/scales.cjs.map +1 -0
  22. package/dist/charts/scales.js +29 -0
  23. package/dist/charts/scales.js.map +1 -0
  24. package/dist/charts.cjs +1 -1
  25. package/dist/charts.d.ts +91 -0
  26. package/dist/charts.js +9 -8
  27. package/dist/components/BottomNavigation/BottomNavigation.module.cjs.map +1 -1
  28. package/dist/components/BottomNavigation/BottomNavigation.module.js.map +1 -1
  29. package/dist/components/Carousel/Carousel.module.cjs.map +1 -1
  30. package/dist/components/Carousel/Carousel.module.js.map +1 -1
  31. package/dist/components/CodeBlock/CodeBlock.cjs +3 -0
  32. package/dist/components/CodeBlock/CodeBlock.cjs.map +1 -0
  33. package/dist/components/CodeBlock/CodeBlock.js +53 -0
  34. package/dist/components/CodeBlock/CodeBlock.js.map +1 -0
  35. package/dist/components/CodeBlock/CodeBlock.module.cjs +2 -0
  36. package/dist/components/CodeBlock/CodeBlock.module.cjs.map +1 -0
  37. package/dist/components/CodeBlock/CodeBlock.module.js +29 -0
  38. package/dist/components/CodeBlock/CodeBlock.module.js.map +1 -0
  39. package/dist/components/CodeBlock/tokenize.cjs +3 -0
  40. package/dist/components/CodeBlock/tokenize.cjs.map +1 -0
  41. package/dist/components/CodeBlock/tokenize.js +267 -0
  42. package/dist/components/CodeBlock/tokenize.js.map +1 -0
  43. package/dist/components/Navbar/Navbar.module.cjs.map +1 -1
  44. package/dist/components/Navbar/Navbar.module.js.map +1 -1
  45. package/dist/components/QRCode/QRCode.cjs +2 -0
  46. package/dist/components/QRCode/QRCode.cjs.map +1 -0
  47. package/dist/components/QRCode/QRCode.js +49 -0
  48. package/dist/components/QRCode/QRCode.js.map +1 -0
  49. package/dist/components/QRCode/QRCode.module.cjs +2 -0
  50. package/dist/components/QRCode/QRCode.module.cjs.map +1 -0
  51. package/dist/components/QRCode/QRCode.module.js +9 -0
  52. package/dist/components/QRCode/QRCode.module.js.map +1 -0
  53. package/dist/components/QRCode/qr-encode.cjs +2 -0
  54. package/dist/components/QRCode/qr-encode.cjs.map +1 -0
  55. package/dist/components/QRCode/qr-encode.js +295 -0
  56. package/dist/components/QRCode/qr-encode.js.map +1 -0
  57. package/dist/components/QRCode/qr-tables.cjs +2 -0
  58. package/dist/components/QRCode/qr-tables.cjs.map +1 -0
  59. package/dist/components/QRCode/qr-tables.js +366 -0
  60. package/dist/components/QRCode/qr-tables.js.map +1 -0
  61. package/dist/components/ScrollArea/ScrollArea.cjs +1 -1
  62. package/dist/components/ScrollArea/ScrollArea.cjs.map +1 -1
  63. package/dist/components/ScrollArea/ScrollArea.js +22 -16
  64. package/dist/components/ScrollArea/ScrollArea.js.map +1 -1
  65. package/dist/components/ScrollArea/ScrollArea.module.cjs.map +1 -1
  66. package/dist/components/ScrollArea/ScrollArea.module.js.map +1 -1
  67. package/dist/components/Sparkline/Sparkline.cjs +2 -0
  68. package/dist/components/Sparkline/Sparkline.cjs.map +1 -0
  69. package/dist/components/Sparkline/Sparkline.js +64 -0
  70. package/dist/components/Sparkline/Sparkline.js.map +1 -0
  71. package/dist/components/Sparkline/Sparkline.module.cjs +2 -0
  72. package/dist/components/Sparkline/Sparkline.module.cjs.map +1 -0
  73. package/dist/components/Sparkline/Sparkline.module.js +10 -0
  74. package/dist/components/Sparkline/Sparkline.module.js.map +1 -0
  75. package/dist/components/Sparkline/sparkline-geometry.cjs +2 -0
  76. package/dist/components/Sparkline/sparkline-geometry.cjs.map +1 -0
  77. package/dist/components/Sparkline/sparkline-geometry.js +56 -0
  78. package/dist/components/Sparkline/sparkline-geometry.js.map +1 -0
  79. package/dist/components/Table/Table.cjs +1 -1
  80. package/dist/components/Table/Table.cjs.map +1 -1
  81. package/dist/components/Table/Table.js +37 -30
  82. package/dist/components/Table/Table.js.map +1 -1
  83. package/dist/components/Table/Table.module.cjs.map +1 -1
  84. package/dist/components/Table/Table.module.js.map +1 -1
  85. package/dist/components/VirtualList/VirtualList.cjs +1 -1
  86. package/dist/components/VirtualList/VirtualList.cjs.map +1 -1
  87. package/dist/components/VirtualList/VirtualList.js +13 -11
  88. package/dist/components/VirtualList/VirtualList.js.map +1 -1
  89. package/dist/components/VirtualList/VirtualList.module.cjs.map +1 -1
  90. package/dist/components/VirtualList/VirtualList.module.js.map +1 -1
  91. package/dist/hooks/use-scroll-overflow.cjs +2 -0
  92. package/dist/hooks/use-scroll-overflow.cjs.map +1 -0
  93. package/dist/hooks/use-scroll-overflow.js +22 -0
  94. package/dist/hooks/use-scroll-overflow.js.map +1 -0
  95. package/dist/push/utils.cjs +1 -1
  96. package/dist/push/utils.cjs.map +1 -1
  97. package/dist/push/utils.js +1 -1
  98. package/dist/push/utils.js.map +1 -1
  99. package/dist/styles.css +1 -1
  100. package/dist/tempest-react-sdk.cjs +1 -1
  101. package/dist/tempest-react-sdk.d.ts +450 -2
  102. package/dist/tempest-react-sdk.js +197 -190
  103. package/dist/theme/create-theme.cjs +3 -3
  104. package/dist/theme/create-theme.cjs.map +1 -1
  105. package/dist/theme/create-theme.js +52 -28
  106. package/dist/theme/create-theme.js.map +1 -1
  107. package/dist/theme/data-viz-ramps.cjs +2 -0
  108. package/dist/theme/data-viz-ramps.cjs.map +1 -0
  109. package/dist/theme/data-viz-ramps.js +58 -0
  110. package/dist/theme/data-viz-ramps.js.map +1 -0
  111. package/package.json +2 -1
@@ -155,6 +155,51 @@ describe("tempest doctor — generic findings still surface", () => {
155
155
  });
156
156
  });
157
157
 
158
+ describe("tempest doctor — stylesheets", () => {
159
+ beforeEach(thirdPartyApp);
160
+
161
+ it("says nothing about CSS in a project with no stylesheet", () => {
162
+ expect(doctor().out).not.toContain("Stylesheets");
163
+ });
164
+
165
+ it("reports a clean sheet as clean", () => {
166
+ write("src/app.css", ".a {\n color: red;\n}\n");
167
+ const { out, code } = doctor();
168
+ expect(out).toContain("Stylesheets");
169
+ expect(out).toContain("no CSS problems found");
170
+ expect(code).toBe(0);
171
+ });
172
+
173
+ it("fails the audit on CSS the browser cannot parse", () => {
174
+ write("src/app.css", ".a {\n color: red;\n");
175
+ const { out, code } = doctor();
176
+ expect(out).toContain("src/app.css:1");
177
+ expect(out).toContain("never closed");
178
+ expect(code).toBe(1);
179
+ });
180
+
181
+ it("warns without failing on CSS that is valid but wrong", () => {
182
+ write("src/app.css", ".a {\n bacground-color: red;\n}\n");
183
+ const { out, code } = doctor();
184
+ expect(out).toContain("did you mean `background-color`");
185
+ expect(code).toBe(0);
186
+ });
187
+
188
+ it("points at the fix command when something is auto-fixable", () => {
189
+ write("src/app.css", ".a {\n color: red;\n color: red;\n}\n");
190
+ const { out } = doctor();
191
+ expect(out).toMatch(/finding\(s\) are auto-fixable/);
192
+ });
193
+
194
+ it("suggests one global class over repeated local copies", () => {
195
+ const row = ".row {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n";
196
+ write("src/A.module.css", row);
197
+ write("src/B.module.css", row);
198
+ write("src/C.module.css", row);
199
+ expect(doctor().out).toContain("one global class beats");
200
+ });
201
+ });
202
+
158
203
  describe("tempest doctor — a project that does use the SDK", () => {
159
204
  beforeEach(() => {
160
205
  thirdPartyApp();
package/bin/tempest.mjs CHANGED
@@ -14,6 +14,7 @@ import { fileURLToPath } from "node:url";
14
14
  import { aliasImports } from "./lib/alias/index.mjs";
15
15
  import { loadTypeScript } from "./lib/alias/typescript.mjs";
16
16
  import { readTsconfig } from "./lib/alias/tsconfig.mjs";
17
+ import { analyzeCss, applyCssFixes } from "./lib/css/index.mjs";
17
18
  import { checkLucide } from "./lib/doctor/lucide.mjs";
18
19
  import { generateRegistry, loadIconTables } from "./lib/icons/generate.mjs";
19
20
  import { generate } from "./lib/openapi/generate.mjs";
@@ -250,6 +251,74 @@ const STATEFUL_DEPS = [
250
251
  "react-router-dom",
251
252
  ];
252
253
 
254
+ /**
255
+ * Doctor rows for the CSS analysis.
256
+ *
257
+ * Findings are capped per severity because a report nobody reads to the end is a
258
+ * report that hides its own first line. The cap is stated, and the pointer to the
259
+ * uncapped list (`tempest fix --dry-run`) goes with it — a silent truncation
260
+ * would read as "that is all of them".
261
+ *
262
+ * @param {number} [limit] - Findings shown per severity.
263
+ * @returns {Array<[status: string, label: string, detail?: string]>} Check rows.
264
+ */
265
+ function cssChecks(limit = 6) {
266
+ let analysis;
267
+ try {
268
+ analysis = analyzeCss({ root: ROOT, targets: ["."], selfDir: SELF_DIR });
269
+ } catch (err) {
270
+ return [["warn", "CSS analysis failed", String(err?.message ?? err)]];
271
+ }
272
+ if (analysis.stats.files === 0) return [];
273
+
274
+ const rows = [["section", "Stylesheets"]];
275
+ rows.push([
276
+ "info",
277
+ `${analysis.stats.files} stylesheet(s) · ${analysis.stats.rules} rules · ${analysis.stats.declarations} declarations`,
278
+ analysis.tokens.source ? "" : "SDK token table not found — token checks skipped",
279
+ ]);
280
+ if (analysis.truncated) {
281
+ rows.push([
282
+ "info",
283
+ "file cap reached",
284
+ "only the first 600 stylesheets were analyzed — narrow the run with `tempest fix <path> --dry-run`",
285
+ ]);
286
+ }
287
+
288
+ const status = { error: "fail", warn: "warn", info: "info" };
289
+ let hidden = 0;
290
+ for (const severity of ["error", "warn", "info"]) {
291
+ const group = analysis.findings.filter((f) => f.severity === severity);
292
+ for (const f of group.slice(0, limit)) {
293
+ rows.push([status[severity], `${f.file}:${f.line}`, f.message]);
294
+ }
295
+ hidden += Math.max(0, group.length - limit);
296
+ }
297
+ if (hidden > 0) {
298
+ rows.push([
299
+ "info",
300
+ `${hidden} more CSS finding(s) not shown`,
301
+ "run `tempest fix --dry-run` for the full list",
302
+ ]);
303
+ }
304
+ if (analysis.findings.length === 0) {
305
+ rows.push(["ok", "no CSS problems found"]);
306
+ } else {
307
+ const fixable = analysis.findings.filter((f) => f.fixable).length;
308
+ if (fixable > 0) {
309
+ rows.push([
310
+ "info",
311
+ `${fixable} finding(s) are auto-fixable`,
312
+ "run `tempest fix` — it removes only what is provably dead",
313
+ ]);
314
+ }
315
+ }
316
+ for (const skip of analysis.skipped.slice(0, 3)) {
317
+ rows.push(["info", `skipped ${skip.file}`, skip.reason]);
318
+ }
319
+ return rows;
320
+ }
321
+
253
322
  function doctor() {
254
323
  const checks = [];
255
324
  const pkg = readJSON(join(ROOT, "package.json"));
@@ -581,6 +650,9 @@ function doctor() {
581
650
  ]);
582
651
  }
583
652
 
653
+ // ── Stylesheets ───────────────────────────────────────────────────────────
654
+ checks.push(...cssChecks());
655
+
584
656
  // ── Tooling ────────────────────────────────────────────────────────────────
585
657
  checks.push(["section", "Tooling"]);
586
658
  checks.push(
@@ -742,7 +814,7 @@ function lint(args) {
742
814
  }
743
815
 
744
816
  /** Flags `fix` owns itself; anything else is rejected rather than forwarded. */
745
- const FIX_FLAGS = new Set(["--no-alias", "--dry-run"]);
817
+ const FIX_FLAGS = new Set(["--no-alias", "--no-css", "--dry-run"]);
746
818
 
747
819
  /**
748
820
  * Print the alias pass result.
@@ -785,6 +857,91 @@ function reportAlias(result, dryRun) {
785
857
  );
786
858
  }
787
859
 
860
+ /** Prefix for a finding line: red for a browser-visible defect, yellow otherwise. */
861
+ function severityMark(severity) {
862
+ if (severity === "error") return `${c.red}✗${c.reset}`;
863
+ return severity === "warn" ? `${c.yellow}!${c.reset}` : `${c.cyan}i${c.reset}`;
864
+ }
865
+
866
+ /**
867
+ * The CSS pass of `fix`: report what the analysis found, then remove the subset
868
+ * that is provably dead.
869
+ *
870
+ * Findings that need a human are printed in full here rather than capped, because
871
+ * `--dry-run` is the review surface — `doctor` is the summary, this is the list
872
+ * you read before letting the tool write. Only the advisory (`info`) tail is
873
+ * capped, since "this could be a token" scales with the size of the project.
874
+ *
875
+ * @param {object} params
876
+ * @param {string[]} params.targets - Positional paths from the command line.
877
+ * @param {boolean} params.dryRun
878
+ * @returns {number} Exit status contribution.
879
+ */
880
+ function cssPass({ targets, dryRun }) {
881
+ console.log(
882
+ `${c.dim}→ css (dedupe declarations · drop dead rules)${dryRun ? " [dry-run]" : ""}${c.reset}`,
883
+ );
884
+ let analysis;
885
+ try {
886
+ analysis = analyzeCss({ root: ROOT, targets, selfDir: SELF_DIR });
887
+ } catch (err) {
888
+ console.log(
889
+ ` ${c.red}✗ analysis failed${c.reset} ${c.dim}${err?.message ?? err}${c.reset}`,
890
+ );
891
+ return 1;
892
+ }
893
+ if (analysis.stats.files === 0) {
894
+ console.log(` ${c.dim}no stylesheets found${c.reset}`);
895
+ return 0;
896
+ }
897
+
898
+ const manual = analysis.findings.filter((f) => !f.fixable);
899
+ const infoLimit = 10;
900
+ let shownInfo = 0;
901
+ let hiddenInfo = 0;
902
+ for (const f of manual) {
903
+ if (f.severity === "info") {
904
+ if (shownInfo >= infoLimit) {
905
+ hiddenInfo += 1;
906
+ continue;
907
+ }
908
+ shownInfo += 1;
909
+ }
910
+ console.log(
911
+ ` [${severityMark(f.severity)}] ${f.file}:${f.line} ${c.dim}${f.message}${c.reset}`,
912
+ );
913
+ }
914
+ if (hiddenInfo > 0) {
915
+ console.log(` ${c.dim}…+${hiddenInfo} more suggestion(s)${c.reset}`);
916
+ }
917
+
918
+ const result = applyCssFixes({ analysis, dryRun });
919
+ for (const file of result.files) {
920
+ console.log(` ${file.file} ${c.dim}${file.changes.length}${c.reset}`);
921
+ for (const change of file.changes) {
922
+ console.log(` ${c.dim}${change.line}:${c.reset} ${change.message}`);
923
+ }
924
+ }
925
+ for (const err of result.errors) {
926
+ console.log(` ${c.red}✗ ${err.file}${c.reset} ${c.dim}${err.message}${c.reset}`);
927
+ }
928
+
929
+ if (result.total === 0) {
930
+ console.log(` ${c.dim}nothing to remove${c.reset}`);
931
+ } else {
932
+ const verb = dryRun ? "would remove" : "removed";
933
+ console.log(
934
+ ` ${c.green}✓${c.reset} ${verb} ${result.total} dead declaration(s)/rule(s) in ${result.files.length} file(s)`,
935
+ );
936
+ }
937
+ if (analysis.counts.error > 0) {
938
+ console.log(
939
+ ` ${c.red}✗ ${analysis.counts.error} CSS syntax error(s)${c.reset} ${c.dim}— those files were not touched; fix them and run again${c.reset}`,
940
+ );
941
+ }
942
+ return result.errors.length || analysis.counts.error ? 1 : 0;
943
+ }
944
+
788
945
  function fix(args) {
789
946
  const { flags, paths } = splitArgs(args);
790
947
  const unknown = flags.filter((f) => !FIX_FLAGS.has(f));
@@ -804,7 +961,10 @@ function fix(args) {
804
961
  reportAlias(result, dryRun);
805
962
  aliasStatus = result.status === "error" ? 1 : 0;
806
963
  }
807
- if (dryRun) return aliasStatus;
964
+
965
+ const cssStatus = flags.includes("--no-css") ? 0 : cssPass({ targets, dryRun });
966
+
967
+ if (dryRun) return aliasStatus || cssStatus;
808
968
 
809
969
  console.log(`${c.dim}→ eslint --fix (sort imports · drop unused · tidy whitespace)${c.reset}`);
810
970
  const eslintStatus = run(requireBin("eslint"), [...targets, "--fix"]);
@@ -816,7 +976,7 @@ function fix(args) {
816
976
  } else {
817
977
  console.log(`${c.yellow}! prettier not installed — skipping format pass${c.reset}`);
818
978
  }
819
- return aliasStatus || eslintStatus || prettierStatus;
979
+ return aliasStatus || cssStatus || eslintStatus || prettierStatus;
820
980
  }
821
981
 
822
982
  function format(args) {
@@ -952,8 +1112,8 @@ ${c.bold}Usage${c.reset}
952
1112
  ${c.bold}Commands${c.reset}
953
1113
  ${c.bold}doctor${c.reset} Health-check the current project
954
1114
  ${c.bold}lint${c.reset} [paths] Run ESLint (report only)
955
- ${c.bold}fix${c.reset} [paths] Alias imports (../ → @/) + ESLint --fix (sort imports, remove
956
- unused, tidy whitespace) + Prettier
1115
+ ${c.bold}fix${c.reset} [paths] Alias imports (../ → @/) + CSS dead-code removal + ESLint --fix
1116
+ (sort imports, remove unused, tidy whitespace) + Prettier
957
1117
  ${c.bold}format${c.reset} [paths] Prettier --write
958
1118
  ${c.bold}gen api${c.reset} <src> Generate Zod schemas + types + service classes from an OpenAPI spec
959
1119
  (e.g. tempest gen api http://127.0.0.1:8000/openapi.json --out src/api)
@@ -961,8 +1121,10 @@ ${c.bold}Commands${c.reset}
961
1121
  (e.g. tempest gen icons --out src/icons.generated.ts --dir src)
962
1122
 
963
1123
  ${c.bold}fix options${c.reset}
964
- --dry-run List the imports that would be rewritten; write nothing
965
- --no-alias Skip the alias pass (ESLint + Prettier only)
1124
+ --dry-run List every CSS finding and the imports that would be rewritten;
1125
+ write nothing
1126
+ --no-alias Skip the alias pass
1127
+ --no-css Skip the CSS pass (analysis and dead-code removal)
966
1128
 
967
1129
  ${c.bold}Options${c.reset}
968
1130
  -h, --help Show this help
@@ -0,0 +1,2 @@
1
+ var e=7,t=9,n=3;function r(e){return`var(--tempest-chart-${e})`}function i(e,t,n){if(!Number.isFinite(e))return 0;let r=Math.min(t,n),i=Math.max(t,n);return i===r?.5:Math.min(1,Math.max(0,(e-r)/(i-r)))}function a(e,t){return Math.min(t,Math.max(1,Math.round(e*(t-1))+1))}function o(e){let{min:t,max:n,ordinal:o=!1}=e,s=o?3:1,c=7-s+1;return e=>r(`sequential-${s+a(i(e,t,n),c)-1}`)}function s(e){let{min:t,max:n,center:i=0}=e,o=Math.abs(i-Math.min(t,n)),s=Math.abs(Math.max(t,n)-i);return e=>r(!Number.isFinite(e)||e===i?`diverging-5`:e<i?o===0?`diverging-5`:`diverging-${5-a(Math.min(1,(i-e)/o),4)}`:s===0?`diverging-5`:`diverging-${5+a(Math.min(1,(e-i)/s),4)}`)}function c(e){let t=e===`sequential`?7:9;return Array.from({length:t},(t,n)=>r(`${e}-${n+1}`))}exports.DIVERGING_STEP_COUNT=t,exports.ORDINAL_START_STEP=n,exports.SEQUENTIAL_STEP_COUNT=e,exports.divergingScale=s,exports.scaleSteps=c,exports.sequentialScale=o;
2
+ //# sourceMappingURL=scales.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scales.cjs","names":[],"sources":["../../src/charts/scales.ts"],"sourcesContent":["/** How many steps the sequential token scale has. */\nexport const SEQUENTIAL_STEP_COUNT = 7;\n\n/** How many steps the diverging token scale has, midpoint included. */\nexport const DIVERGING_STEP_COUNT = 9;\n\n/**\n * First sequential step that clears 2:1 against the chart surface.\n *\n * A sequential scale may let its near-zero end recede into the surface — on a\n * heatmap that is exactly what \"almost nothing\" should look like. An **ordinal**\n * scale may not: every step is a discrete mark someone has to see. Starting an\n * ordinal ramp here is the difference.\n */\nexport const ORDINAL_START_STEP = 3;\n\n/** A `var(--tempest-chart-…)` reference, so the value follows the active theme. */\nexport type ChartColorToken = string;\n\n/** Reference a token by name rather than interpolating the string at each call site. */\nfunction token(name: string): ChartColorToken {\n return `var(--tempest-chart-${name})`;\n}\n\n/**\n * Clamp `value` into `0…1` against a domain, tolerating a reversed or empty one.\n *\n * A zero-width domain (every datum equal) would otherwise divide by zero and paint\n * the whole chart `NaN`; it resolves to the middle of the scale instead, which is\n * the honest reading of \"no variation\".\n */\nfunction normalize(value: number, min: number, max: number): number {\n if (!Number.isFinite(value)) return 0;\n const lo = Math.min(min, max);\n const hi = Math.max(min, max);\n if (hi === lo) return 0.5;\n return Math.min(1, Math.max(0, (value - lo) / (hi - lo)));\n}\n\n/** Map a `0…1` position onto `1…steps`, inclusive. */\nfunction stepOf(t: number, steps: number): number {\n return Math.min(steps, Math.max(1, Math.round(t * (steps - 1)) + 1));\n}\n\nexport interface SequentialScaleOptions {\n /** Lowest value in the data. */\n min: number;\n /** Highest value in the data. */\n max: number;\n /**\n * Keep every step visible against the surface, for discrete ordered marks.\n *\n * Off by default: a heatmap *wants* its near-zero cells to recede. Turn it on\n * for tiers, funnel stages or anything where each step is its own mark.\n */\n ordinal?: boolean;\n}\n\n/**\n * Build a magnitude scale over the sequential tokens.\n *\n * Returns `var(--tempest-chart-sequential-N)` rather than a hex string, so a\n * heatmap painted once follows the theme — including dark mode, whose steps are\n * chosen for the dark surface rather than flipped.\n *\n * @example\n * const color = sequentialScale({ min: 0, max: 250 });\n * <rect fill={color(value)} />\n *\n * @param options - The data domain, and whether every step must stay visible.\n * @returns A function from value to a CSS colour reference.\n */\nexport function sequentialScale(\n options: SequentialScaleOptions,\n): (value: number) => ChartColorToken {\n const { min, max, ordinal = false } = options;\n const first = ordinal ? ORDINAL_START_STEP : 1;\n const span = SEQUENTIAL_STEP_COUNT - first + 1;\n return (value) => token(`sequential-${first + stepOf(normalize(value, min, max), span) - 1}`);\n}\n\nexport interface DivergingScaleOptions {\n /** Lowest value in the data. */\n min: number;\n /** Highest value in the data. */\n max: number;\n /**\n * The value that means \"no deviation\". Default `0`.\n *\n * It is a parameter because the interesting midpoint is often not zero — a\n * budget variance diverges around the target, not around nothing.\n */\n center?: number;\n}\n\n/**\n * Build a polarity scale over the diverging tokens.\n *\n * Each arm is scaled against its **own** distance from the centre, so an asymmetric\n * domain (say −5…+80) still uses the full cool arm for its small negatives. Scaling\n * both arms by the wider one — the easy mistake — would collapse every negative\n * into the step next to the midpoint and hide the sign entirely.\n *\n * @example\n * const color = divergingScale({ min: -12, max: 40 }); // centre 0\n * const budget = divergingScale({ min: 80, max: 130, center: 100 });\n *\n * @param options - The data domain and the neutral centre.\n * @returns A function from value to a CSS colour reference.\n */\nexport function divergingScale(options: DivergingScaleOptions): (value: number) => ChartColorToken {\n const { min, max, center = 0 } = options;\n const mid = Math.ceil(DIVERGING_STEP_COUNT / 2);\n const armSteps = mid - 1;\n const coolSpan = Math.abs(center - Math.min(min, max));\n const warmSpan = Math.abs(Math.max(min, max) - center);\n\n return (value) => {\n if (!Number.isFinite(value) || value === center) return token(`diverging-${mid}`);\n if (value < center) {\n if (coolSpan === 0) return token(`diverging-${mid}`);\n const t = Math.min(1, (center - value) / coolSpan);\n // Step 1 is the cool extreme, so a bigger deviation walks toward it.\n return token(`diverging-${mid - stepOf(t, armSteps)}`);\n }\n if (warmSpan === 0) return token(`diverging-${mid}`);\n const t = Math.min(1, (value - center) / warmSpan);\n return token(`diverging-${mid + stepOf(t, armSteps)}`);\n };\n}\n\n/**\n * Every step of a token scale, in order — for rendering a legend.\n *\n * A continuous scale needs a legend showing the ramp with its end labels; without\n * one the reader has no way to turn a colour back into a number.\n *\n * @param kind - Which scale.\n * @returns The token references, lightest/coolest first.\n */\nexport function scaleSteps(kind: \"sequential\" | \"diverging\"): ChartColorToken[] {\n const count = kind === \"sequential\" ? SEQUENTIAL_STEP_COUNT : DIVERGING_STEP_COUNT;\n return Array.from({ length: count }, (_, i) => token(`${kind}-${i + 1}`));\n}\n"],"mappings":"AACA,IAAa,EAAwB,EAGxB,EAAuB,EAUvB,EAAqB,EAMlC,SAAS,EAAM,EAA+B,CAC1C,MAAO,uBAAuB,EAAK,EACvC,CASA,SAAS,EAAU,EAAe,EAAa,EAAqB,CAChE,GAAI,CAAC,OAAO,SAAS,CAAK,EAAG,MAAO,GACpC,IAAM,EAAK,KAAK,IAAI,EAAK,CAAG,EACtB,EAAK,KAAK,IAAI,EAAK,CAAG,EAE5B,OADI,IAAO,EAAW,GACf,KAAK,IAAI,EAAG,KAAK,IAAI,GAAI,EAAQ,IAAO,EAAK,EAAG,CAAC,CAC5D,CAGA,SAAS,EAAO,EAAW,EAAuB,CAC9C,OAAO,KAAK,IAAI,EAAO,KAAK,IAAI,EAAG,KAAK,MAAM,GAAK,EAAQ,EAAE,EAAI,CAAC,CAAC,CACvE,CA8BA,SAAgB,EACZ,EACkC,CAClC,GAAM,CAAE,MAAK,MAAK,UAAU,IAAU,EAChC,EAAQ,EAAA,EAA+B,EACvC,EAAA,EAA+B,EAAQ,EAC7C,MAAQ,IAAU,EAAM,cAAc,EAAQ,EAAO,EAAU,EAAO,EAAK,CAAG,EAAG,CAAI,EAAI,GAAG,CAChG,CA+BA,SAAgB,EAAe,EAAoE,CAC/F,GAAM,CAAE,MAAK,MAAK,SAAS,GAAM,EAG3B,EAAW,KAAK,IAAI,EAAS,KAAK,IAAI,EAAK,CAAG,CAAC,EAC/C,EAAW,KAAK,IAAI,KAAK,IAAI,EAAK,CAAG,EAAI,CAAM,EAErD,MAAQ,IACoD,EAApD,CAAC,OAAO,SAAS,CAAK,GAAK,IAAU,EAAqB,cAC1D,EAAQ,EACJ,IAAa,EAAgB,cAGpB,aAAa,EAAM,EAFtB,KAAK,IAAI,GAAI,EAAS,GAAS,CAEF,EAAG,CAAQ,IAElD,IAAa,EAAgB,cAEpB,aAAa,EAAM,EADtB,KAAK,IAAI,GAAI,EAAQ,GAAU,CACF,EAAG,CAAQ,GAT8B,CAWxF,CAWA,SAAgB,EAAW,EAAqD,CAC5E,IAAM,EAAQ,IAAS,aAAA,EAAA,EACvB,OAAO,MAAM,KAAK,CAAE,OAAQ,CAAM,GAAI,EAAG,IAAM,EAAM,GAAG,EAAK,GAAG,EAAI,GAAG,CAAC,CAC5E"}
@@ -0,0 +1,29 @@
1
+ //#region src/charts/scales.ts
2
+ var e = 7, t = 9, n = 3;
3
+ function r(e) {
4
+ return `var(--tempest-chart-${e})`;
5
+ }
6
+ function i(e, t, n) {
7
+ if (!Number.isFinite(e)) return 0;
8
+ let r = Math.min(t, n), i = Math.max(t, n);
9
+ return i === r ? .5 : Math.min(1, Math.max(0, (e - r) / (i - r)));
10
+ }
11
+ function a(e, t) {
12
+ return Math.min(t, Math.max(1, Math.round(e * (t - 1)) + 1));
13
+ }
14
+ function o(e) {
15
+ let { min: t, max: n, ordinal: o = !1 } = e, s = o ? 3 : 1, c = 7 - s + 1;
16
+ return (e) => r(`sequential-${s + a(i(e, t, n), c) - 1}`);
17
+ }
18
+ function s(e) {
19
+ let { min: t, max: n, center: i = 0 } = e, o = Math.abs(i - Math.min(t, n)), s = Math.abs(Math.max(t, n) - i);
20
+ return (e) => r(!Number.isFinite(e) || e === i ? "diverging-5" : e < i ? o === 0 ? "diverging-5" : `diverging-${5 - a(Math.min(1, (i - e) / o), 4)}` : s === 0 ? "diverging-5" : `diverging-${5 + a(Math.min(1, (e - i) / s), 4)}`);
21
+ }
22
+ function c(e) {
23
+ let t = e === "sequential" ? 7 : 9;
24
+ return Array.from({ length: t }, (t, n) => r(`${e}-${n + 1}`));
25
+ }
26
+ //#endregion
27
+ export { t as DIVERGING_STEP_COUNT, n as ORDINAL_START_STEP, e as SEQUENTIAL_STEP_COUNT, s as divergingScale, c as scaleSteps, o as sequentialScale };
28
+
29
+ //# sourceMappingURL=scales.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scales.js","names":[],"sources":["../../src/charts/scales.ts"],"sourcesContent":["/** How many steps the sequential token scale has. */\nexport const SEQUENTIAL_STEP_COUNT = 7;\n\n/** How many steps the diverging token scale has, midpoint included. */\nexport const DIVERGING_STEP_COUNT = 9;\n\n/**\n * First sequential step that clears 2:1 against the chart surface.\n *\n * A sequential scale may let its near-zero end recede into the surface — on a\n * heatmap that is exactly what \"almost nothing\" should look like. An **ordinal**\n * scale may not: every step is a discrete mark someone has to see. Starting an\n * ordinal ramp here is the difference.\n */\nexport const ORDINAL_START_STEP = 3;\n\n/** A `var(--tempest-chart-…)` reference, so the value follows the active theme. */\nexport type ChartColorToken = string;\n\n/** Reference a token by name rather than interpolating the string at each call site. */\nfunction token(name: string): ChartColorToken {\n return `var(--tempest-chart-${name})`;\n}\n\n/**\n * Clamp `value` into `0…1` against a domain, tolerating a reversed or empty one.\n *\n * A zero-width domain (every datum equal) would otherwise divide by zero and paint\n * the whole chart `NaN`; it resolves to the middle of the scale instead, which is\n * the honest reading of \"no variation\".\n */\nfunction normalize(value: number, min: number, max: number): number {\n if (!Number.isFinite(value)) return 0;\n const lo = Math.min(min, max);\n const hi = Math.max(min, max);\n if (hi === lo) return 0.5;\n return Math.min(1, Math.max(0, (value - lo) / (hi - lo)));\n}\n\n/** Map a `0…1` position onto `1…steps`, inclusive. */\nfunction stepOf(t: number, steps: number): number {\n return Math.min(steps, Math.max(1, Math.round(t * (steps - 1)) + 1));\n}\n\nexport interface SequentialScaleOptions {\n /** Lowest value in the data. */\n min: number;\n /** Highest value in the data. */\n max: number;\n /**\n * Keep every step visible against the surface, for discrete ordered marks.\n *\n * Off by default: a heatmap *wants* its near-zero cells to recede. Turn it on\n * for tiers, funnel stages or anything where each step is its own mark.\n */\n ordinal?: boolean;\n}\n\n/**\n * Build a magnitude scale over the sequential tokens.\n *\n * Returns `var(--tempest-chart-sequential-N)` rather than a hex string, so a\n * heatmap painted once follows the theme — including dark mode, whose steps are\n * chosen for the dark surface rather than flipped.\n *\n * @example\n * const color = sequentialScale({ min: 0, max: 250 });\n * <rect fill={color(value)} />\n *\n * @param options - The data domain, and whether every step must stay visible.\n * @returns A function from value to a CSS colour reference.\n */\nexport function sequentialScale(\n options: SequentialScaleOptions,\n): (value: number) => ChartColorToken {\n const { min, max, ordinal = false } = options;\n const first = ordinal ? ORDINAL_START_STEP : 1;\n const span = SEQUENTIAL_STEP_COUNT - first + 1;\n return (value) => token(`sequential-${first + stepOf(normalize(value, min, max), span) - 1}`);\n}\n\nexport interface DivergingScaleOptions {\n /** Lowest value in the data. */\n min: number;\n /** Highest value in the data. */\n max: number;\n /**\n * The value that means \"no deviation\". Default `0`.\n *\n * It is a parameter because the interesting midpoint is often not zero — a\n * budget variance diverges around the target, not around nothing.\n */\n center?: number;\n}\n\n/**\n * Build a polarity scale over the diverging tokens.\n *\n * Each arm is scaled against its **own** distance from the centre, so an asymmetric\n * domain (say −5…+80) still uses the full cool arm for its small negatives. Scaling\n * both arms by the wider one — the easy mistake — would collapse every negative\n * into the step next to the midpoint and hide the sign entirely.\n *\n * @example\n * const color = divergingScale({ min: -12, max: 40 }); // centre 0\n * const budget = divergingScale({ min: 80, max: 130, center: 100 });\n *\n * @param options - The data domain and the neutral centre.\n * @returns A function from value to a CSS colour reference.\n */\nexport function divergingScale(options: DivergingScaleOptions): (value: number) => ChartColorToken {\n const { min, max, center = 0 } = options;\n const mid = Math.ceil(DIVERGING_STEP_COUNT / 2);\n const armSteps = mid - 1;\n const coolSpan = Math.abs(center - Math.min(min, max));\n const warmSpan = Math.abs(Math.max(min, max) - center);\n\n return (value) => {\n if (!Number.isFinite(value) || value === center) return token(`diverging-${mid}`);\n if (value < center) {\n if (coolSpan === 0) return token(`diverging-${mid}`);\n const t = Math.min(1, (center - value) / coolSpan);\n // Step 1 is the cool extreme, so a bigger deviation walks toward it.\n return token(`diverging-${mid - stepOf(t, armSteps)}`);\n }\n if (warmSpan === 0) return token(`diverging-${mid}`);\n const t = Math.min(1, (value - center) / warmSpan);\n return token(`diverging-${mid + stepOf(t, armSteps)}`);\n };\n}\n\n/**\n * Every step of a token scale, in order — for rendering a legend.\n *\n * A continuous scale needs a legend showing the ramp with its end labels; without\n * one the reader has no way to turn a colour back into a number.\n *\n * @param kind - Which scale.\n * @returns The token references, lightest/coolest first.\n */\nexport function scaleSteps(kind: \"sequential\" | \"diverging\"): ChartColorToken[] {\n const count = kind === \"sequential\" ? SEQUENTIAL_STEP_COUNT : DIVERGING_STEP_COUNT;\n return Array.from({ length: count }, (_, i) => token(`${kind}-${i + 1}`));\n}\n"],"mappings":";AACA,IAAa,IAAwB,GAGxB,IAAuB,GAUvB,IAAqB;AAMlC,SAAS,EAAM,GAA+B;CAC1C,OAAO,uBAAuB,EAAK;AACvC;AASA,SAAS,EAAU,GAAe,GAAa,GAAqB;CAChE,IAAI,CAAC,OAAO,SAAS,CAAK,GAAG,OAAO;CACpC,IAAM,IAAK,KAAK,IAAI,GAAK,CAAG,GACtB,IAAK,KAAK,IAAI,GAAK,CAAG;CAE5B,OADI,MAAO,IAAW,KACf,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,IAAQ,MAAO,IAAK,EAAG,CAAC;AAC5D;AAGA,SAAS,EAAO,GAAW,GAAuB;CAC9C,OAAO,KAAK,IAAI,GAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,IAAQ,EAAE,IAAI,CAAC,CAAC;AACvE;AA8BA,SAAgB,EACZ,GACkC;CAClC,IAAM,EAAE,QAAK,QAAK,aAAU,OAAU,GAChC,IAAQ,IAAA,IAA+B,GACvC,IAAA,IAA+B,IAAQ;CAC7C,QAAQ,MAAU,EAAM,cAAc,IAAQ,EAAO,EAAU,GAAO,GAAK,CAAG,GAAG,CAAI,IAAI,GAAG;AAChG;AA+BA,SAAgB,EAAe,GAAoE;CAC/F,IAAM,EAAE,QAAK,QAAK,YAAS,MAAM,GAG3B,IAAW,KAAK,IAAI,IAAS,KAAK,IAAI,GAAK,CAAG,CAAC,GAC/C,IAAW,KAAK,IAAI,KAAK,IAAI,GAAK,CAAG,IAAI,CAAM;CAErD,QAAQ,MACoD,EAApD,CAAC,OAAO,SAAS,CAAK,KAAK,MAAU,IAAqB,gBAC1D,IAAQ,IACJ,MAAa,IAAgB,gBAGpB,aAAa,IAAM,EAFtB,KAAK,IAAI,IAAI,IAAS,KAAS,CAEF,GAAG,CAAQ,MAElD,MAAa,IAAgB,gBAEpB,aAAa,IAAM,EADtB,KAAK,IAAI,IAAI,IAAQ,KAAU,CACF,GAAG,CAAQ,GAT8B;AAWxF;AAWA,SAAgB,EAAW,GAAqD;CAC5E,IAAM,IAAQ,MAAS,eAAA,IAAA;CACvB,OAAO,MAAM,KAAK,EAAE,QAAQ,EAAM,IAAI,GAAG,MAAM,EAAM,GAAG,EAAK,GAAG,IAAI,GAAG,CAAC;AAC5E"}
package/dist/charts.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./charts/palette.cjs"),t=require("./charts/use-chart-colors.cjs"),n=require("./charts/AreaChart.cjs"),r=require("./charts/BarChart.cjs"),i=require("./charts/LineChart.cjs"),a=require("./charts/PieChart.cjs"),o=require("./charts/RadarChart.cjs");exports.AreaChart=n.AreaChart,exports.BarChart=r.BarChart,exports.CHART_COLOR_TOKEN_COUNT=e.CHART_COLOR_TOKEN_COUNT,exports.DEFAULT_CHART_COLORS=e.DEFAULT_CHART_COLORS,exports.LineChart=i.LineChart,exports.PieChart=a.PieChart,exports.RadarChart=o.RadarChart,exports.resolveChartChrome=e.resolveChartChrome,exports.resolveChartColors=e.resolveChartColors,exports.useChartColors=t.useChartColors;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./charts/scales.cjs"),t=require("./charts/palette.cjs"),n=require("./charts/use-chart-colors.cjs"),r=require("./charts/AreaChart.cjs"),i=require("./charts/BarChart.cjs"),a=require("./charts/LineChart.cjs"),o=require("./charts/PieChart.cjs"),s=require("./charts/RadarChart.cjs");exports.AreaChart=r.AreaChart,exports.BarChart=i.BarChart,exports.CHART_COLOR_TOKEN_COUNT=t.CHART_COLOR_TOKEN_COUNT,exports.DEFAULT_CHART_COLORS=t.DEFAULT_CHART_COLORS,exports.DIVERGING_STEP_COUNT=e.DIVERGING_STEP_COUNT,exports.LineChart=a.LineChart,exports.ORDINAL_START_STEP=e.ORDINAL_START_STEP,exports.PieChart=o.PieChart,exports.RadarChart=s.RadarChart,exports.SEQUENTIAL_STEP_COUNT=e.SEQUENTIAL_STEP_COUNT,exports.divergingScale=e.divergingScale,exports.resolveChartChrome=t.resolveChartChrome,exports.resolveChartColors=t.resolveChartColors,exports.scaleSteps=e.scaleSteps,exports.sequentialScale=e.sequentialScale,exports.useChartColors=n.useChartColors;
package/dist/charts.d.ts CHANGED
@@ -60,6 +60,9 @@ export declare interface CartesianChartProps {
60
60
  /** How many `--tempest-chart-N` tokens the SDK ships. */
61
61
  export declare const CHART_COLOR_TOKEN_COUNT = 8;
62
62
 
63
+ /** A `var(--tempest-chart-…)` reference, so the value follows the active theme. */
64
+ export declare type ChartColorToken = string;
65
+
63
66
  /**
64
67
  * Tabular data consumed by every chart: an array of rows, where each row maps a
65
68
  * column key to a string (label) or number (value).
@@ -77,6 +80,40 @@ export declare type ChartData = Array<Record<string, string | number>>;
77
80
  */
78
81
  export declare const DEFAULT_CHART_COLORS: string[];
79
82
 
83
+ /** How many steps the diverging token scale has, midpoint included. */
84
+ export declare const DIVERGING_STEP_COUNT = 9;
85
+
86
+ /**
87
+ * Build a polarity scale over the diverging tokens.
88
+ *
89
+ * Each arm is scaled against its **own** distance from the centre, so an asymmetric
90
+ * domain (say −5…+80) still uses the full cool arm for its small negatives. Scaling
91
+ * both arms by the wider one — the easy mistake — would collapse every negative
92
+ * into the step next to the midpoint and hide the sign entirely.
93
+ *
94
+ * @example
95
+ * const color = divergingScale({ min: -12, max: 40 }); // centre 0
96
+ * const budget = divergingScale({ min: 80, max: 130, center: 100 });
97
+ *
98
+ * @param options - The data domain and the neutral centre.
99
+ * @returns A function from value to a CSS colour reference.
100
+ */
101
+ export declare function divergingScale(options: DivergingScaleOptions): (value: number) => ChartColorToken;
102
+
103
+ export declare interface DivergingScaleOptions {
104
+ /** Lowest value in the data. */
105
+ min: number;
106
+ /** Highest value in the data. */
107
+ max: number;
108
+ /**
109
+ * The value that means "no deviation". Default `0`.
110
+ *
111
+ * It is a parameter because the interesting midpoint is often not zero — a
112
+ * budget variance diverges around the target, not around nothing.
113
+ */
114
+ center?: number;
115
+ }
116
+
80
117
  /**
81
118
  * LineChart — themed wrapper over recharts `LineChart`.
82
119
  *
@@ -88,6 +125,16 @@ export declare const DEFAULT_CHART_COLORS: string[];
88
125
  */
89
126
  export declare function LineChart({ data, index, categories, colors, height, width, showLegend, showGrid, showTooltip, valueFormatter, className, }: CartesianChartProps): JSX.Element;
90
127
 
128
+ /**
129
+ * First sequential step that clears 2:1 against the chart surface.
130
+ *
131
+ * A sequential scale may let its near-zero end recede into the surface — on a
132
+ * heatmap that is exactly what "almost nothing" should look like. An **ordinal**
133
+ * scale may not: every step is a discrete mark someone has to see. Starting an
134
+ * ordinal ramp here is the difference.
135
+ */
136
+ export declare const ORDINAL_START_STEP = 3;
137
+
91
138
  /**
92
139
  * PieChart — themed wrapper over recharts `PieChart`.
93
140
  *
@@ -165,6 +212,50 @@ export declare function resolveChartChrome(part: "grid" | "axis", element?: Elem
165
212
  */
166
213
  export declare function resolveChartColors(element?: Element | null): string[];
167
214
 
215
+ /**
216
+ * Every step of a token scale, in order — for rendering a legend.
217
+ *
218
+ * A continuous scale needs a legend showing the ramp with its end labels; without
219
+ * one the reader has no way to turn a colour back into a number.
220
+ *
221
+ * @param kind - Which scale.
222
+ * @returns The token references, lightest/coolest first.
223
+ */
224
+ export declare function scaleSteps(kind: "sequential" | "diverging"): ChartColorToken[];
225
+
226
+ /** How many steps the sequential token scale has. */
227
+ export declare const SEQUENTIAL_STEP_COUNT = 7;
228
+
229
+ /**
230
+ * Build a magnitude scale over the sequential tokens.
231
+ *
232
+ * Returns `var(--tempest-chart-sequential-N)` rather than a hex string, so a
233
+ * heatmap painted once follows the theme — including dark mode, whose steps are
234
+ * chosen for the dark surface rather than flipped.
235
+ *
236
+ * @example
237
+ * const color = sequentialScale({ min: 0, max: 250 });
238
+ * <rect fill={color(value)} />
239
+ *
240
+ * @param options - The data domain, and whether every step must stay visible.
241
+ * @returns A function from value to a CSS colour reference.
242
+ */
243
+ export declare function sequentialScale(options: SequentialScaleOptions): (value: number) => ChartColorToken;
244
+
245
+ export declare interface SequentialScaleOptions {
246
+ /** Lowest value in the data. */
247
+ min: number;
248
+ /** Highest value in the data. */
249
+ max: number;
250
+ /**
251
+ * Keep every step visible against the surface, for discrete ordered marks.
252
+ *
253
+ * Off by default: a heatmap *wants* its near-zero cells to recede. Turn it on
254
+ * for tiers, funnel stages or anything where each step is its own mark.
255
+ */
256
+ ordinal?: boolean;
257
+ }
258
+
168
259
  /**
169
260
  * Theme-aware series colors for a chart.
170
261
  *
package/dist/charts.js CHANGED
@@ -1,8 +1,9 @@
1
- import { CHART_COLOR_TOKEN_COUNT as e, DEFAULT_CHART_COLORS as t, resolveChartChrome as n, resolveChartColors as r } from "./charts/palette.js";
2
- import { useChartColors as i } from "./charts/use-chart-colors.js";
3
- import { AreaChart as a } from "./charts/AreaChart.js";
4
- import { BarChart as o } from "./charts/BarChart.js";
5
- import { LineChart as s } from "./charts/LineChart.js";
6
- import { PieChart as c } from "./charts/PieChart.js";
7
- import { RadarChart as l } from "./charts/RadarChart.js";
8
- export { a as AreaChart, o as BarChart, e as CHART_COLOR_TOKEN_COUNT, t as DEFAULT_CHART_COLORS, s as LineChart, c as PieChart, l as RadarChart, n as resolveChartChrome, r as resolveChartColors, i as useChartColors };
1
+ import { DIVERGING_STEP_COUNT as e, ORDINAL_START_STEP as t, SEQUENTIAL_STEP_COUNT as n, divergingScale as r, scaleSteps as i, sequentialScale as a } from "./charts/scales.js";
2
+ import { CHART_COLOR_TOKEN_COUNT as o, DEFAULT_CHART_COLORS as s, resolveChartChrome as c, resolveChartColors as l } from "./charts/palette.js";
3
+ import { useChartColors as u } from "./charts/use-chart-colors.js";
4
+ import { AreaChart as d } from "./charts/AreaChart.js";
5
+ import { BarChart as f } from "./charts/BarChart.js";
6
+ import { LineChart as p } from "./charts/LineChart.js";
7
+ import { PieChart as m } from "./charts/PieChart.js";
8
+ import { RadarChart as h } from "./charts/RadarChart.js";
9
+ export { d as AreaChart, f as BarChart, o as CHART_COLOR_TOKEN_COUNT, s as DEFAULT_CHART_COLORS, e as DIVERGING_STEP_COUNT, p as LineChart, t as ORDINAL_START_STEP, m as PieChart, h as RadarChart, n as SEQUENTIAL_STEP_COUNT, r as divergingScale, c as resolveChartChrome, l as resolveChartColors, i as scaleSteps, a as sequentialScale, u as useChartColors };
@@ -1 +1 @@
1
- {"version":3,"file":"BottomNavigation.module.cjs","names":[],"sources":["../../../src/components/BottomNavigation/BottomNavigation.module.css"],"sourcesContent":[".bar {\n display: flex;\n width: 100%;\n background: var(--tempest-surface);\n border-top: 1px solid var(--tempest-border);\n padding-bottom: env(safe-area-inset-bottom, 0);\n font-family: var(--tempest-font-sans);\n}\n\n.item {\n flex: 1 1 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 2px;\n padding: 8px 4px;\n background: none;\n border: 0;\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition: color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n min-height: 56px;\n}\n\n.item:hover {\n color: var(--tempest-text);\n}\n\n.item.active {\n color: var(--tempest-primary);\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: 20px;\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);\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}\n"],"mappings":""}
1
+ {"version":3,"file":"BottomNavigation.module.cjs","names":[],"sources":["../../../src/components/BottomNavigation/BottomNavigation.module.css"],"sourcesContent":[".bar {\n display: flex;\n width: 100%;\n background: var(--tempest-surface);\n border-top: 1px solid var(--tempest-border);\n padding-bottom: env(safe-area-inset-bottom, 0);\n font-family: var(--tempest-font-sans);\n}\n\n.item {\n flex: 1 1 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 2px;\n padding: 8px 4px;\n background: none;\n border: 0;\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition: color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n min-height: 56px;\n}\n\n.item:hover {\n color: var(--tempest-text);\n}\n\n.item.active {\n color: var(--tempest-primary);\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: 20px;\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}\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"BottomNavigation.module.js","names":[],"sources":["../../../src/components/BottomNavigation/BottomNavigation.module.css"],"sourcesContent":[".bar {\n display: flex;\n width: 100%;\n background: var(--tempest-surface);\n border-top: 1px solid var(--tempest-border);\n padding-bottom: env(safe-area-inset-bottom, 0);\n font-family: var(--tempest-font-sans);\n}\n\n.item {\n flex: 1 1 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 2px;\n padding: 8px 4px;\n background: none;\n border: 0;\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition: color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n min-height: 56px;\n}\n\n.item:hover {\n color: var(--tempest-text);\n}\n\n.item.active {\n color: var(--tempest-primary);\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: 20px;\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);\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}\n"],"mappings":""}
1
+ {"version":3,"file":"BottomNavigation.module.js","names":[],"sources":["../../../src/components/BottomNavigation/BottomNavigation.module.css"],"sourcesContent":[".bar {\n display: flex;\n width: 100%;\n background: var(--tempest-surface);\n border-top: 1px solid var(--tempest-border);\n padding-bottom: env(safe-area-inset-bottom, 0);\n font-family: var(--tempest-font-sans);\n}\n\n.item {\n flex: 1 1 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 2px;\n padding: 8px 4px;\n background: none;\n border: 0;\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition: color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n min-height: 56px;\n}\n\n.item:hover {\n color: var(--tempest-text);\n}\n\n.item.active {\n color: var(--tempest-primary);\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: 20px;\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}\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"Carousel.module.cjs","names":[],"sources":["../../../src/components/Carousel/Carousel.module.css"],"sourcesContent":[".root {\n position: relative;\n width: 100%;\n font-family: var(--tempest-font-sans);\n}\n\n.root:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.viewport {\n overflow: hidden;\n border-radius: var(--tempest-radius-lg);\n}\n\n.track {\n display: flex;\n transition: transform var(--tempest-duration-normal) var(--tempest-ease-out);\n}\n\n.slide {\n flex: 0 0 100%;\n min-width: 0;\n}\n\n.arrow {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full);\n box-shadow: var(--tempest-shadow-md);\n color: var(--tempest-text);\n font-size: var(--tempest-text-xl);\n line-height: 1;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .arrow:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.arrow:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.arrow:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.prev {\n left: var(--tempest-space-2);\n}\n\n.next {\n right: var(--tempest-space-2);\n}\n\n.dots {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-2);\n margin-top: var(--tempest-space-3);\n}\n\n.dot {\n width: 0.5rem;\n height: 0.5rem;\n padding: 0;\n background-color: var(--tempest-border);\n border: none;\n border-radius: var(--tempest-radius-full);\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n.dotActive {\n background-color: var(--tempest-primary);\n}\n\n.dot:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .track {\n transition: none;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"Carousel.module.cjs","names":[],"sources":["../../../src/components/Carousel/Carousel.module.css"],"sourcesContent":[".root {\n position: relative;\n width: 100%;\n font-family: var(--tempest-font-sans);\n}\n\n.root:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.viewport {\n overflow: hidden;\n border-radius: var(--tempest-radius-lg);\n}\n\n.track {\n display: flex;\n transition: transform var(--tempest-duration-base) var(--tempest-ease-out);\n}\n\n.slide {\n flex: 0 0 100%;\n min-width: 0;\n}\n\n.arrow {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full);\n box-shadow: var(--tempest-shadow-md);\n color: var(--tempest-text);\n font-size: var(--tempest-text-xl);\n line-height: 1;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .arrow:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.arrow:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.arrow:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.prev {\n left: var(--tempest-space-2);\n}\n\n.next {\n right: var(--tempest-space-2);\n}\n\n.dots {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-2);\n margin-top: var(--tempest-space-3);\n}\n\n.dot {\n width: 0.5rem;\n height: 0.5rem;\n padding: 0;\n background-color: var(--tempest-border);\n border: none;\n border-radius: var(--tempest-radius-full);\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n.dotActive {\n background-color: var(--tempest-primary);\n}\n\n.dot:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .track {\n transition: none;\n }\n}\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"Carousel.module.js","names":[],"sources":["../../../src/components/Carousel/Carousel.module.css"],"sourcesContent":[".root {\n position: relative;\n width: 100%;\n font-family: var(--tempest-font-sans);\n}\n\n.root:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.viewport {\n overflow: hidden;\n border-radius: var(--tempest-radius-lg);\n}\n\n.track {\n display: flex;\n transition: transform var(--tempest-duration-normal) var(--tempest-ease-out);\n}\n\n.slide {\n flex: 0 0 100%;\n min-width: 0;\n}\n\n.arrow {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full);\n box-shadow: var(--tempest-shadow-md);\n color: var(--tempest-text);\n font-size: var(--tempest-text-xl);\n line-height: 1;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .arrow:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.arrow:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.arrow:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.prev {\n left: var(--tempest-space-2);\n}\n\n.next {\n right: var(--tempest-space-2);\n}\n\n.dots {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-2);\n margin-top: var(--tempest-space-3);\n}\n\n.dot {\n width: 0.5rem;\n height: 0.5rem;\n padding: 0;\n background-color: var(--tempest-border);\n border: none;\n border-radius: var(--tempest-radius-full);\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n.dotActive {\n background-color: var(--tempest-primary);\n}\n\n.dot:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .track {\n transition: none;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"Carousel.module.js","names":[],"sources":["../../../src/components/Carousel/Carousel.module.css"],"sourcesContent":[".root {\n position: relative;\n width: 100%;\n font-family: var(--tempest-font-sans);\n}\n\n.root:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.viewport {\n overflow: hidden;\n border-radius: var(--tempest-radius-lg);\n}\n\n.track {\n display: flex;\n transition: transform var(--tempest-duration-base) var(--tempest-ease-out);\n}\n\n.slide {\n flex: 0 0 100%;\n min-width: 0;\n}\n\n.arrow {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full);\n box-shadow: var(--tempest-shadow-md);\n color: var(--tempest-text);\n font-size: var(--tempest-text-xl);\n line-height: 1;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .arrow:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.arrow:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.arrow:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.prev {\n left: var(--tempest-space-2);\n}\n\n.next {\n right: var(--tempest-space-2);\n}\n\n.dots {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-2);\n margin-top: var(--tempest-space-3);\n}\n\n.dot {\n width: 0.5rem;\n height: 0.5rem;\n padding: 0;\n background-color: var(--tempest-border);\n border: none;\n border-radius: var(--tempest-radius-full);\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n.dotActive {\n background-color: var(--tempest-primary);\n}\n\n.dot:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .track {\n transition: none;\n }\n}\n"],"mappings":""}
@@ -0,0 +1,3 @@
1
+ const e=require("../../utils/cn.cjs"),t=require("../CopyButton/CopyButton.cjs"),n=require("./tokenize.cjs"),r=require("./CodeBlock.module.cjs");let i=require("react"),a=require("react/jsx-runtime");function o({code:o,language:s,filename:c,showLineNumbers:l=!1,highlightLines:u,copyable:d=!0,maxHeight:f,wrap:p=!1,label:m,className:h,...g}){let _=(0,i.useMemo)(()=>o.replace(/^\n+/,``).replace(/\s+$/,``),[o]),v=(0,i.useMemo)(()=>n.tokenizeLines(_,s),[_,s]),y=n.resolveLanguage(s),b=(0,i.useMemo)(()=>new Set(u??[]),[u]),x=m??(c&&typeof c==`string`?`Código: ${c}`:`Bloco de código${y===`plain`?``:` em ${y}`}`),S=!!c||d;return(0,a.jsxs)(`div`,{className:e.cn(r.default.wrapper,h),...g,children:[S&&(0,a.jsxs)(`div`,{className:r.default.header,children:[(0,a.jsx)(`span`,{className:r.default.filename,children:c??(y===`plain`?``:y)}),d&&(0,a.jsx)(t.CopyButton,{value:_,className:r.default.copy})]}),(0,a.jsx)(`pre`,{className:e.cn(r.default.pre,p&&r.default.wrap),style:{maxHeight:f},tabIndex:0,role:`group`,"aria-label":x,children:(0,a.jsx)(`code`,{className:r.default.code,"data-language":y,children:v.map((t,n)=>(0,a.jsxs)(i.Fragment,{children:[(0,a.jsxs)(`span`,{className:e.cn(r.default.line,b.has(n+1)&&r.default.marked),children:[l&&(0,a.jsx)(`span`,{className:r.default.lineNumber,"aria-hidden":`true`,children:n+1}),(0,a.jsx)(`span`,{className:r.default.lineContent,children:t.map((e,t)=>(0,a.jsx)(`span`,{className:r.default[e.kind],"data-token":e.kind,children:e.value},t))})]}),n<v.length-1?`
2
+ `:null]},n))})})]})}exports.CodeBlock=o;
3
+ //# sourceMappingURL=CodeBlock.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CodeBlock.cjs","names":[],"sources":["../../../src/components/CodeBlock/CodeBlock.tsx"],"sourcesContent":["import { Fragment, useMemo } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nimport { CopyButton } from \"../CopyButton\";\nimport { resolveLanguage, tokenizeLines } from \"./tokenize\";\nimport styles from \"./CodeBlock.module.css\";\n\nexport interface CodeBlockProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /** The source to display. Leading and trailing blank lines are trimmed. */\n code: string;\n /** Grammar name or alias (`ts`, `bash`, `json`…). Unknown values render plain. */\n language?: string;\n /** Shown in the header, usually a file path. */\n filename?: ReactNode;\n /** Number the lines. Default `false`. */\n showLineNumbers?: boolean;\n /** 1-based line numbers to mark as the point of the snippet. */\n highlightLines?: readonly number[];\n /** Offer a copy button. Default `true`. */\n copyable?: boolean;\n /** Cap the height in px or any CSS length; the body scrolls past it. */\n maxHeight?: number | string;\n /** Wrap long lines instead of scrolling sideways. Default `false`. */\n wrap?: boolean;\n /**\n * Accessible name for the code region. Defaults to naming the language, and\n * to the filename when there is one.\n */\n label?: string;\n}\n\n/**\n * A read-only code sample: syntax colours, optional line numbers, copy button.\n *\n * The `<pre>` is always focusable. A code block is scrollable and holds nothing\n * focusable inside, so without a tab stop a keyboard user can see there is more\n * code past the edge and has no way to reach it. It is the one scroll container\n * where the stop is unconditional rather than measured — a code sample is\n * expected to be reachable, read and selected on its own.\n *\n * Colours come from an approximate scanner, not a parser — see {@link tokenize}.\n *\n * @example\n * <CodeBlock code={snippet} language=\"ts\" filename=\"src/main.ts\" showLineNumbers />\n * <CodeBlock code={log} language=\"bash\" maxHeight={280} />\n */\nexport function CodeBlock({\n code,\n language,\n filename,\n showLineNumbers = false,\n highlightLines,\n copyable = true,\n maxHeight,\n wrap = false,\n label,\n className,\n ...rest\n}: CodeBlockProps) {\n const trimmed = useMemo(() => code.replace(/^\\n+/, \"\").replace(/\\s+$/, \"\"), [code]);\n const lines = useMemo(() => tokenizeLines(trimmed, language), [trimmed, language]);\n const resolved = resolveLanguage(language);\n const marked = useMemo(() => new Set(highlightLines ?? []), [highlightLines]);\n\n const name =\n label ??\n (filename && typeof filename === \"string\"\n ? `Código: ${filename}`\n : `Bloco de código${resolved === \"plain\" ? \"\" : ` em ${resolved}`}`);\n\n const hasHeader = Boolean(filename) || copyable;\n\n return (\n <div className={cn(styles.wrapper, className)} {...rest}>\n {hasHeader && (\n <div className={styles.header}>\n <span className={styles.filename}>\n {filename ?? (resolved === \"plain\" ? \"\" : resolved)}\n </span>\n {copyable && <CopyButton value={trimmed} className={styles.copy} />}\n </div>\n )}\n\n <pre\n className={cn(styles.pre, wrap && styles.wrap)}\n style={{ maxHeight }}\n tabIndex={0}\n role=\"group\"\n aria-label={name}\n >\n <code className={styles.code} data-language={resolved}>\n {lines.map((tokens, index) => (\n <Fragment key={index}>\n <span\n className={cn(styles.line, marked.has(index + 1) && styles.marked)}\n >\n {showLineNumbers && (\n /*\n * The number is decoration: it must not land in the\n * clipboard when the reader selects the snippet, and a\n * screen reader announcing \"one const two import\" adds\n * nothing. `user-select: none` in CSS plus this.\n */\n <span className={styles.lineNumber} aria-hidden=\"true\">\n {index + 1}\n </span>\n )}\n <span className={styles.lineContent}>\n {tokens.map((token, tokenIndex) => (\n <span\n key={tokenIndex}\n className={styles[token.kind]}\n data-token={token.kind}\n >\n {token.value}\n </span>\n ))}\n </span>\n </span>\n {/*\n * A real newline character, and outside the line box.\n * It has to be real so that selecting the snippet by\n * hand and copying yields the source instead of one\n * run-together line. It has to be outside because the\n * line is an inline-block: a newline within one is\n * consumed inside that box, and every line ends up\n * side by side on a single row. The last one is\n * dropped so the block does not end in blank space.\n */}\n {index < lines.length - 1 ? \"\\n\" : null}\n </Fragment>\n ))}\n </code>\n </pre>\n </div>\n );\n}\n"],"mappings":"sMAgDA,SAAgB,EAAU,CACtB,OACA,WACA,WACA,kBAAkB,GAClB,iBACA,WAAW,GACX,YACA,OAAO,GACP,QACA,YACA,GAAG,GACY,CACf,IAAM,GAAA,EAAA,EAAA,QAAA,KAAwB,EAAK,QAAQ,OAAQ,EAAE,CAAC,CAAC,QAAQ,OAAQ,EAAE,EAAG,CAAC,CAAI,CAAC,EAC5E,GAAA,EAAA,EAAA,QAAA,KAAsB,EAAA,cAAc,EAAS,CAAQ,EAAG,CAAC,EAAS,CAAQ,CAAC,EAC3E,EAAW,EAAA,gBAAgB,CAAQ,EACnC,GAAA,EAAA,EAAA,QAAA,KAAuB,IAAI,IAAI,GAAkB,CAAC,CAAC,EAAG,CAAC,CAAc,CAAC,EAEtE,EACF,IACC,GAAY,OAAO,GAAa,SAC3B,WAAW,IACX,kBAAkB,IAAa,QAAU,GAAK,OAAO,OAEzD,EAAY,EAAQ,GAAa,EAEvC,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,CAAS,EAAG,GAAI,WAAnD,CACK,IACG,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,gBAAvB,EACI,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,kBACnB,IAAa,IAAa,QAAU,GAAK,EACxC,CAAA,EACL,IAAY,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CAAY,MAAO,EAAS,UAAW,EAAA,QAAO,IAAO,CAAA,CACjE,KAGT,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,GAAG,EAAA,QAAO,IAAK,GAAQ,EAAA,QAAO,IAAI,EAC7C,MAAO,CAAE,WAAU,EACnB,SAAU,EACV,KAAK,QACL,aAAY,YAEZ,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,KAAM,gBAAe,WACxC,EAAM,KAAK,EAAQ,KAChB,EAAA,EAAA,KAAA,CAAC,EAAA,SAAD,CAAA,SAAA,EACI,EAAA,EAAA,KAAA,CAAC,OAAD,CACI,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,EAAO,IAAI,EAAQ,CAAC,GAAK,EAAA,QAAO,MAAM,WADrE,CAGK,IAOG,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,WAAY,cAAY,gBAC3C,EAAQ,CACP,CAAA,GAEV,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,qBACnB,EAAO,KAAK,EAAO,KAChB,EAAA,EAAA,IAAA,CAAC,OAAD,CAEI,UAAW,EAAA,QAAO,EAAM,MACxB,aAAY,EAAM,cAEjB,EAAM,KACL,EALG,CAKH,CACT,CACC,CAAA,CACJ,IAWL,EAAQ,EAAM,OAAS,EAAI;EAAO,IAC7B,CAAA,EAtCK,CAsCL,CACb,CACC,CAAA,CACL,CAAA,CACJ,GAEb"}
@@ -0,0 +1,53 @@
1
+ import { cn as e } from "../../utils/cn.js";
2
+ import { CopyButton as t } from "../CopyButton/CopyButton.js";
3
+ import { resolveLanguage as n, tokenizeLines as r } from "./tokenize.js";
4
+ import i from "./CodeBlock.module.js";
5
+ import { Fragment as a, useMemo as o } from "react";
6
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
7
+ //#region src/components/CodeBlock/CodeBlock.tsx
8
+ function l({ code: l, language: u, filename: d, showLineNumbers: f = !1, highlightLines: p, copyable: m = !0, maxHeight: h, wrap: g = !1, label: _, className: v, ...y }) {
9
+ let b = o(() => l.replace(/^\n+/, "").replace(/\s+$/, ""), [l]), x = o(() => r(b, u), [b, u]), S = n(u), C = o(() => new Set(p ?? []), [p]), w = _ ?? (d && typeof d == "string" ? `Código: ${d}` : `Bloco de código${S === "plain" ? "" : ` em ${S}`}`), T = !!d || m;
10
+ return /* @__PURE__ */ c("div", {
11
+ className: e(i.wrapper, v),
12
+ ...y,
13
+ children: [T && /* @__PURE__ */ c("div", {
14
+ className: i.header,
15
+ children: [/* @__PURE__ */ s("span", {
16
+ className: i.filename,
17
+ children: d ?? (S === "plain" ? "" : S)
18
+ }), m && /* @__PURE__ */ s(t, {
19
+ value: b,
20
+ className: i.copy
21
+ })]
22
+ }), /* @__PURE__ */ s("pre", {
23
+ className: e(i.pre, g && i.wrap),
24
+ style: { maxHeight: h },
25
+ tabIndex: 0,
26
+ role: "group",
27
+ "aria-label": w,
28
+ children: /* @__PURE__ */ s("code", {
29
+ className: i.code,
30
+ "data-language": S,
31
+ children: x.map((t, n) => /* @__PURE__ */ c(a, { children: [/* @__PURE__ */ c("span", {
32
+ className: e(i.line, C.has(n + 1) && i.marked),
33
+ children: [f && /* @__PURE__ */ s("span", {
34
+ className: i.lineNumber,
35
+ "aria-hidden": "true",
36
+ children: n + 1
37
+ }), /* @__PURE__ */ s("span", {
38
+ className: i.lineContent,
39
+ children: t.map((e, t) => /* @__PURE__ */ s("span", {
40
+ className: i[e.kind],
41
+ "data-token": e.kind,
42
+ children: e.value
43
+ }, t))
44
+ })]
45
+ }), n < x.length - 1 ? "\n" : null] }, n))
46
+ })
47
+ })]
48
+ });
49
+ }
50
+ //#endregion
51
+ export { l as CodeBlock };
52
+
53
+ //# sourceMappingURL=CodeBlock.js.map