@rojaostudio/ds 2.0.0-next.20 → 2.0.0-next.22

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.
@@ -35,6 +35,12 @@ type DataTableHeaderProps = {
35
35
  * Checkbox "Agrupar por produto". In view in the compact arrangement too.
36
36
  */
37
37
  view?: ReactNode;
38
+ /**
39
+ * The list's action (Figma: `showActions`, off by default, + the `actions` instance): an IconButton, neutral outline
40
+ * md, with its Tooltip, such as a gear "Organizar categorias" or exporting. It sits outside the tools, always at the
41
+ * end of the FIRST line, on the right; the tools wrap, the action never goes down.
42
+ * Creating is NOT an action of the bar: creating is the FAB. The component does not enforce this; it documents it.
43
+ */
38
44
  actions?: ReactNode;
39
45
  className?: string;
40
46
  /** In the compact arrangement (bar below 1024), moves the filters and the actions into a Drawer. The quick filters stay in view. */
@@ -47,8 +53,10 @@ declare function FilterDropdown({ f, placement }: {
47
53
  }): react.JSX.Element;
48
54
  /**
49
55
  * DataTableHeader — a composition of the Input (search), FilterChips, Buttons, the DropdownMenu, the Popover and
50
- * the Drawer: the bar above a table. The search is always on the left and takes the free width (at least 320); the
51
- * filters are always on the right; what does not fit wraps. Quick filters go in the `quickFilters` slot; one filter
56
+ * the Drawer: the bar above a table. A row that never wraps: the tools (the search, the quick filters, the filters,
57
+ * the view control) wrap inside their own group; the action stays outside it, at the end of the first line, on the
58
+ * right. The search is always on the left and takes the free width (at least 320); the filters are always on the
59
+ * right; what does not fit wraps. Quick filters go in the `quickFilters` slot; one filter
52
60
  * is a dropdown, two or more collapse into "Filtros" (the active ones stay in view as removable Chips); the `view`
53
61
  * control comes after the filters, before the actions. "Filtros · N" counts the active `filters`, the ones that
54
62
  * button opens.
@@ -155,85 +155,87 @@ function DataTableHeader({
155
155
  const singleFilter = filters.length === 1 ? filters[0] : null;
156
156
  const lead = Boolean(search || quickFilters);
157
157
  return /* @__PURE__ */ jsxs("div", { className: ["rds-data-table-header", className].filter(Boolean).join(" "), children: [
158
- search && /* @__PURE__ */ jsx(
159
- Input,
160
- {
161
- className: "rds-data-table-header__search",
162
- type: "search",
163
- "aria-label": (_a = search.placeholder) != null ? _a : "Buscar\u2026",
164
- placeholder: (_b = search.placeholder) != null ? _b : "Buscar\u2026",
165
- leadingIcon: /* @__PURE__ */ jsx(SearchIcon, {}),
166
- clearable: true,
167
- clearLabel: "Limpar busca",
168
- value: search.value,
169
- onChange: (e) => search.onChange(e.target.value)
170
- }
171
- ),
172
- quickFilters && /* @__PURE__ */ jsx("div", { className: "rds-data-table-header__quick", children: quickFilters }),
173
- filters.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rds-data-table-header__wide", children: [
174
- lead && /* @__PURE__ */ jsx(Separator, { orientation: "vertical" }),
175
- collapseDesktop ? /* @__PURE__ */ jsxs(Fragment, { children: [
176
- activeFilters.map((f) => /* @__PURE__ */ jsx(Chip, { onRemove: () => f.onChange(""), removeLabel: `Remover filtro ${f.label}`, children: optionLabel(f) }, f.key)),
177
- /* @__PURE__ */ jsx(
178
- FiltersPopover,
179
- {
180
- filters,
181
- onClear,
182
- showClear: activeFilterCount > 0,
183
- trigger: /* @__PURE__ */ jsx(FiltersButton, { count: activeFilterCount })
184
- }
185
- )
186
- ] }) : filters.map((f) => /* @__PURE__ */ jsx(FilterDropdown, { f }, f.key)),
187
- onClear && activeFilterCount > 0 && !collapseDesktop && /* @__PURE__ */ jsx(Tooltip, { text: "Limpar filtros", children: /* @__PURE__ */ jsx(IconButton, { icon: /* @__PURE__ */ jsx(CloseIcon, {}), label: "Limpar filtros", variant: "ghost", tone: "neutral", onClick: onClear }) })
188
- ] }),
189
- singleFilter && !(mobileCollapse && actions) && /* @__PURE__ */ jsx("div", { className: "rds-data-table-header__narrow", children: /* @__PURE__ */ jsx(MobileSingleFilter, { f: singleFilter }) }),
190
- !mobileCollapse && filters.length >= 2 && /* @__PURE__ */ jsx("div", { className: "rds-data-table-header__narrow", children: /* @__PURE__ */ jsx(
191
- FiltersPopover,
192
- {
193
- filters,
194
- onClear,
195
- showClear: activeFilterCount > 0,
196
- trigger: /* @__PURE__ */ jsx(
197
- CompactFilterTrigger,
198
- {
199
- label: "Filtros",
200
- state: activeState(activeFilterCount),
201
- indicator: activeFilterCount > 0 ? activeFilterCount : void 0
202
- }
203
- )
204
- }
205
- ) }),
206
- mobileCollapse && (filters.length >= 2 || actions) && /* @__PURE__ */ jsxs("div", { className: "rds-data-table-header__narrow", children: [
207
- /* @__PURE__ */ jsx(
208
- CompactFilterTrigger,
158
+ /* @__PURE__ */ jsxs("div", { className: "rds-data-table-header__tools", children: [
159
+ search && /* @__PURE__ */ jsx(
160
+ Input,
209
161
  {
210
- label: "Filtros",
211
- state: activeState(activeFilterCount),
212
- indicator: activeFilterCount === 0 ? void 0 : filters.length === 1 ? "dot" : activeFilterCount,
213
- onClick: () => setSheetOpen(true)
162
+ className: "rds-data-table-header__search",
163
+ type: "search",
164
+ "aria-label": (_a = search.placeholder) != null ? _a : "Buscar\u2026",
165
+ placeholder: (_b = search.placeholder) != null ? _b : "Buscar\u2026",
166
+ leadingIcon: /* @__PURE__ */ jsx(SearchIcon, {}),
167
+ clearable: true,
168
+ clearLabel: "Limpar busca",
169
+ value: search.value,
170
+ onChange: (e) => search.onChange(e.target.value)
214
171
  }
215
172
  ),
216
- /* @__PURE__ */ jsx(Drawer, { open: sheetOpen, onOpenChange: setSheetOpen, title: "Filtros", children: /* @__PURE__ */ jsxs("div", { className: "rds-data-table-header__groups", children: [
173
+ quickFilters && /* @__PURE__ */ jsx("div", { className: "rds-data-table-header__quick", children: quickFilters }),
174
+ filters.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rds-data-table-header__wide", children: [
175
+ lead && /* @__PURE__ */ jsx(Separator, { orientation: "vertical" }),
176
+ collapseDesktop ? /* @__PURE__ */ jsxs(Fragment, { children: [
177
+ activeFilters.map((f) => /* @__PURE__ */ jsx(Chip, { onRemove: () => f.onChange(""), removeLabel: `Remover filtro ${f.label}`, children: optionLabel(f) }, f.key)),
178
+ /* @__PURE__ */ jsx(
179
+ FiltersPopover,
180
+ {
181
+ filters,
182
+ onClear,
183
+ showClear: activeFilterCount > 0,
184
+ trigger: /* @__PURE__ */ jsx(FiltersButton, { count: activeFilterCount })
185
+ }
186
+ )
187
+ ] }) : filters.map((f) => /* @__PURE__ */ jsx(FilterDropdown, { f }, f.key)),
188
+ onClear && activeFilterCount > 0 && !collapseDesktop && /* @__PURE__ */ jsx(Tooltip, { text: "Limpar filtros", children: /* @__PURE__ */ jsx(IconButton, { icon: /* @__PURE__ */ jsx(CloseIcon, {}), label: "Limpar filtros", variant: "ghost", tone: "neutral", onClick: onClear }) })
189
+ ] }),
190
+ singleFilter && !(mobileCollapse && actions) && /* @__PURE__ */ jsx("div", { className: "rds-data-table-header__narrow", children: /* @__PURE__ */ jsx(MobileSingleFilter, { f: singleFilter }) }),
191
+ !mobileCollapse && filters.length >= 2 && /* @__PURE__ */ jsx("div", { className: "rds-data-table-header__narrow", children: /* @__PURE__ */ jsx(
192
+ FiltersPopover,
193
+ {
194
+ filters,
195
+ onClear,
196
+ showClear: activeFilterCount > 0,
197
+ trigger: /* @__PURE__ */ jsx(
198
+ CompactFilterTrigger,
199
+ {
200
+ label: "Filtros",
201
+ state: activeState(activeFilterCount),
202
+ indicator: activeFilterCount > 0 ? activeFilterCount : void 0
203
+ }
204
+ )
205
+ }
206
+ ) }),
207
+ mobileCollapse && (filters.length >= 2 || actions) && /* @__PURE__ */ jsxs("div", { className: "rds-data-table-header__narrow", children: [
217
208
  /* @__PURE__ */ jsx(
218
- FilterGroups,
209
+ CompactFilterTrigger,
219
210
  {
220
- filters,
221
- onPick: (f, v) => {
222
- f.onChange(v);
223
- setSheetOpen(false);
224
- },
225
- onClear: onClear ? () => {
226
- onClear();
227
- setSheetOpen(false);
228
- } : void 0,
229
- showClear: activeFilterCount > 0
211
+ label: "Filtros",
212
+ state: activeState(activeFilterCount),
213
+ indicator: activeFilterCount === 0 ? void 0 : filters.length === 1 ? "dot" : activeFilterCount,
214
+ onClick: () => setSheetOpen(true)
230
215
  }
231
216
  ),
232
- actions && /* @__PURE__ */ jsx("div", { className: "rds-data-table-header__drawer-actions", children: actions })
233
- ] }) })
217
+ /* @__PURE__ */ jsx(Drawer, { open: sheetOpen, onOpenChange: setSheetOpen, title: "Filtros", children: /* @__PURE__ */ jsxs("div", { className: "rds-data-table-header__groups", children: [
218
+ /* @__PURE__ */ jsx(
219
+ FilterGroups,
220
+ {
221
+ filters,
222
+ onPick: (f, v) => {
223
+ f.onChange(v);
224
+ setSheetOpen(false);
225
+ },
226
+ onClear: onClear ? () => {
227
+ onClear();
228
+ setSheetOpen(false);
229
+ } : void 0,
230
+ showClear: activeFilterCount > 0
231
+ }
232
+ ),
233
+ actions && /* @__PURE__ */ jsx("div", { className: "rds-data-table-header__drawer-actions", children: actions })
234
+ ] }) })
235
+ ] }),
236
+ view && /* @__PURE__ */ jsx("div", { className: "rds-data-table-header__view", children: view })
234
237
  ] }),
235
- view && /* @__PURE__ */ jsx("div", { className: "rds-data-table-header__view", children: view }),
236
- actions && /* @__PURE__ */ jsx("div", { className: mobileCollapse ? "rds-data-table-header__wide" : "rds-data-table-header__actions", children: actions })
238
+ actions && /* @__PURE__ */ jsx("div", { className: ["rds-data-table-header__actions", mobileCollapse && "rds-data-table-header__wide"].filter(Boolean).join(" "), children: actions })
237
239
  ] });
