@rojaostudio/ds 2.0.0-next.21 → 2.0.0-next.23

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.
@@ -43,7 +43,8 @@ interface ChartProps extends Omit<HTMLAttributes<HTMLElement>, 'children' | 'onS
43
43
  showAllDates?: boolean;
44
44
  /**
45
45
  * bar and column: where each item leads, in the order of `labels`. In bar each bar becomes a link named
46
- * "<label>: <value>"; in column a click or Enter on the plot follows the item's link.
46
+ * "<label>: <value>"; in column a click or Enter on the plot follows the item's link. Only http(s) URLs, relative
47
+ * paths and #fragments are followed; any other scheme (javascript:, data:…) is ignored, with a warning in dev.
47
48
  */
48
49
  hrefs?: Array<string | undefined>;
49
50
  /** bar and column: called with the item's index when it is chosen (click, Enter). In bar each bar becomes a button. */
@@ -53,11 +54,6 @@ interface ChartProps extends Omit<HTMLAttributes<HTMLElement>, 'children' | 'onS
53
54
  /** Height of the plot area, in px (Figma: 200). */
54
55
  height?: number;
55
56
  }
56
- /**
57
- * Chart — Figma [RDS] Content/Chart. The chart area, to go inside a Card. Every chart carries a table with the same
58
- * data (visually hidden unless `showTable`). In line and column the plot is a slider for screen readers and the
59
- * keyboard: the arrows move from point to point, and each point is read with its label and values. Styles: chart.css.
60
- */
61
- declare function Chart({ label, type, labels, series, formatLeft, formatRight, showRightAxis, showLegend, showTooltip, dates, dateEvery, showAllDates, hrefs, onSelect, showTable, height, className, ...rest }: ChartProps): react.JSX.Element;
57
+ declare function Chart({ label, type, labels, series, formatLeft, formatRight, showRightAxis, showLegend, showTooltip, dates, dateEvery, showAllDates, hrefs: hrefsProp, onSelect, showTable, height, className, ...rest }: ChartProps): react.JSX.Element;
62
58
 
63
59
  export { Chart, type ChartProps, type ChartSeries, type ChartType };
@@ -5,7 +5,7 @@ import {
5
5
  __spreadValues
6
6
  } from "../chunk-ORMEWXMH.js";
7
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
- import { useId, useState } from "react";
8
+ import { useId, useMemo, useState } from "react";
9
9
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table.js";
10
10
  const compact = new Intl.NumberFormat("pt-BR", { notation: "compact", maximumFractionDigits: 1 });
11
11
  const defaultFormat = (value) => compact.format(value);
@@ -25,6 +25,20 @@ function scaleOf(values) {
25
25
  const max = min + step * 4;
26
26
  return { min, max, ticks: [4, 3, 2, 1, 0].map((i) => min + step * i) };
27
27
  }
