@rojaostudio/ds 2.0.0-next.0 → 2.0.0-next.10

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.
@@ -0,0 +1,66 @@
1
+ # Avisos de terceiros
2
+
3
+ O Rojão DS é distribuído sob a [licença MIT](../../LICENSE). Alguns trechos vêm de projetos de terceiros, ou se apoiam neles, sob as licenças abaixo.
4
+
5
+ ## Incluído no pacote
6
+
7
+ ### Lucide (ícones)
8
+
9
+ Os SVGs em `packages/ds/icons/svg/` e os traçados de `packages/ds/components/internal/icons.tsx` vêm do [Lucide](https://lucide.dev), sob a licença ISC. Parte do Lucide vem do Feather, sob a licença MIT.
10
+
11
+ ```
12
+ ISC License
13
+
14
+ Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part of Feather (MIT).
15
+ All other copyright (c) for Lucide are held by Lucide Contributors 2022.
16
+
17
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is
18
+ hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
21
+ INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
22
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
23
+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
24
+ CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25
+ ```
26
+
27
+ ```
28
+ The MIT License (MIT) — Feather
29
+
30
+ Copyright (c) 2013-2022 Cole Bemis
31
+
32
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
33
+ documentation files (the "Software"), to deal in the Software without restriction, including without
34
+ limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
35
+ Software, and to permit persons to whom the Software is furnished to do so, subject to the following
36
+ conditions:
37
+
38
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions
39
+ of the Software.
40
+
41
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
42
+ TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
43
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
44
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
45
+ DEALINGS IN THE SOFTWARE.
46
+ ```
47
+
48
+ ### Pix
49
+
50
+ O símbolo do Pix (`packages/ds/icons/pix.tsx`) é marca do Banco Central do Brasil. Ele está aqui só para indicar o Pix como meio de pagamento, conforme o manual de uso da marca. A licença MIT deste projeto não se estende a ele.
51
+
52
+ ## Dependências (instaladas pelo gerenciador de pacotes, não redistribuídas)
53
+
54
+ | Pacote | Licença | Uso |
55
+ |---|---|---|
56
+ | `@radix-ui/react-*` | MIT | comportamento acessível de overlays, menus, abas e afins |
57
+ | `lucide-react` (peer opcional) | ISC | re-exportação de ícones em `@rojaostudio/ds/icons` |
58
+ | `react-image-crop` (peer opcional) | ISC | recorte do `ImageCropModal` |
59
+ | `react-international-phone` (peer opcional) | MIT | campo e bandeiras do `PhoneInput` |
60
+
61
+ ## Fontes (referenciadas pelo nome, não distribuídas)
62
+
63
+ | Fonte | Licença |
64
+ |---|---|
65
+ | Inter | SIL Open Font License 1.1 |
66
+ | Roboto Mono | Apache License 2.0 |
@@ -10,7 +10,7 @@ interface ChartSeries {
10
10
  /** line only: which axis it reads on. right only when the two series have different units (volume and rate). */
11
11
  axis?: 'left' | 'right';
12
12
  }
13
- interface ChartProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
13
+ interface ChartProps extends Omit<HTMLAttributes<HTMLElement>, 'children' | 'onSelect'> {
14
14
  /** What the chart shows, with the period. It names the chart and captions its data table. */
15
15
  label: string;
16
16
  /** line: lines over time; bar: a ranking in horizontal bars; column: columns by category. */
@@ -32,8 +32,22 @@ interface ChartProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
32
32
  showLegend?: boolean;
33
33
  /** The values of the point under the pointer or the keyboard (Figma: `showTooltip`). */
34
34
  showTooltip?: boolean;
35
- /** Off: only the first, middle and last label (Figma: `showAllDates`). Use it under about 360 px. */
35
+ /**
36
+ * line and column: which x labels show (Figma: `dates`). edges, the default: the first, middle and last; all: every
37
+ * label, for short series only.
38
+ */
39
+ dates?: 'edges' | 'all';
40
+ /** line and column: one label every N points, with a tick on every point (Figma: `dates=every`). Wins over `dates`. */
41
+ dateEvery?: number;
42
+ /** @deprecated Use `dates="all"` (true) or `dates="edges"` (false). */
36
43
  showAllDates?: boolean;
44
+ /**
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.
47
+ */
48
+ hrefs?: Array<string | undefined>;
49
+ /** bar and column: called with the item's index when it is chosen (click, Enter). In bar each bar becomes a button. */
50
+ onSelect?: (index: number) => void;
37
51
  /** Show the data table under the chart. By default it is there only for screen readers. */
38
52
  showTable?: boolean;
39
53
  /** Height of the plot area, in px (Figma: 200). */
@@ -44,6 +58,6 @@ interface ChartProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
44
58
  * data (visually hidden unless `showTable`). In line and column the plot is a slider for screen readers and the
45
59
  * keyboard: the arrows move from point to point, and each point is read with its label and values. Styles: chart.css.
46
60
  */
47
- declare function Chart({ label, type, labels, series, formatLeft, formatRight, showRightAxis, showLegend, showTooltip, showAllDates, showTable, height, className, ...rest }: ChartProps): react.JSX.Element;
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;
48
62
 
49
63
  export { Chart, type ChartProps, type ChartSeries, type ChartType };
@@ -36,7 +36,11 @@ function Chart(_a) {
36
36
  showRightAxis = true,
37
37
  showLegend = true,
38
38
  showTooltip = true,
39
- showAllDates = true,
39
+ dates,
40
+ dateEvery,
41
+ showAllDates,
42
+ hrefs,
43
+ onSelect,
40
44
  showTable = false,
41
45
  height = 200,
42
46
  className
@@ -50,7 +54,11 @@ function Chart(_a) {
50
54
  "showRightAxis",
51
55
  "showLegend",
52
56
  "showTooltip",
57
+ "dates",
58
+ "dateEvery",
53
59
  "showAllDates",
60
+ "hrefs",
61
+ "onSelect",
54
62
  "showTable",
55
63
  "height",
56
64
  "className"
@@ -78,7 +86,14 @@ function Chart(_a) {
78
86
  return `${s.name} ${formatFor(s)((_a3 = s.data[index]) != null ? _a3 : 0)}`;
79
87
  }).join(", ");
80
88
  const legendName = (s) => dual ? `${s.name} (${onRight(s) ? "direita" : "esquerda"})` : s.name;
81
- const labelIndexes = showAllDates || count <= 3 ? labels.map((_, i) => i) : [0, Math.floor((count - 1) / 2), count - 1];
89
+ const every = dateEvery && dateEvery > 1 ? Math.floor(dateEvery) : null;
90
+ const allDates = (dates != null ? dates : showAllDates === void 0 ? "edges" : showAllDates ? "all" : "edges") === "all";
91
+ const labelIndexes = every ? labels.map((_, i) => i).filter((i) => i % every === 0) : allDates || count <= 3 ? labels.map((_, i) => i) : [0, Math.floor((count - 1) / 2), count - 1];
92
+ const linked = type !== "line" && Boolean(onSelect || (hrefs == null ? void 0 : hrefs.some(Boolean)));
93
+ const choose = (index) => {
94
+ if (onSelect) onSelect(index);
95
+ else if (hrefs == null ? void 0 : hrefs[index]) window.location.assign(hrefs[index]);
96
+ };
82
97
  const onPointerMove = (event) => {
83
98
  const box = event.currentTarget.getBoundingClientRect();
84
99
  const ratio = Math.min(1, Math.max(0, (event.clientX - box.left) / box.width));
@@ -94,6 +109,11 @@ function Chart(_a) {
94
109
  Home: 0,
95
110
  End: count - 1
96
111
  };
112
+ if (linked && event.key === "Enter" && active !== null) {
113
+ event.preventDefault();
114
+ choose(active);
115
+ return;
116
+ }
97
117
  if (!(event.key in keys)) return;
98
118
  event.preventDefault();
99
119
  setActive(keys[event.key]);
@@ -133,6 +153,7 @@ function Chart(_a) {
133
153
  onPointerLeave: (event) => {
134
154
  if (document.activeElement !== event.currentTarget) setActive(null);
135
155
  },
156
+ onClick: linked ? () => active !== null && choose(active) : void 0,
136
157
  onFocus: () => setActive((current) => current != null ? current : 0),
137
158
  onBlur: () => setActive(null),
138
159
  onKeyDown
@@ -142,19 +163,37 @@ function Chart(_a) {
142
163
  if (type === "bar") {
143
164
  const s = shown[0];
144
165
  const top = Math.max(0, ...(_a2 = s == null ? void 0 : s.data) != null ? _a2 : [0]);
145
- body = /* @__PURE__ */ jsx("div", { className: "rds-chart__bars", "aria-hidden": "true", children: labels.map((name, i) => {
166
+ body = /* @__PURE__ */ jsx("div", { className: "rds-chart__bars", "aria-hidden": linked ? void 0 : true, children: labels.map((name, i) => {
146
167
  var _a3, _b2;
147
- return /* @__PURE__ */ jsxs("div", { className: "rds-chart__bar-row", children: [
168
+ const value = s ? formatLeft((_a3 = s.data[i]) != null ? _a3 : 0) : "";
169
+ const parts = /* @__PURE__ */ jsxs(Fragment, { children: [
148
170
  /* @__PURE__ */ jsx("span", { className: "rds-chart__bar-label", children: name }),
149
171
  /* @__PURE__ */ jsx("span", { className: "rds-chart__track", children: /* @__PURE__ */ jsx(
150
172
  "span",
151
173
  {
152
174
  className: "rds-chart__bar",
153
- style: __spreadProps(__spreadValues({}, seriesStyle(i + 1)), { width: `${top ? ((_a3 = s == null ? void 0 : s.data[i]) != null ? _a3 : 0) / top * 100 : 0}%` })
175
+ style: __spreadProps(__spreadValues({}, seriesStyle(i + 1)), { width: `${top ? ((_b2 = s == null ? void 0 : s.data[i]) != null ? _b2 : 0) / top * 100 : 0}%` })
154
176
  }
155
177
  ) }),
156
- /* @__PURE__ */ jsx("span", { className: "rds-chart__bar-value", children: s ? formatLeft((_b2 = s.data[i]) != null ? _b2 : 0) : "" })
157
- ] }, name);
178
+ /* @__PURE__ */ jsx("span", { className: "rds-chart__bar-value", children: value })
179
+ ] });
180
+ const href = hrefs == null ? void 0 : hrefs[i];
181
+ const accessibleName = `${name}: ${value}`;
182
+ if (linked && href && !onSelect)
183
+ return /* @__PURE__ */ jsx("a", { href, className: "rds-chart__bar-row rds-chart__bar-row--link", "aria-label": accessibleName, children: parts }, name);
184
+ if (linked && onSelect)
185
+ return /* @__PURE__ */ jsx(
186
+ "button",
187
+ {
188
+ type: "button",
189
+ className: "rds-chart__bar-row rds-chart__bar-row--link",
190
+ "aria-label": accessibleName,
191
+ onClick: () => onSelect(i),
192
+ children: parts
193
+ },
194
+ name
195
+ );
196
+ return /* @__PURE__ */ jsx("div", { className: "rds-chart__bar-row", children: parts }, name);
158
197
  }) });
159
198
  } else {
160
199
  body = /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -208,7 +247,10 @@ function Chart(_a) {
208
247
  ] })),
209
248
  rightScale && /* @__PURE__ */ jsx(Axis, { ticks: rightScale.ticks, format: formatRight, side: "right" })
210
249
  ] }),
211
- /* @__PURE__ */ jsx("div", { className: "rds-chart__labels", "aria-hidden": "true", children: /* @__PURE__ */ jsx("div", { className: `rds-chart__labels-track rds-chart__labels-track--${type}`, children: labelIndexes.map((i) => /* @__PURE__ */ jsx("span", { className: "rds-chart__label", style: { left: `${x(i)}%` }, children: labels[i] }, i)) }) })
250
+ /* @__PURE__ */ jsx("div", { className: "rds-chart__labels", "aria-hidden": "true", children: /* @__PURE__ */ jsxs("div", { className: `rds-chart__labels-track rds-chart__labels-track--${type}${every ? " rds-chart__labels-track--ticks" : ""}`, children: [
251
+ every && labels.map((_, i) => /* @__PURE__ */ jsx("i", { className: "rds-chart__tick", style: { left: `${x(i)}%` } }, `t${i}`)),
252
+ labelIndexes.map((i) => /* @__PURE__ */ jsx("span", { className: "rds-chart__label", style: { left: `${x(i)}%` }, children: labels[i] }, i))
253
+ ] }) })
212
254
  ] });
213
255
  }
214
256
  const firstColumn = type === "line" ? "Data" : "Categoria";
@@ -216,7 +258,7 @@ function Chart(_a) {
216
258
  "figure",
217
259
  __spreadProps(__spreadValues({}, rest), {
218
260
  "aria-label": label,
219
- className: ["rds-chart", `rds-chart--${type}`, dual && "rds-chart--dual", className].filter(Boolean).join(" "),
261
+ className: ["rds-chart", `rds-chart--${type}`, dual && "rds-chart--dual", linked && "rds-chart--linked", className].filter(Boolean).join(" "),
220
262
  children: [
221
263
  body,
222
264
  showLegend && type !== "bar" && /* @__PURE__ */ jsx("ul", { className: "rds-chart__legend", "aria-hidden": "true", children: shown.map((s, n) => /* @__PURE__ */ jsxs("li", { children: [
@@ -229,26 +271,30 @@ function Chart(_a) {
229
271
  shown.map((s) => /* @__PURE__ */ jsx(TableHead, { align: "end", children: legendName(s) }, s.name))
230
272
  ] }) }),
231
273
  /* @__PURE__ */ jsx(TableBody, { children: labels.map((name, i) => /* @__PURE__ */ jsxs(TableRow, { children: [
232
- /* @__PURE__ */ jsx(TableCell, { children: name }),
274
+ /* @__PURE__ */ jsx(TableCell, { children: linked && onSelect ? /* @__PURE__ */ jsx("button", { type: "button", className: "rds-chart__table-link", onClick: () => onSelect(i), children: name }) : linked && (hrefs == null ? void 0 : hrefs[i]) ? /* @__PURE__ */ jsx("a", { className: "rds-chart__table-link", href: hrefs[i], children: name }) : name }),
233
275
  shown.map((s) => {
234
276
  var _a3;
235
277
  return /* @__PURE__ */ jsx(TableCell, { align: "end", children: formatFor(s)((_a3 = s.data[i]) != null ? _a3 : 0) }, s.name);
236
278
  })
237
279
  ] }, name)) })
238
- ] }) : /* @__PURE__ */ jsxs("table", { className: "rds-visually-hidden", children: [
239
- /* @__PURE__ */ jsx("caption", { children: label }),
240
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
241
- /* @__PURE__ */ jsx("th", { scope: "col", children: firstColumn }),
242
- shown.map((s) => /* @__PURE__ */ jsx("th", { scope: "col", children: legendName(s) }, s.name))
243
- ] }) }),
244
- /* @__PURE__ */ jsx("tbody", { children: labels.map((name, i) => /* @__PURE__ */ jsxs("tr", { children: [
245
- /* @__PURE__ */ jsx("th", { scope: "row", children: name }),
246
- shown.map((s) => {
247
- var _a3;
248
- return /* @__PURE__ */ jsx("td", { children: formatFor(s)((_a3 = s.data[i]) != null ? _a3 : 0) }, s.name);
249
- })
250
- ] }, name)) })
251
- ] })
280
+ ] }) : (
281
+ // The clip goes on a wrapper: a <table> ignores the 1px box and overflow of .rds-visually-hidden, so it
282
+ // kept its full size, anchored to the page and stretched the page scroll.
283
+ /* @__PURE__ */ jsx("div", { className: "rds-visually-hidden", children: /* @__PURE__ */ jsxs("table", { children: [
284
+ /* @__PURE__ */ jsx("caption", { children: label }),
285
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
286
+ /* @__PURE__ */ jsx("th", { scope: "col", children: firstColumn }),
287
+ shown.map((s) => /* @__PURE__ */ jsx("th", { scope: "col", children: legendName(s) }, s.name))
288
+ ] }) }),
289
+ /* @__PURE__ */ jsx("tbody", { children: labels.map((name, i) => /* @__PURE__ */ jsxs("tr", { children: [
290
+ /* @__PURE__ */ jsx("th", { scope: "row", children: name }),
291
+ shown.map((s) => {
292
+ var _a3;
293
+ return /* @__PURE__ */ jsx("td", { children: formatFor(s)((_a3 = s.data[i]) != null ? _a3 : 0) }, s.name);
294
+ })
295
+ ] }, name)) })
296
+ ] }) })
297
+ )
252
298
  ]