238
240
  }
239
241
  export {
@@ -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.20",
3
+ "version": "2.0.0-next.22",
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",
@@ -3583,9 +3583,10 @@ input:focus-visible + .rds-checkbox__box {
3583
3583
  /* @tokens data-table-header */
3584
3584
  /*
3585
3585
  * DataTableHeader: no tokens of its own; the Input, FilterChip, Chip, Button, Separator, Checkbox, DropdownMenu,
3586
- * Popover and Drawer bring theirs. One row, 8 apart, 12 above the table, packed to the right: the search on the left
3587
- * takes the free width, at least 320 (never wider than the bar); the quick filters, the filters, the view control
3588
- * and the actions follow it on the right, and what does not fit wraps.
3586
+ * Popover and Drawer bring theirs. The bar is one row that never wraps, aligned to the top, 8 apart, 12 above the
3587
+ * table: the `tools` group (the search, the quick filters, the filters, the view control) takes the free width and
3588
+ * is what wraps, packed to the right, the search on the left taking the free width, at least 320 (never wider than
3589
+ * the group); the action sits outside it, fixed at the end of the FIRST line, on the right, and never goes down.
3589
3590
  * Two arrangements, by the screen width, as the Figma viewport mode (layout/compact below lg 1024; the SavingBar's cut):
3590
3591
  * expanded from 1024 (the __wide parts: Separator, Buttons with labels); compact below it (the __narrow parts: the
3591
3592
  * filter as an IconButton of 44), where the search accepts 200. On the IconButton, while a filter is on, a dot of 8
@@ -3596,11 +3597,27 @@ input:focus-visible + .rds-checkbox__box {
3596
3597
  box-sizing: border-box;
3597
3598
  width: 100%;
3598
3599
  display: flex;
3600
+ flex-wrap: nowrap;
3601
+ align-items: flex-start;
3602
+ gap: var(--space-8);
3603
+ margin-bottom: var(--space-12);
3604
+ }
3605
+
3606
+ /* Figma `tools`: horizontal, wrap, fill. */
3607
+ .rds-data-table-header__tools {
3608
+ flex: 1 1 0;
3609
+ min-width: 0;
3610
+ display: flex;
3599
3611
  flex-wrap: wrap;
3600
3612
  align-items: center;
3601
3613
  justify-content: flex-end;
3602
3614
  gap: var(--space-8);
3603
- margin-bottom: var(--space-12);
3615
+ }
3616
+
3617
+ /* Figma `actions`: outside `tools`, at the end of the first line. */
3618
+ .rds-data-table-header__actions {
3619
+ flex: none;
3620
+ align-self: flex-start;
3604
3621
  }
3605
3622
 
3606
3623
  /* Scoped to the bar: the field's own `min-width: 0` (internal/field.css) comes later in the bundle. */
@@ -8443,10 +8460,14 @@ input:focus-visible + .rds-radio__circle {
8443
8460
  /* @tokens savingbar */
8444
8461
  /*
8445
8462
  * SavingBar (Actions collection, savingbar/*): full width (at least 320), 12 24 padding, 16 between the
8446
- * message (label, medium, at most 2 lines with an ellipsis) and the actions (8 apart). The bar is the brand plate
8447
- * (.ds-plate on the element, set by the component): savingbar/background is surface/page and savingbar/text is text/body,
8448
- * read in the brand mode, so it comes out the same over a light or a dark page. The Buttons are tone action (Salvar)
8449
- * 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.
8450
8471
  * Two arrangements, by the screen width, as the Figma viewport mode (layout/compact below lg 1024): expanded from 1024,
8451
8472
  * the message grows (at least 160) beside the actions; compact below it, the message on top and Descartar and
8452
8473
  * Salvar under it, sharing the width.
@@ -8463,6 +8484,23 @@ input:focus-visible + .rds-radio__circle {
8463
8484
  background: var(--savingbar-background);
8464
8485
  color: var(--savingbar-text);
8465
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);
8466
8504
  }
8467
8505
 
8468
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);