28
+ const SAFE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
29
+ function safeHref(href) {
30
+ if (!href) return void 0;
31
+ if (href.startsWith("#")) return href;
32
+ try {
33
+ const base = typeof window !== "undefined" && SAFE_PROTOCOLS.has(window.location.protocol) ? window.location.href : "https://example.invalid/";
34
+ if (SAFE_PROTOCOLS.has(new URL(href, base).protocol)) return href;
35
+ } catch (e) {
36
+ }
37
+ if (process.env.NODE_ENV !== "production") {
38
+ console.warn(`Chart: href "${href}" ignored. Only http(s) URLs, relative paths and #fragments are followed.`);
39
+ }
40
+ return void 0;
41
+ }
28
42
  function Chart(_a) {
29
43
  var _b = _a, {
30
44
  label,
@@ -39,7 +53,7 @@ function Chart(_a) {
39
53
  dates,
40
54
  dateEvery,
41
55
  showAllDates,
42
- hrefs,
56
+ hrefs: hrefsProp,
43
57
  onSelect,
44
58
  showTable = false,
45
59
  height = 200,
@@ -65,6 +79,7 @@ function Chart(_a) {
65
79
  ]);
66
80
  var _a2;
67
81
  const [active, setActive] = useState(null);
82
+ const hrefs = useMemo(() => hrefsProp == null ? void 0 : hrefsProp.map(safeHref), [hrefsProp]);
68
83
  const tooltipId = useId();
69
84
  const count = labels.length;
70
85
  const shown = type === "bar" ? series.slice(0, 1) : series.slice(0, 5);
@@ -92,7 +107,10 @@ function Chart(_a) {
92
107
  const linked = type !== "line" && Boolean(onSelect || (hrefs == null ? void 0 : hrefs.some(Boolean)));
93
108
  const choose = (index) => {
94
109
  if (onSelect) onSelect(index);
95
- else if (hrefs == null ? void 0 : hrefs[index]) window.location.assign(hrefs[index]);
110
+ else {
111
+ const href = safeHref(hrefs == null ? void 0 : hrefs[index]);
112
+ if (href) window.location.assign(href);
113
+ }
96
114
  };
97
115
  const onPointerMove = (event) => {
98
116
  const box = event.currentTarget.getBoundingClientRect();
@@ -35,10 +35,10 @@ interface SavingBarProps extends HTMLAttributes<HTMLDivElement> {
35
35
  }
36
36
  /**
37
37
  * SavingBar — Figma [RDS] Actions/SavingBar. Shows at the foot of the screen while a change is not
38
- * saved, and leaves once saved. It is the brand plate on purpose (.ds-plate on the bar, in every brand and in
39
- * light or dark alike): a state that waits for a decision, not a notice. It does not place itself: put it
40
- * sticky or fixed at the bottom. Its Buttons are Salvar in tone action and Descartar in tone neutral ghost, read
41
- * inside the plate. Styles: saving-bar.css.
38
+ * saved, and leaves once saved. It is the brand's primary colour (savingbar/background, colors/primary/default),
39
+ * in light and dark and in every brand: a state that waits for a decision, not a notice. It does not place itself:
40
+ * put it sticky or fixed at the bottom. Its Buttons are Salvar in tone action and Descartar in tone neutral ghost,
41
+ * with the bar's colours (savingbar/button/*). Styles: saving-bar.css.
42
42
  */
43
43
  declare function SavingBar({ status, message, savingMessage, errorMessage, onSave, onDiscard, saveLabel, retryLabel, discardLabel, onDetails, detailsLabel, detailsExpanded, detailsControls, className, ...rest }: SavingBarProps): react.JSX.Element;
44
44
 
@@ -50,7 +50,7 @@ function SavingBar(_a) {
50
50
  role: "region",
51
51
  "aria-label": "Salvar altera\xE7\xF5es"
52
52
  }, rest), {
53
- className: ["rds-savingbar", "ds-plate", className].filter(Boolean).join(" "),
53
+ className: ["rds-savingbar", className].filter(Boolean).join(" "),
54
54
  children: /* @__PURE__ */ jsxs("div", { className: "rds-savingbar__row", children: [
55
55
  /* @__PURE__ */ jsxs("p", { className: ["rds-savingbar__message", details && "rds-savingbar__message--details"].filter(Boolean).join(" "), role: "status", children: [
56
56
  saving && /* @__PURE__ */ jsx("span", { className: "rds-savingbar__icon rds-savingbar__loader", "aria-hidden": "true", children: /* @__PURE__ */ jsx(Loader, {}) }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rojaostudio/ds",
3
- "version": "2.0.0-next.21",
3
+ "version": "2.0.0-next.23",
4
4
  "type": "module",
5
5
  "description": "Rojão Design System — componentes React, estilos e alvo React Native. O motor vive em @rojaostudio/ds-core.",
6
6
  "license": "MIT",
@@ -190,7 +190,7 @@
190
190
  "@radix-ui/react-toggle": "^1.1.18",
191
191
  "@radix-ui/react-toggle-group": "^1.1.19",
192
192
  "@radix-ui/react-tooltip": "^1.2.16",
193
- "@rojaostudio/ds-core": "1.1.0-next.8"
193
+ "@rojaostudio/ds-core": "1.1.0-next.9"
194
194
  },
195
195
  "scripts": {
196
196
  "build": "pnpm run build:tokens && pnpm run build:themes && pnpm run build:rds && pnpm run build:native && tsup",
@@ -8460,10 +8460,14 @@ input:focus-visible + .rds-radio__circle {
8460
8460
  /* @tokens savingbar */
8461
8461
  /*
8462
8462
  * SavingBar (Actions collection, savingbar/*): full width (at least 320), 12 24 padding, 16 between the
8463
- * message (label, medium, at most 2 lines with an ellipsis) and the actions (8 apart). The bar is the brand plate
8464
- * (.ds-plate on the element, set by the component): savingbar/background is surface/page and savingbar/text is text/body,
8465
- * read in the brand mode, so it comes out the same over a light or a dark page. The Buttons are tone action (Salvar)
8466
- * and tone neutral ghost (Descartar), read on the plate too.
8463
+ * message (label, medium, at most 2 lines with an ellipsis) and the actions (8 apart). The bar is the brand's
8464
+ * primary colour: savingbar/background is colors/primary/default and savingbar/text is text/on/primary (the text,
8465
+ * the error alert, the chevron and the loader), so it follows the theme, light or dark, and every brand.
8466
+ * The Buttons stay the Button: Salvar tone action fill and Descartar tone neutral ghost, with the bar's colours.
8467
+ * The bar redeclares the Button tokens those two read: the Salvar fill is savingbar/button/fill/background with
8468
+ * savingbar/button/fill/label, its hover the fill at 85% over the bar (active 70%); the Descartar label is
8469
+ * savingbar/button/ghost/label, its hover the label colour at 15% (active 25%); the focus ring is savingbar/focus/ring;
8470
+ * disabled (saving) is each Button at 40% over the bar, as the Figma layer opacity.
8467
8471
  * Two arrangements, by the screen width, as the Figma viewport mode (layout/compact below lg 1024): expanded from 1024,
8468
8472
  * the message grows (at least 160) beside the actions; compact below it, the message on top and Descartar and
8469
8473
  * Salvar under it, sharing the width.
@@ -8480,6 +8484,23 @@ input:focus-visible + .rds-radio__circle {
8480
8484
  background: var(--savingbar-background);
8481
8485
  color: var(--savingbar-text);
8482
8486
  font-family: var(--type-font-stack);
8487
+
8488
+ /* The Button tokens Salvar (action fill) and Descartar (neutral ghost) read, in the bar's colours. */
8489
+ --button-focus-ring: var(--savingbar-focus-ring);
8490
+ --button-action-fill-background-default: var(--savingbar-button-fill-background);
8491
+ --button-action-fill-background-hover: color-mix(in srgb, var(--savingbar-button-fill-background) 85%, var(--savingbar-background));
8492
+ --button-action-fill-background-active: color-mix(in srgb, var(--savingbar-button-fill-background) 70%, var(--savingbar-background));
8493
+ --button-action-fill-label: var(--savingbar-button-fill-label);
8494
+ --button-action-fill-icon: var(--savingbar-button-fill-label);
8495
+ --button-neutral-ghost-label: var(--savingbar-button-ghost-label);
8496
+ --button-neutral-ghost-icon: var(--savingbar-button-ghost-label);
8497
+ --button-neutral-ghost-background-hover: color-mix(in srgb, var(--savingbar-button-ghost-label) 15%, transparent);
8498
+ --button-neutral-ghost-background-active: color-mix(in srgb, var(--savingbar-button-ghost-label) 25%, transparent);
8499
+ --button-disabled-fill-background: color-mix(in srgb, var(--savingbar-button-fill-background) 40%, var(--savingbar-background));
8500
+ --button-disabled-fill-label: color-mix(in srgb, var(--savingbar-button-fill-label) 40%, var(--savingbar-background));
8501
+ --button-disabled-fill-icon: var(--button-disabled-fill-label);
8502
+ --button-disabled-ghost-label: color-mix(in srgb, var(--savingbar-button-ghost-label) 40%, var(--savingbar-background));
8503
+ --button-disabled-ghost-icon: var(--button-disabled-ghost-label);
8483
8504
  }
8484
8505
 
8485
8506
  .rds-savingbar__row {
@@ -73,7 +73,7 @@
73
73
  --button-inverse-focus-ring: var(--text-on-primary-strong);
74
74
  --button-inverse-disabled-fill-background: var(--colors-primary-light);
75
75
  --button-inverse-disabled-label: var(--text-disabled-invert);
76
- --savingbar-background: var(--surface-page);
76
+ --savingbar-background: var(--colors-primary-default);
77
77
  --toggle-label: var(--text-body);
78
78
  --toggle-border: var(--border-strong);
79
79
  --toggle-background-hover: var(--surface-muted);
@@ -83,7 +83,7 @@
83
83
  --toggle-label-disabled: var(--text-disabled);
84
84
  --toggle-border-pressed: var(--colors-primary-default);
85
85
  --toggle-border-disabled: var(--border-default);
86
- --savingbar-text: var(--text-body);
86
+ --savingbar-text: var(--text-on-primary);
87
87
  --fab-cta-background-default: var(--colors-primary-default);
88
88
  --fab-cta-background-hover: var(--colors-primary-active);
89
89
  --fab-cta-text: var(--text-on-primary);
@@ -120,7 +120,10 @@
120
120
  --button-size-lg-icon-only: var(--size-24);
121
121
  --button-size-lg-text-size: var(--type-body-size);
122
122
  --button-size-lg-text-line: var(--type-body-line);
123
- --savingbar-focus-ring: var(--focus-ring);
123
+ --savingbar-focus-ring: var(--text-on-primary);
124
+ --savingbar-button-fill-background: var(--text-on-primary);
125
+ --savingbar-button-fill-label: var(--colors-primary-default);
126
+ --savingbar-button-ghost-label: var(--text-on-primary);
124
127
 
125
128
  /* blocks */
126
129
  --topnav-background: var(--surface-card);