253
299
  })
254
300
  );
@@ -0,0 +1,40 @@
1
+ import * as react from 'react';
2
+ import { ComponentPropsWithRef, ReactNode } from 'react';
3
+
4
+ interface ChoiceListProps extends Omit<ComponentPropsWithRef<'ul'>, 'children' | 'defaultValue' | 'onChange'> {
5
+ /** The ChoiceListItems (Figma: the slot `items`). */
6
+ children: ReactNode;
7
+ /** Controlled: the chosen row's value. */
8
+ value?: string;
9
+ /** Uncontrolled: the row chosen at first. */
10
+ defaultValue?: string;
11
+ onValueChange?: (value: string) => void;
12
+ }
13
+ /**
14
+ * ChoiceList — Figma [RDS] Content/ChoiceList. A dense list to choose one thing from: a client, a product, an
15
+ * order. Each row is media, a title, a muted description and a trailing value; a line under each row, dozens on
16
+ * screen. One chosen at a time, marked by the tint and a bar on the left (role listbox, aria-selected). The list
17
+ * takes the focus: the arrows move the active row past disabled ones, Home and End go to the ends, Enter or Space
18
+ * choose, a click chooses. Name it with aria-label or aria-labelledby. Styles: choice-list.css.
19
+ *
20
+ * Not for 2 to 4 options that need an explanation (ChoiceCard), nor for a list that only shows (Item).
21
+ */
22
+ declare function ChoiceList({ children, value, defaultValue, onValueChange, className, onKeyDown, onFocus, ref, ...rest }: ChoiceListProps): react.JSX.Element;
23
+ interface ChoiceListItemProps {
24
+ /** The value it stands for. */
25
+ value: string;
26
+ /** The name, one line (Figma: `title`). It is the row's accessible name. */
27
+ title: ReactNode;
28
+ /** One muted line under the title (Figma: `showDescription` + `description`). */
29
+ description?: ReactNode;
30
+ /** An Avatar for a person, a Tile for a thing, 32 (Figma: `showMedia` + `media`). Decorative: hidden from screen readers. */
31
+ media?: ReactNode;
32
+ /** A value or a date on the right (Figma: `showTrailing` + `trailing`). */
33
+ trailing?: ReactNode;
34
+ /** Cannot be chosen now: skipped by the arrows, read as unavailable. */
35
+ disabled?: boolean;
36
+ }
37
+ /** One row of the ChoiceList (Figma: .choice-list/item). The whole row is the target. */
38
+ declare function ChoiceListItem({ value, title, description, media, trailing, disabled }: ChoiceListItemProps): react.JSX.Element;
39
+
40
+ export { ChoiceList, ChoiceListItem, type ChoiceListItemProps, type ChoiceListProps };
@@ -0,0 +1,122 @@
1
+ "use client";
2
+ import {
3
+ __objRest,
4
+ __spreadProps,
5
+ __spreadValues
6
+ } from "../chunk-ORMEWXMH.js";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ import {
9
+ createContext,
10
+ useContext,
11
+ useId,
12
+ useRef,
13
+ useState
14
+ } from "react";
15
+ import { warnIfUnlabelled } from "./internal/field.js";
16
+ const ChoiceListContext = createContext(null);
17
+ function ChoiceList(_a) {
18
+ var _b = _a, { children, value, defaultValue, onValueChange, className, onKeyDown, onFocus, ref } = _b, rest = __objRest(_b, ["children", "value", "defaultValue", "onValueChange", "className", "onKeyDown", "onFocus", "ref"]);
19
+ warnIfUnlabelled("ChoiceList", void 0, rest["aria-label"], rest["aria-labelledby"]);
20
+ const [own, setOwn] = useState(defaultValue);
21
+ const selected = value !== void 0 ? value : own;
22
+ const [activeId, setActiveId] = useState(null);
23
+ const list = useRef(null);
24
+ const rows = () => {
25
+ var _a2, _b2;
26
+ return Array.from((_b2 = (_a2 = list.current) == null ? void 0 : _a2.querySelectorAll('[role="option"]')) != null ? _b2 : []).filter(
27
+ (o) => o.getAttribute("aria-disabled") !== "true"
28
+ );
29
+ };
30
+ function choose(next) {
31
+ if (value === void 0) setOwn(next);
32
+ onValueChange == null ? void 0 : onValueChange(next);
33
+ }
34
+ function activate(row) {
35
+ if (!row) return;
36
+ setActiveId(row.id);
37
+ row.scrollIntoView({ block: "nearest" });
38
+ }
39
+ function keyDown(event) {
40
+ var _a2;
41
+ onKeyDown == null ? void 0 : onKeyDown(event);
42
+ if (event.defaultPrevented) return;
43
+ const all = rows();
44
+ const index = all.findIndex((o) => o.id === activeId);
45
+ const moves = {
46
+ ArrowDown: () => all[Math.min(index + 1, all.length - 1)],
47
+ ArrowUp: () => all[Math.max(index - 1, 0)],
48
+ Home: () => all[0],
49
+ End: () => all[all.length - 1]
50
+ };
51
+ if (moves[event.key]) {
52
+ event.preventDefault();
53
+ activate(moves[event.key]());
54
+ } else if ((event.key === "Enter" || event.key === " ") && index >= 0) {
55
+ event.preventDefault();
56
+ choose((_a2 = all[index].dataset.value) != null ? _a2 : "");
57
+ }
58
+ }
59
+ function setRef(node) {
60
+ list.current = node;
61
+ if (typeof ref === "function") ref(node);
62
+ else if (ref) ref.current = node;
63
+ }
64
+ return /* @__PURE__ */ jsx(ChoiceListContext.Provider, { value: { selected, activeId, setActiveId, choose }, children: /* @__PURE__ */ jsx(
65
+ "ul",
66
+ __spreadProps(__spreadValues({
67
+ tabIndex: 0
68
+ }, rest), {
69
+ ref: setRef,
70
+ role: "listbox",
71
+ "aria-activedescendant": activeId != null ? activeId : void 0,
72
+ className: ["rds-choice-list", className].filter(Boolean).join(" "),
73
+ onKeyDown: keyDown,
74
+ onFocus: (event) => {
75
+ var _a2;
76
+ onFocus == null ? void 0 : onFocus(event);
77
+ if (activeId && document.getElementById(activeId)) return;
78
+ const all = rows();
79
+ activate((_a2 = all.find((o) => o.dataset.value === selected)) != null ? _a2 : all[0]);
80
+ },
81
+ children
82
+ })
83
+ ) });
84
+ }
85
+ function ChoiceListItem({ value, title, description, media, trailing, disabled }) {
86
+ const context = useContext(ChoiceListContext);
87
+ const id = useId();
88
+ if (!context) throw new Error("[@rojaostudio/ds] ChoiceListItem goes inside a ChoiceList.");
89
+ const selected = context.selected === value;
90
+ const describedBy = [description != null && `${id}-description`, trailing != null && `${id}-trailing`].filter(Boolean).join(" ");
91
+ return /* @__PURE__ */ jsxs(
92
+ "li",
93
+ {
94
+ id,
95
+ role: "option",
96
+ "data-value": value,
97
+ "aria-selected": selected,
98
+ "aria-disabled": disabled || void 0,
99
+ "aria-labelledby": `${id}-title`,
100
+ "aria-describedby": describedBy || void 0,
101
+ "data-active": context.activeId === id ? "" : void 0,
102
+ className: "rds-choice-list__item",
103
+ onClick: () => {
104
+ if (disabled) return;
105
+ context.setActiveId(id);
106
+ context.choose(value);
107
+ },
108
+ children: [
109
+ media != null && /* @__PURE__ */ jsx("span", { className: "rds-choice-list__media", "aria-hidden": "true", children: media }),
110
+ /* @__PURE__ */ jsxs("span", { className: "rds-choice-list__content", children: [
111
+ /* @__PURE__ */ jsx("span", { id: `${id}-title`, className: "rds-choice-list__title", children: title }),
112
+ description != null && /* @__PURE__ */ jsx("span", { id: `${id}-description`, className: "rds-choice-list__description", children: description })
113
+ ] }),
114
+ trailing != null && /* @__PURE__ */ jsx("span", { id: `${id}-trailing`, className: "rds-choice-list__trailing", children: trailing })
115
+ ]
116
+ }
117
+ );
118
+ }
119
+ export {
120
+ ChoiceList,
121
+ ChoiceListItem
122
+ };
@@ -47,6 +47,7 @@ export { ChipInput, ChipInputProps } from './chip-input.js';
47
47
  export { CopyField, CopyFieldProps } from './copy-field.js';
48
48
  export { QRDisplay, QRDisplayProps } from './qr-display.js';
49
49
  export { ChoiceCard } from './choice-card.js';
50
+ export { ChoiceList, ChoiceListItem, ChoiceListItemProps, ChoiceListProps } from './choice-list.js';
50
51
  export { OptionTile, OptionTileGrid, OptionTileGridProps, OptionTileItem, OptionTileProps } from './option-tile.js';
51
52
  export { SelectableCard, SelectableCardProps } from './selectable-card.js';
52
53
  export { ChoicePreviewCard, ChoicePreviewCardProps } from './choice-preview-card.js';
@@ -99,9 +100,6 @@ export { Chip, ChipProps } from './chip.js';
99
100
  export { CardsSkeleton, CardsSkeletonProps, PageSkeleton, PageSkeletonProps } from './page-skeleton.js';
100
101
  export { CurrencyInput, CurrencyInputProps } from './currency-input.js';
101
102
  export { ColorInput, ColorInputProps, normalizeHex } from './color-input.js';
102
- export { ImageUpload, ImageUploadAspect, ImageUploadLabels, ImageUploadProps, ImageUploadVariant } from './image-upload.js';
103
- export { CropPreset, ImageCropModal, ImageCropModalProps } from './image-crop-modal.js';
104
- export { PhoneInput, PhoneInputProps } from './phone-input.js';
105
103
  export { RowActionItem, RowActions, RowActionsProps } from './row-actions.js';
106
104
  export { PageShell, PageShellBodyProps, PageShellHeaderProps, PageShellProps, PageShellWidth } from './page-shell.js';
107
105
  export { DangerZone, DangerZoneItem, DangerZoneItemProps, DangerZoneProps } from './danger-zone.js';
@@ -54,6 +54,7 @@ import { ChipInput } from "./chip-input.js";
54
54
  import { CopyField } from "./copy-field.js";
55
55
  import { QRDisplay } from "./qr-display.js";
56
56
  import { ChoiceCard, ChoiceCardGroup } from "./choice-card.js";
57
+ import { ChoiceList, ChoiceListItem } from "./choice-list.js";
57
58
  import { OptionTile, OptionTileGrid } from "./option-tile.js";
58
59
  import { SelectableCard } from "./selectable-card.js";
59
60
  import { ChoicePreviewCard } from "./choice-preview-card.js";
@@ -106,9 +107,6 @@ import { Chip } from "./chip.js";
106
107
  import { PageSkeleton, CardsSkeleton } from "./page-skeleton.js";
107
108
  import { CurrencyInput } from "./currency-input.js";
108
109
  import { ColorInput, normalizeHex } from "./color-input.js";
109
- import { ImageUpload } from "./image-upload.js";
110
- import { ImageCropModal } from "./image-crop-modal.js";
111
- import { PhoneInput } from "./phone-input.js";
112
110
  import { RowActions } from "./row-actions.js";
113
111
  import { PageShell } from "./page-shell.js";
114
112
  import { DangerZone, DangerZoneItem } from "./danger-zone.js";
@@ -164,6 +162,8 @@ export {
164
162
  ChoiceCard,
165
163
  ChoiceCardGroup,
166
164
  ChoiceCarousel,
165
+ ChoiceList,
166
+ ChoiceListItem,
167
167
  ChoicePreviewCard,
168
168
  ColorInput,
169
169
  Combobox,
@@ -210,8 +210,6 @@ export {
210
210
  GoogleSignIn,
211
211
  HoverCard,
212
212
  IconButton,
213
- ImageCropModal,
214
- ImageUpload,
215
213
  Input,
216
214
  InputOTP,
217
215
  InsightCard,
@@ -236,7 +234,6 @@ export {
236
234
  Pagination,
237
235
  PasswordInput,
238
236
  Phone,
239
- PhoneInput,
240
237
  Popover,
241
238
  PopoverClose,
242
239
  Pricing,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rojaostudio/ds",
3
- "version": "2.0.0-next.0",
3
+ "version": "2.0.0-next.10",
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",
@@ -100,6 +100,7 @@
100
100
  },
101
101
  "files": [
102
102
  "LICENSE",
103
+ "THIRD_PARTY_NOTICES.md",
103
104
  "README.md",
104
105
  "dist",
105
106
  "styles"
@@ -153,7 +154,7 @@
153
154
  "publishConfig": {
154
155
  "registry": "https://registry.npmjs.org",
155
156
  "access": "public",
156
- "provenance": false
157
+ "provenance": true
157
158
  },
158
159
  "devDependencies": {
159
160
  "@types/node": "^20.19.43",
@@ -190,7 +191,7 @@
190
191
  "@radix-ui/react-toggle": "^1.1.18",
191
192
  "@radix-ui/react-toggle-group": "^1.1.19",
192
193
  "@radix-ui/react-tooltip": "^1.2.16",
193
- "@rojaostudio/ds-core": "1.1.0-next.0"
194
+ "@rojaostudio/ds-core": "1.1.0-next.5"
194
195
  },
195
196
  "scripts": {
196
197
  "build": "pnpm run build:tokens && pnpm run build:themes && pnpm run build:rds && pnpm run build:native && tsup",
@@ -1992,6 +1992,8 @@ button.rds-breadcrumb__link:focus-visible {
1992
1992
  * in --_series: chart.tsx picks one of the --_series-N locals below, each one a chart/series-color/N token.
1993
1993
  */
1994
1994
  .rds-chart {
1995
+ /* Anchors the hidden alternative table (and any absolute part) to the chart, not to the page. */
1996
+ position: relative;
1995
1997
  --_series-1: var(--chart-series-color-1);
1996
1998
  --_series-2: var(--chart-series-color-2);
1997
1999
  --_series-3: var(--chart-series-color-3);
@@ -2180,14 +2182,31 @@ button.rds-breadcrumb__link:focus-visible {
2180
2182
  white-space: nowrap;
2181
2183
  }
2182
2184
 
2183
- .rds-chart__labels-track--line .rds-chart__label:first-child {
2185
+ .rds-chart__labels-track--line .rds-chart__label:first-of-type {
2184
2186
  transform: none;
2185
2187
  }
2186
2188
 
2187
- .rds-chart__labels-track--line .rds-chart__label:last-child {
2189
+ .rds-chart__labels-track--line .rds-chart__label:last-of-type {
2188
2190
  transform: translateX(-100%);
2189
2191
  }
2190
2192
 
2193
+ /* dateEvery: a 1x4 tick on every point, the labels under them. */
2194
+ .rds-chart__labels-track--ticks {
2195
+ height: calc(var(--type-caption-line) + var(--space-4));
2196
+ }
2197
+
2198
+ .rds-chart__labels-track--ticks .rds-chart__label {
2199
+ top: var(--space-4);
2200
+ }
2201
+
2202
+ .rds-chart__tick {
2203
+ position: absolute;
2204
+ top: 0;
2205
+ width: 1px;
2206
+ height: var(--space-4);
2207
+ background: var(--chart-tick);
2208
+ }
2209
+
2191
2210
  /* Bars: label, track, value. */
2192
2211
  .rds-chart__bars {
2193
2212
  display: flex;
@@ -2218,6 +2237,50 @@ button.rds-breadcrumb__link:focus-visible {
2218
2237
  background: var(--_series);
2219
2238
  }
2220
2239
 
2240
+ /* A bar with a destination (hrefs or onSelect): the row is the link or button. */
2241
+ .rds-chart__bar-row--link {
2242
+ box-sizing: border-box;
2243
+ width: 100%;
2244
+ margin: 0;
2245
+ padding: 0;
2246
+ border: 0;
2247
+ border-radius: var(--radius-focus);
2248
+ background: none;
2249
+ color: inherit;
2250
+ font: inherit;
2251
+ text-align: start;
2252
+ text-decoration: none;
2253
+ cursor: pointer;
2254
+ }
2255
+
2256
+ .rds-chart__bar-row--link:hover .rds-chart__track {
2257
+ background: var(--chart-bar-hover);
2258
+ }
2259
+
2260
+ .rds-chart__bar-row--link:hover .rds-chart__bar-label {
2261
+ text-decoration: underline;
2262
+ }
2263
+
2264
+ .rds-chart__bar-row--link:focus-visible,
2265
+ .rds-chart__table-link:focus-visible {
2266
+ outline: var(--focus-ring-width) solid var(--chart-focus-ring);
2267
+ outline-offset: var(--focus-ring-width);
2268
+ }
2269
+
2270
+ .rds-chart--linked.rds-chart--column .rds-chart__plot {
2271
+ cursor: pointer;
2272
+ }
2273
+
2274
+ .rds-chart__table-link {
2275
+ padding: 0;
2276
+ border: 0;
2277
+ background: none;
2278
+ color: inherit;
2279
+ font: inherit;
2280
+ text-decoration: underline;
2281
+ cursor: pointer;
2282
+ }
2283
+
2221
2284
  .rds-chart__bar-value {
2222
2285
  color: var(--chart-axis);
2223
2286
  font-variant-numeric: tabular-nums;
@@ -2251,7 +2314,8 @@ button.rds-breadcrumb__link:focus-visible {
2251
2314
  background: CanvasText;
2252
2315
  }
2253
2316
 
2254
- .rds-chart__plot:focus-visible {
2317
+ .rds-chart__plot:focus-visible,
2318
+ .rds-chart__bar-row--link:focus-visible {
2255
2319
  outline-color: Highlight;
2256
2320
  }
2257
2321
  }
@@ -2993,6 +3057,132 @@ input:focus-visible + .rds-checkbox__box {
2993
3057
  }
2994
3058
  }
2995
3059
 
3060
+ /* components/choice-list.css */
3061
+ /* @tokens choice-list */
3062
+ /*
3063
+ * ChoiceList (Content collection, choice-list/*): the dense list to choose one from. Each row pads 12 16 with 12
3064
+ * between media, text and trailing; a line of border-width only at the bottom (padding-bottom gives it back, so the
3065
+ * row is 60 with a description, 44 without). Title small medium, description caption, trailing small.
3066
+ * Hover: choice-list/item/background/hover. Chosen: the tint and a bar of border-width-strong on the left, no hover.
3067
+ * Focus: the list holds it (aria-activedescendant); the active row draws the ring inside its box, as the rows touch.
3068
+ * Disabled: text in the disabled tokens, media at half; it stays chosen if it was.
3069
+ */
3070
+ .rds-choice-list {
3071
+ box-sizing: border-box;
3072
+ margin: 0;
3073
+ padding: 0;
3074
+ overflow-y: auto;
3075
+ font-family: var(--type-font-stack);
3076
+ list-style: none;
3077
+ outline: none;
3078
+ }
3079
+
3080
+ .rds-choice-list__item {
3081
+ --_background: transparent;
3082
+ --_title: var(--choice-list-item-title-default);
3083
+ --_description: var(--choice-list-item-description-default);
3084
+ --_trailing: var(--choice-list-item-trailing-default);
3085
+
3086
+ box-sizing: border-box;
3087
+ display: flex;
3088
+ align-items: center;
3089
+ gap: var(--space-12);
3090
+ min-height: calc(var(--space-12) * 2 + var(--type-button-line));
3091
+ padding: var(--space-12) var(--space-16) calc(var(--space-12) - var(--border-width));
3092
+ border-bottom: var(--border-width) solid var(--choice-list-item-border);
3093
+ background: var(--_background);
3094
+ cursor: pointer;
3095
+ user-select: none;
3096
+ }
3097
+
3098
+ @media (hover: hover) {
3099
+ .rds-choice-list__item:not([aria-selected='true'], [aria-disabled='true']):hover {
3100
+ --_background: var(--choice-list-item-background-hover);
3101
+ }
3102
+ }
3103
+
3104
+ .rds-choice-list__item[aria-selected='true'] {
3105
+ --_background: var(--choice-list-item-background-selected);
3106
+ /* On the tint the muted line does not hold contrast in dark: it takes text/on/tint. */
3107
+ --_description: var(--choice-list-item-description-selected);
3108
+
3109
+ box-shadow: inset var(--border-width-strong) 0 0 var(--choice-list-item-indicator);
3110
+ }
3111
+
3112
+ .rds-choice-list:focus-visible .rds-choice-list__item[data-active] {
3113
+ outline: var(--focus-ring-width) solid var(--choice-list-focus-ring);
3114
+ outline-offset: calc(-1 * var(--focus-ring-width));
3115
+ }
3116
+
3117
+ .rds-choice-list__item[aria-disabled='true'] {
3118
+ --_title: var(--choice-list-item-title-disabled);
3119
+ --_description: var(--choice-list-item-description-disabled);
3120
+ --_trailing: var(--choice-list-item-trailing-disabled);
3121
+
3122
+ cursor: not-allowed;
3123
+ }
3124
+
3125
+ .rds-choice-list__media {
3126
+ display: inline-flex;
3127
+ flex: none;
3128
+ }
3129
+
3130
+ .rds-choice-list__item[aria-disabled='true'] .rds-choice-list__media {
3131
+ opacity: 0.5;
3132
+ }
3133
+
3134
+ .rds-choice-list__content {
3135
+ display: flex;
3136
+ flex: 1;
3137
+ flex-direction: column;
3138
+ min-width: 0;
3139
+ }
3140
+
3141
+ .rds-choice-list__title,
3142
+ .rds-choice-list__description {
3143
+ overflow: hidden;
3144
+ text-overflow: ellipsis;
3145
+ white-space: nowrap;
3146
+ }
3147
+
3148
+ .rds-choice-list__title {
3149
+ color: var(--_title);
3150
+ font-size: var(--type-small-size);
3151
+ line-height: var(--type-small-line);
3152
+ font-weight: 500;
3153
+ }
3154
+
3155
+ .rds-choice-list__description {
3156
+ color: var(--_description);
3157
+ font-size: var(--type-caption-size);
3158
+ line-height: var(--type-caption-line);
3159
+ }
3160
+
3161
+ .rds-choice-list__trailing {
3162
+ flex: none;
3163
+ color: var(--_trailing);
3164
+ font-size: var(--type-small-size);
3165
+ line-height: var(--type-small-line);
3166
+ font-variant-numeric: tabular-nums;
3167
+ text-align: right;
3168
+ }
3169
+
3170
+ @media (forced-colors: active) {
3171
+ .rds-choice-list__item[aria-selected='true'] {
3172
+ forced-color-adjust: none;
3173
+ background: Highlight;
3174
+ color: HighlightText;
3175
+ }
3176
+
3177
+ .rds-choice-list__item[aria-selected='true'] :is(.rds-choice-list__title, .rds-choice-list__description, .rds-choice-list__trailing) {
3178
+ color: HighlightText;
3179
+ }
3180
+
3181
+ .rds-choice-list:focus-visible .rds-choice-list__item[data-active] {
3182
+ outline-color: CanvasText;
3183
+ }
3184
+ }
3185
+
2996
3186
  /* components/color-input.css */
2997
3187
  /* @tokens color-input */
2998
3188
  /*
@@ -5298,6 +5488,23 @@ input:focus-visible + .rds-checkbox__box {
5298
5488
  font: inherit;
5299
5489
  }
5300
5490
 
5491
+ /*
5492
+ * Browser autofill paints the control's own background (blue in light, yellow in dark) and the control is smaller
5493
+ * than the box that draws the border: it showed as a patch in the middle of the field. Cover it with the box
5494
+ * colour and keep the value colour. The background-color transition is the fallback for browsers that ignore the
5495
+ * inset shadow on autofill.
5496
+ */
5497
+ .rds-field__control:-webkit-autofill,
5498
+ .rds-field__control:-webkit-autofill:hover,
5499
+ .rds-field__control:-webkit-autofill:focus,
5500
+ .rds-field__control:autofill {
5501
+ -webkit-box-shadow: 0 0 0 1000px var(--_background) inset;
5502
+ box-shadow: 0 0 0 1000px var(--_background) inset;
5503
+ -webkit-text-fill-color: var(--_value);
5504
+ caret-color: var(--_value);
5505
+ transition: background-color 600000s 0s;
5506
+ }
5507
+
5301
5508
  .rds-field__control::placeholder {
5302
5509
  color: var(--_placeholder);
5303
5510
  opacity: 1;
@@ -8772,9 +8979,12 @@ input:focus-visible + .rds-radio__circle {
8772
8979
  cursor: pointer;
8773
8980
  }
8774
8981
 
8982
+ /* Figma .sidebar/section: label in caps with 6% tracking; the count keeps its own case and no tracking. */
8775
8983
  .rds-sidebar__section-label {
8776
8984
  flex: 1;
8777
8985
  min-width: 0;
8986
+ text-transform: uppercase;
8987
+ letter-spacing: 0.06em;
8778
8988
  }
8779
8989
 
8780
8990
  .rds-sidebar__section-count {
@@ -292,6 +292,9 @@
292
292
  --chart-tooltip-border: var(--border-default);
293
293
  --chart-label: var(--text-body);
294
294
  --chart-tooltip-title: var(--text-heading);
295
+ --chart-bar-hover: var(--surface-muted);
296
+ --chart-focus-ring: var(--focus-ring);
297
+ --chart-tick: var(--border-strong);
295
298
  --carousel-slide-background: var(--surface-muted);
296
299
  --carousel-dot: var(--border-strong);
297
300
  --carousel-dot-active: var(--colors-primary-default);
@@ -371,6 +374,18 @@
371
374
  --choice-card-preview-background: var(--surface-muted);
372
375
  --choice-card-preview-border: var(--border-default);
373
376
  --choice-card-preview-icon: var(--text-subtle);
377
+ --choice-list-item-background-hover: var(--surface-muted);
378
+ --choice-list-item-background-selected: var(--surface-tint-default);
379
+ --choice-list-item-indicator: var(--colors-primary-default);
380
+ --choice-list-item-border: var(--border-default);
381
+ --choice-list-item-title-default: var(--text-body);
382
+ --choice-list-item-title-disabled: var(--text-disabled);
383
+ --choice-list-item-description-default: var(--text-muted);
384
+ --choice-list-item-description-disabled: var(--text-disabled);
385
+ --choice-list-item-trailing-default: var(--text-body);
386
+ --choice-list-item-trailing-disabled: var(--text-disabled);
387
+ --choice-list-focus-ring: var(--focus-ring);
388
+ --choice-list-item-description-selected: var(--text-on-tint);
374
389
 
375
390
  /* external */
376
391
  --google-button-light-background: #ffffff;