@12-apps/ui 6.3.1 → 6.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/{chunk-WFPXRSIZ.js → chunk-5TCPZ6GR.js} +7 -5
  2. package/dist/chunk-5TCPZ6GR.js.map +1 -0
  3. package/dist/{chunk-TPQRIPCN.js → chunk-AR5LUIBC.js} +12 -9
  4. package/dist/chunk-AR5LUIBC.js.map +1 -0
  5. package/dist/{chunk-R5ZQZSEQ.js → chunk-NUCLEOHF.js} +12 -6
  6. package/dist/chunk-NUCLEOHF.js.map +1 -0
  7. package/dist/data-display/CardKit.js +1 -1
  8. package/dist/data-display/DataViews.js +30 -23
  9. package/dist/data-display/DataViews.js.map +1 -1
  10. package/dist/feedback/ConfirmAction.js +1 -1
  11. package/dist/form/CategorySelect.js +1 -1
  12. package/dist/form/CreatableSelect.js +1 -1
  13. package/dist/form/CreatableSelect.js.map +1 -1
  14. package/dist/form/SearchPalette.js +3 -4
  15. package/dist/form/SearchPalette.js.map +1 -1
  16. package/dist/form/total-form.js +1 -1
  17. package/dist/layout/ContentToolbar.js +1 -1
  18. package/dist/pt-BR.js +28 -5
  19. package/dist/pt-BR.js.map +1 -1
  20. package/dist/types/components/data-display/DataViews/DataViewsBoard.d.ts.map +1 -1
  21. package/dist/types/components/data-display/DataViews/base-list-card-gutters.d.ts.map +1 -1
  22. package/dist/types/components/data-display/DataViews/data-views-category-pill.d.ts.map +1 -1
  23. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts +44 -1
  24. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts.map +1 -1
  25. package/dist/types/components/data-display/DataViews/data-views-empty.d.ts.map +1 -1
  26. package/dist/types/components/data-display/DataViews/data-views-more-fields.d.ts.map +1 -1
  27. package/dist/types/components/data-display/DataViews/data-views-more-filters.d.ts.map +1 -1
  28. package/dist/types/components/data-display/DataViews/data-views-range-pill.d.ts.map +1 -1
  29. package/dist/types/components/data-display/DataViews/data-views-toolbar.d.ts.map +1 -1
  30. package/dist/types/components/feedback/ConfirmAction/ConfirmAction.parts.d.ts.map +1 -1
  31. package/dist/types/components/form/CategorySelect/CategoryPanelList.d.ts.map +1 -1
  32. package/dist/types/components/form/CategorySelect/CategoryQuickActions.d.ts +4 -1
  33. package/dist/types/components/form/CategorySelect/CategoryQuickActions.d.ts.map +1 -1
  34. package/dist/types/components/form/CategorySelect/CategoryRows.d.ts +3 -1
  35. package/dist/types/components/form/CategorySelect/CategoryRows.d.ts.map +1 -1
  36. package/dist/types/components/form/CreatableSelect/CreatableSelect.d.ts.map +1 -1
  37. package/dist/types/components/form/CreatableSelect/CreatableSelect.types.d.ts +2 -1
  38. package/dist/types/components/form/CreatableSelect/CreatableSelect.types.d.ts.map +1 -1
  39. package/dist/types/components/form/SearchPalette/SearchPalette.d.ts.map +1 -1
  40. package/dist/types/components/form/SearchPalette/SearchPalette.types.d.ts +3 -3
  41. package/dist/types/components/form/SearchPalette/SearchPalette.types.d.ts.map +1 -1
  42. package/dist/types/components/layout/ContentToolbar/ContentToolbar.d.ts +1 -1
  43. package/dist/types/components/layout/ContentToolbar/ContentToolbar.d.ts.map +1 -1
  44. package/dist/types/components/layout/ContentToolbar/ContentToolbar.types.d.ts +12 -0
  45. package/dist/types/components/layout/ContentToolbar/ContentToolbar.types.d.ts.map +1 -1
  46. package/dist/types/components/layout/ContentToolbar/MultiSelectDropdown.d.ts +1 -1
  47. package/dist/types/components/layout/ContentToolbar/MultiSelectDropdown.d.ts.map +1 -1
  48. package/dist/types/components/layout/ContentToolbar/MultiSelectTriggers.d.ts +10 -2
  49. package/dist/types/components/layout/ContentToolbar/MultiSelectTriggers.d.ts.map +1 -1
  50. package/dist/types/copy.d.ts +29 -0
  51. package/dist/types/copy.d.ts.map +1 -1
  52. package/dist/types/pt-BR.d.ts.map +1 -1
  53. package/package.json +1 -1
  54. package/src/components/data-display/DataViews/DataViewsBoard.tsx +2 -1
  55. package/src/components/data-display/DataViews/DeleteViewDialog.tsx +2 -2
  56. package/src/components/data-display/DataViews/ManageViewsDialog.tsx +1 -1
  57. package/src/components/data-display/DataViews/base-list-card-gutters.tsx +2 -1
  58. package/src/components/data-display/DataViews/data-views-category-pill.tsx +1 -0
  59. package/src/components/data-display/DataViews/data-views-columns-tab.tsx +4 -3
  60. package/src/components/data-display/DataViews/data-views-copy.ts +44 -1
  61. package/src/components/data-display/DataViews/data-views-empty.tsx +6 -3
  62. package/src/components/data-display/DataViews/data-views-more-fields.tsx +3 -1
  63. package/src/components/data-display/DataViews/data-views-more-filters.tsx +2 -1
  64. package/src/components/data-display/DataViews/data-views-range-pill.tsx +2 -1
  65. package/src/components/data-display/DataViews/data-views-toolbar.tsx +3 -0
  66. package/src/components/data-display/DataViews/data-views-view-nav.tsx +2 -2
  67. package/src/components/feedback/ConfirmAction/ConfirmAction.parts.tsx +6 -2
  68. package/src/components/form/CategorySelect/CategoryPanel.tsx +1 -1
  69. package/src/components/form/CategorySelect/CategoryPanelList.tsx +1 -0
  70. package/src/components/form/CategorySelect/CategoryQuickActions.tsx +7 -2
  71. package/src/components/form/CategorySelect/CategoryRows.tsx +6 -1
  72. package/src/components/form/CategorySelect/CategorySelect.tsx +1 -1
  73. package/src/components/form/CreatableSelect/CreatableSelect.md +2 -2
  74. package/src/components/form/CreatableSelect/CreatableSelect.tsx +1 -1
  75. package/src/components/form/CreatableSelect/CreatableSelect.types.ts +2 -1
  76. package/src/components/form/SearchPalette/SearchPalette.tsx +3 -4
  77. package/src/components/form/SearchPalette/SearchPalette.types.ts +3 -3
  78. package/src/components/layout/ContentToolbar/ContentToolbar.tsx +13 -2
  79. package/src/components/layout/ContentToolbar/ContentToolbar.types.ts +12 -0
  80. package/src/components/layout/ContentToolbar/MultiSelectDropdown.tsx +2 -0
  81. package/src/components/layout/ContentToolbar/MultiSelectTriggers.tsx +15 -2
  82. package/src/copy.ts +29 -0
  83. package/src/pt-BR.ts +23 -0
  84. package/dist/chunk-R5ZQZSEQ.js.map +0 -1
  85. package/dist/chunk-TPQRIPCN.js.map +0 -1
  86. package/dist/chunk-WFPXRSIZ.js.map +0 -1
@@ -60,6 +60,7 @@ function OverflowPill<T extends Record<string, unknown>>({
60
60
  if (options.length > INLINE_OPTION_LIMIT) {
61
61
  return (
62
62
  <MultiSelectDropdown
63
+ clearLabel={copy.filters.clearRange(field.label)}
63
64
  label={field.pill?.label ?? field.label}
64
65
  options={options}
65
66
  selected={new Set(values)}
@@ -213,6 +214,7 @@ export function MoreGroup<T extends Record<string, unknown>>({
213
214
  }: {
214
215
  field: OverflowField<T>;
215
216
  } & MoreFieldProps): React.JSX.Element {
217
+ const copy = useDataViewsCopy();
216
218
  const { applied, clear } = fieldClearing({ field, pills, ranges, onTogglePill, onChangeRange });
217
219
  return (
218
220
  <Box sx={{ mb: 1.5, "&:last-of-type": { mb: 0 } }}>
@@ -245,7 +247,7 @@ export function MoreGroup<T extends Record<string, unknown>>({
245
247
  "&:hover": { textDecoration: "underline" },
246
248
  }}
247
249
  >
248
- Limpar
250
+ {copy.filters.clear}
249
251
  </Box>
250
252
  )}
251
253
  </Box>
@@ -153,6 +153,7 @@ function MoreFooter({
153
153
  onClearAll: () => void;
154
154
  testIdPrefix: string;
155
155
  }): React.JSX.Element {
156
+ const copy = useDataViewsCopy();
156
157
  return (
157
158
  <Box sx={{ px: 1.5, py: 1, borderTop: 1, borderColor: "divider" }}>
158
159
  <Box
@@ -173,7 +174,7 @@ function MoreFooter({
173
174
  "&:hover": { textDecoration: "underline" },
174
175
  }}
175
176
  >
176
- Limpar todos os filtros
177
+ {copy.filters.clearAll}
177
178
  </Box>
178
179
  </Box>
179
180
  );
@@ -261,6 +261,7 @@ export function RangePill<T extends Record<string, unknown>>({
261
261
  onOpenChange?: (open: boolean) => void;
262
262
  testIdPrefix: string;
263
263
  }): React.JSX.Element {
264
+ const copy = useDataViewsCopy();
264
265
  const [anchor, setAnchor] = useState<HTMLElement | null>(null);
265
266
  const active = isRangeSet(value);
266
267
  const testId = `${testIdPrefix}-range-${field.id}`;
@@ -303,7 +304,7 @@ export function RangePill<T extends Record<string, unknown>>({
303
304
  data-testid={`${testId}-clear`}
304
305
  sx={{ textTransform: 'none' }}
305
306
  >
306
- Limpar
307
+ {copy.filters.clear}
307
308
  </Button>
308
309
  </Box>
309
310
  )}
@@ -11,6 +11,7 @@ import {
11
11
  import { Box } from "../../../mui/Box";
12
12
  import { Text } from "../../typography/Text";
13
13
 
14
+ import { useDataViewsCopy } from "./data-views-copy-context";
14
15
  import { DataViewsDisplayPanel, type DisplayPanelView } from "./data-views-display-panel";
15
16
  import { DataViewsExportMenu, type DataViewExport } from "./data-views-export";
16
17
  import { renderBulkActions } from "./data-views-grid-helpers";
@@ -136,6 +137,7 @@ function ToolbarRightControls<T extends Record<string, unknown>>(props: GridTool
136
137
  * separators reach the sidebar's vertical line and the window edge.
137
138
  */
138
139
  export function GridToolbar<T extends Record<string, unknown>>(props: GridToolbarProps<T>): React.JSX.Element {
140
+ const copy = useDataViewsCopy();
139
141
  const { testIdPrefix, selectedRows } = props;
140
142
  return (
141
143
  <Box
@@ -150,6 +152,7 @@ export function GridToolbar<T extends Record<string, unknown>>(props: GridToolba
150
152
  }}
151
153
  >
152
154
  <ContentToolbar
155
+ selectAllLabel={copy.selection.selectAll}
153
156
  hasSelection={selectedRows.length > 0}
154
157
  selectedCount={selectedRows.length}
155
158
  selectAll={props.selectAll}
@@ -342,7 +342,7 @@ export function ViewFooter({
342
342
  onClick={view.onReset}
343
343
  dataTestId={`${testIdPrefix}-display-reset`}
344
344
  >
345
- Redefinir
345
+ {copy.nav.reset}
346
346
  </Button>
347
347
  <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
348
348
  {/* Only meaningful with a view APPLIED: on {copy.nav.mainView} the single
@@ -358,7 +358,7 @@ export function ViewFooter({
358
358
  }}
359
359
  dataTestId={`${testIdPrefix}-display-save-as`}
360
360
  >
361
- Salvar como nova
361
+ {copy.nav.saveAs}
362
362
  </Button>
363
363
  )}
364
364
  {/* Enabled only when something in Ordenar / Colunas / Exibição (or the
@@ -60,9 +60,12 @@ function TypeToConfirmField({
60
60
  onChange,
61
61
  disabled,
62
62
  dataTestId,
63
+ copy,
63
64
  }: {
64
65
  expected: string;
65
66
  label?: string;
67
+ /** The words this prompt renders. REQUIRED — no default copy. */
68
+ copy: ConfirmActionCopy;
66
69
  value: string;
67
70
  onChange: (next: string) => void;
68
71
  disabled: boolean;
@@ -71,7 +74,7 @@ function TypeToConfirmField({
71
74
  return (
72
75
  <Stack spacing={1} sx={{ mt: 2 }}>
73
76
  <Typography variant="body2" color="text.secondary">
74
- {label ?? `Digite "${expected}" para confirmar.`}
77
+ {label ?? copy.typeToConfirm(expected)}
75
78
  </Typography>
76
79
  <TextField
77
80
  size="small"
@@ -81,7 +84,7 @@ function TypeToConfirmField({
81
84
  // On the INPUT rather than the wrapper: a test id on MUI's TextField
82
85
  // lands on the outer div, which is not the node a test can type into.
83
86
  inputProps={{
84
- 'aria-label': label ?? `Digite ${expected} para confirmar`,
87
+ 'aria-label': label ?? copy.typeToConfirm(expected),
85
88
  'data-testid': `${dataTestId}-type-to-confirm`,
86
89
  }}
87
90
  />
@@ -161,6 +164,7 @@ export function ConfirmActionDialog({
161
164
  onChange={setTyped}
162
165
  disabled={state.pending}
163
166
  dataTestId={dataTestId}
167
+ copy={copy}
164
168
  />
165
169
  )}
166
170
  </AlertDialog>
@@ -74,7 +74,7 @@ export function CategoryPanel({
74
74
  placeholder={single ? copy.search.placeholderSingle : copy.search.placeholderMulti}
75
75
  sheet={sheet}
76
76
  quickActions={
77
- single ? undefined : <CategoryQuickActions state={state} dataTestId={dataTestId} />
77
+ single ? undefined : <CategoryQuickActions state={state} dataTestId={dataTestId} copy={copy} />
78
78
  }
79
79
  onQueryChange={state.setQuery}
80
80
  searchInputRef={searchInputRef}
@@ -145,6 +145,7 @@ function CategoryGroupRows({
145
145
  onToggleExpanded={() => props.onToggleExpanded(categoryId)}
146
146
  onActivate={() => props.onActivateCategory(group)}
147
147
  dataTestId={props.dataTestId}
148
+ copy={props.copy}
148
149
  />
149
150
  )}
150
151
  {(expanded || asHeading) &&
@@ -3,6 +3,8 @@
3
3
  import { Box } from '@mui/material';
4
4
  import { useMemo } from 'react';
5
5
 
6
+ import type { CategorySelectCopy } from '../../../copy';
7
+
6
8
  import { collectLeafIds } from './category-tree';
7
9
  import type { CategorySelectState } from './useCategorySelect';
8
10
 
@@ -30,9 +32,12 @@ const linkButtonSx = {
30
32
  export function CategoryQuickActions({
31
33
  state,
32
34
  dataTestId,
35
+ copy,
33
36
  }: {
34
37
  state: CategorySelectState;
35
38
  dataTestId: string;
39
+ /** The words this control renders. REQUIRED — no default copy. */
40
+ copy: CategorySelectCopy;
36
41
  }): React.JSX.Element {
37
42
  const allLeafIds = useMemo(() => collectLeafIds(state.allGroups), [state.allGroups]);
38
43
  const everythingPicked = allLeafIds.length > 0 && state.draft.size >= allLeafIds.length;
@@ -50,7 +55,7 @@ export function CategoryQuickActions({
50
55
  sx={linkButtonSx}
51
56
  onClick={() => state.setDraft(everythingPicked ? new Set() : new Set(allLeafIds))}
52
57
  >
53
- {everythingPicked ? 'Desmarcar tudo' : 'Marcar tudo'}
58
+ {everythingPicked ? copy.deselectAll : copy.selectAll}
54
59
  </Box>
55
60
  <Box component="span" sx={{ color: 'divider' }}>
56
61
  ·
@@ -62,7 +67,7 @@ export function CategoryQuickActions({
62
67
  sx={linkButtonSx}
63
68
  onClick={() => state.setAllExpanded(!allExpanded)}
64
69
  >
65
- {allExpanded ? 'Recolher tudo' : 'Expandir tudo'}
70
+ {allExpanded ? copy.collapseAll : copy.expandAll}
66
71
  </Box>
67
72
  </>
68
73
  );
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { Box } from '@mui/material';
4
4
 
5
+ import type { CategorySelectCopy } from '../../../copy';
5
6
  import { CheckGlyph, DisclosureGlyph } from './CategoryIcons';
6
7
  import { highlightSegments, type CategoryCheckState } from './category-tree';
7
8
  import {
@@ -69,6 +70,7 @@ interface CategoryHeadRowProps {
69
70
  onToggleExpanded: () => void;
70
71
  onActivate: () => void;
71
72
  dataTestId: string;
73
+ copy: CategorySelectCopy;
72
74
  }
73
75
 
74
76
  /** The trailing number: how many children are picked, else the item count. */
@@ -101,6 +103,7 @@ export function CategoryHeadRow({
101
103
  onToggleExpanded,
102
104
  onActivate,
103
105
  dataTestId,
106
+ copy,
104
107
  }: CategoryHeadRowProps): React.JSX.Element {
105
108
  const meta = rowMeta(selectedCount, showCounts, option.count);
106
109
  return (
@@ -117,7 +120,9 @@ export function CategoryHeadRow({
117
120
  component="button"
118
121
  type="button"
119
122
  tabIndex={-1}
120
- aria-label={`${expanded ? 'Recolher' : 'Expandir'} ${option.name}`}
123
+ aria-label={
124
+ expanded ? copy.collapseCategory(option.name) : copy.expandCategory(option.name)
125
+ }
121
126
  data-testid={`${dataTestId}-expand-${option.id}`}
122
127
  sx={(theme) => chevronButtonSx(theme, expanded)}
123
128
  onClick={(event: React.MouseEvent) => {
@@ -51,7 +51,7 @@ function resolveConfig(props: CategorySelectProps): ResolvedConfig {
51
51
  const single = props.mode === 'single';
52
52
  return {
53
53
  single,
54
- placeholder: props.placeholder ?? (single ? 'Mover para…' : 'Categoria'),
54
+ placeholder: props.placeholder ?? (single ? props.copy.placeholderSingle : props.copy.placeholderMulti),
55
55
  disabled: props.disabled ?? false,
56
56
  loading: props.loading ?? false,
57
57
  fullWidth: props.fullWidth ?? false,
@@ -21,7 +21,7 @@ Type to filter the options; when the typed text matches nothing, an optional
21
21
  - **onCreate** (`(label: string) => void | Promise<void>`): enables create-on-Enter. When the typed text matches no option, a create row appears; choosing it calls `onCreate` with the raw text. The parent persists it and feeds the new option back through `options`/`value`. Omit to make the field a plain searchable select.
22
22
  - **label** (`string`): field label.
23
23
  - **placeholder** (`string`): placeholder shown when empty.
24
- - **createOptionLabel** (`(input: string) => string`): builds the create-row label (default: `Criar "<input>"`).
24
+ - **createOptionLabel** (`(input: string) => string`, REQUIRED): builds the create-row label. No default — a pt-BR one used to ship, which is how a host in any other language inherited it silently (FUT-760).
25
25
  - **noOptionsText** (`string`): text shown when nothing matches and creation is disabled.
26
26
  - **disabled** (`boolean`): disable the field.
27
27
  - **loading** (`boolean`): show a spinner in the adornment (e.g. while a create request is in flight).
@@ -71,7 +71,7 @@ Type to filter the options; when the typed text matches nothing, an optional
71
71
  1. Keep `options` in sync after `onCreate` resolves — append the created option and
72
72
  set `value` to it so the new selection sticks.
73
73
  2. Use `loading` while the create request is in flight to signal progress.
74
- 3. Provide a localized `createOptionLabel` when the UI language isn't Portuguese.
74
+ 3. `createOptionLabel` is required, so the create-row label is always the host’s own.
75
75
  4. Omit `onCreate` entirely when free creation is not allowed — the field then
76
76
  behaves as a plain searchable select and shows `noOptionsText`.
77
77
 
@@ -185,7 +185,7 @@ export function CreatableSelect({
185
185
  label,
186
186
  error,
187
187
  placeholder,
188
- createOptionLabel = (input) => `Criar "${input}"`,
188
+ createOptionLabel,
189
189
  noOptionsText,
190
190
  disabled = false,
191
191
  loading = false,
@@ -33,7 +33,8 @@ export interface CreatableSelectProps {
33
33
  /** Placeholder shown when empty. */
34
34
  placeholder?: string;
35
35
  /** Builds the create-row label from the typed text (default: `Criar "<text>"`). */
36
- createOptionLabel?: (input: string) => string;
36
+ /** How a new option is offered, naming what was typed. REQUIRED — no default copy. */
37
+ createOptionLabel: (input: string) => string;
37
38
  /** Text shown when no option matches and creation is disabled. */
38
39
  noOptionsText?: string;
39
40
  disabled?: boolean;
@@ -27,7 +27,6 @@ import type { SearchPaletteProps } from './SearchPalette.types';
27
27
  */
28
28
 
29
29
  const DEFAULT_MAX_ITEMS = 5;
30
- const defaultSubmitLabel = (q: string): string => `Todos os resultados para "${q}"`;
31
30
 
32
31
  /** Non-string option defaults (numbers / flags / arrays). */
33
32
  function resolveOpts<T>(props: SearchPaletteProps<T>): {
@@ -56,9 +55,9 @@ function resolveText<T>(props: SearchPaletteProps<T>): {
56
55
  dataTestId: string;
57
56
  } {
58
57
  return {
59
- submitAllLabel: props.submitAllLabel ?? defaultSubmitLabel,
60
- noResultsLabel: props.noResultsLabel ?? 'Nenhum resultado encontrado',
61
- placeholder: props.placeholder ?? 'Buscar…',
58
+ submitAllLabel: props.submitAllLabel,
59
+ noResultsLabel: props.noResultsLabel,
60
+ placeholder: props.placeholder,
62
61
  dataTestId: props.dataTestId ?? 'search-palette',
63
62
  };
64
63
  }
@@ -55,7 +55,7 @@ export interface SearchPaletteProps<T> {
55
55
  /** Footer "see all results" action; hidden when omitted. */
56
56
  onSubmitAll?: (query: string) => void;
57
57
  /** Footer label builder; defaults to `Todos os resultados para "{q}"`. */
58
- submitAllLabel?: (query: string) => string;
58
+ submitAllLabel: (query: string) => string;
59
59
 
60
60
  /**
61
61
  * Content shown when the query is empty (e.g. a "Populares" block). Keeps the
@@ -63,7 +63,7 @@ export interface SearchPaletteProps<T> {
63
63
  */
64
64
  emptyQueryContent?: React.ReactNode;
65
65
  /** Message when a non-empty query yields no items. */
66
- noResultsLabel?: string;
66
+ noResultsLabel: string;
67
67
 
68
68
  /** Loading spinner in the input while results resolve. */
69
69
  isLoading?: boolean;
@@ -71,7 +71,7 @@ export interface SearchPaletteProps<T> {
71
71
  showGhostText?: boolean;
72
72
  /** Autofocus the input on mount. */
73
73
  autoFocus?: boolean;
74
- placeholder?: string;
74
+ placeholder: string;
75
75
  inputAriaLabel?: string;
76
76
  /** Esc handler (host closes the overlay). */
77
77
  onClose?: () => void;
@@ -19,7 +19,15 @@ const selectionButtonSx = {
19
19
 
20
20
  type SelectionClusterProps = Pick<
21
21
  ContentToolbarProps,
22
- 'hasSelection' | 'selectedCount' | 'selectAll' | 'clearSelection' | 'actions' | 'selectAllTestId' | 'clearAllTestId' | 'edgeAlign'
22
+ | 'hasSelection'
23
+ | 'selectedCount'
24
+ | 'selectAll'
25
+ | 'clearSelection'
26
+ | 'actions'
27
+ | 'selectAllTestId'
28
+ | 'clearAllTestId'
29
+ | 'edgeAlign'
30
+ | 'selectAllLabel'
23
31
  >;
24
32
 
25
33
  /** The left cluster: Select All + (when selecting) Clear All / count / actions. */
@@ -32,6 +40,7 @@ function SelectionCluster({
32
40
  selectAllTestId,
33
41
  clearAllTestId,
34
42
  edgeAlign = false,
43
+ selectAllLabel,
35
44
  }: SelectionClusterProps): React.JSX.Element {
36
45
  const actionsSlot = actions !== undefined && (
37
46
  <Box sx={{ ml: 0.5, display: 'flex', flexShrink: 0, alignItems: 'center' }}>{actions}</Box>
@@ -66,7 +75,7 @@ function SelectionCluster({
66
75
  checked={hasSelection}
67
76
  onChange={(event) => (event.target.checked ? selectAll() : clearSelection())}
68
77
  data-testid={`${selectAllTestId}-checkbox`}
69
- inputProps={{ 'aria-label': 'Selecionar todos' }}
78
+ inputProps={{ 'aria-label': selectAllLabel }}
70
79
  sx={{ p: 0.5, display: { xs: 'inline-flex', md: 'none' } }}
71
80
  />
72
81
  {hasSelection ? (
@@ -206,6 +215,7 @@ export function ContentToolbar({
206
215
  clearAllTestId,
207
216
  edgeAlign = false,
208
217
  exclusiveSelection = false,
218
+ selectAllLabel,
209
219
  }: ContentToolbarProps): React.JSX.Element {
210
220
  // Under `exclusiveSelection` the two states are alternatives, not neighbours:
211
221
  // browsing shows the controls and no checkbox, selecting shows the checkbox
@@ -238,6 +248,7 @@ export function ContentToolbar({
238
248
  actions={actions}
239
249
  selectAllTestId={selectAllTestId}
240
250
  clearAllTestId={clearAllTestId}
251
+ selectAllLabel={selectAllLabel}
241
252
  edgeAlign={edgeAlign}
242
253
  />
243
254
  )}
@@ -4,6 +4,12 @@ import type { ReactNode } from 'react';
4
4
 
5
5
  /** Props for {@link ContentToolbar} — the shared content-page toolbar. */
6
6
  export interface ContentToolbarProps {
7
+ /**
8
+ * The select-all checkbox's accessible name. It carries no visible label of
9
+ * its own, so this is the only thing a screen reader gets for it. REQUIRED:
10
+ * this package ships no default copy.
11
+ */
12
+ selectAllLabel: string;
7
13
  /** Whether the page is in selection mode (shows Clear All + count + actions). */
8
14
  hasSelection: boolean;
9
15
  /** Number of currently selected items. */
@@ -129,6 +135,12 @@ export interface MultiSelectDropdownProps<TValue extends string = string> {
129
135
  onClear: () => void;
130
136
  /** Text when nothing or everything is selected. @default "All" */
131
137
  allLabel?: string;
138
+ /**
139
+ * The clear affordance's accessible name. The pill's own label is already on
140
+ * screen, so this is all a screen reader gets for the little cross.
141
+ * REQUIRED: this package ships no default copy.
142
+ */
143
+ clearLabel: string;
132
144
  /** Extra toggles under a labelled "Options" section. */
133
145
  extraOptions?: MultiSelectExtraOption[];
134
146
  /**
@@ -61,6 +61,7 @@ export function MultiSelectDropdown<TValue extends string = string>({
61
61
  onToggle,
62
62
  onClear,
63
63
  allLabel = 'All',
64
+ clearLabel,
64
65
  extraOptions,
65
66
  searchable,
66
67
  searchPlaceholder = 'Search…',
@@ -96,6 +97,7 @@ export function MultiSelectDropdown<TValue extends string = string>({
96
97
  if (layout === 'pill')
97
98
  return (
98
99
  <PillTrigger
100
+ clearLabel={clearLabel}
99
101
  label={label}
100
102
  selectedCount={selected.size}
101
103
  onClear={onClear}
@@ -102,6 +102,18 @@ export function StackedTrigger({ label, triggerLabel, open, onOpen, testId }: Tr
102
102
  }
103
103
 
104
104
  /** Rounded outlined chip (`Label ▾`, `Label (N) ▾` when selected) for a filter row. */
105
+ /** The pill's own props, above the component so the signature stays short. */
106
+ export type PillTriggerProps = TriggerProps & {
107
+ selectedCount: number;
108
+ onClear?: () => void;
109
+ /**
110
+ * The clear affordance's accessible name — the pill's own label is already
111
+ * on screen, so this is the only thing a screen reader gets for the little
112
+ * cross. REQUIRED: this package ships no default copy.
113
+ */
114
+ clearLabel: string;
115
+ };
116
+
105
117
  export function PillTrigger({
106
118
  label,
107
119
  open,
@@ -109,7 +121,8 @@ export function PillTrigger({
109
121
  testId,
110
122
  selectedCount,
111
123
  onClear,
112
- }: TriggerProps & { selectedCount: number; onClear?: () => void }): React.JSX.Element {
124
+ clearLabel,
125
+ }: PillTriggerProps): React.JSX.Element {
113
126
  const active = selectedCount > 0;
114
127
  return (
115
128
  <Button
@@ -129,7 +142,7 @@ export function PillTrigger({
129
142
  component="span"
130
143
  role="button"
131
144
  tabIndex={0}
132
- aria-label={`Limpar ${label}`}
145
+ aria-label={clearLabel}
133
146
  data-testid={testId ? `${testId}-clear` : undefined}
134
147
  onClick={(event: React.MouseEvent) => {
135
148
  // The pill's own click opens the menu; clearing must not.
package/src/copy.ts CHANGED
@@ -33,6 +33,12 @@ export interface ConfirmActionCopy {
33
33
  * never guesses a noun for someone else's rows.
34
34
  */
35
35
  irreversible: string;
36
+ /**
37
+ * The typed-confirmation prompt, naming the exact word that unlocks the
38
+ * button. A function because where that word sits in the sentence, and how
39
+ * it is quoted, is the translator's call.
40
+ */
41
+ typeToConfirm(expected: string): string;
36
42
  }
37
43
 
38
44
  export interface CategorySelectCopy {
@@ -45,6 +51,29 @@ export interface CategorySelectCopy {
45
51
  */
46
52
  purpose: string;
47
53
  clearSelection: string;
54
+ /**
55
+ * The quick actions above the tree. Each toggles as a PAIR, and each label
56
+ * says what pressing it will do rather than what state the list is in — so a
57
+ * translator needs both halves, not one plus a negation.
58
+ */
59
+ selectAll: string;
60
+ deselectAll: string;
61
+ expandAll: string;
62
+ collapseAll: string;
63
+ /**
64
+ * ONE row's chevron, in its two states — a glyph button inside the row
65
+ * button, so this pair is the whole of what a screen reader reads for it.
66
+ * The category's own name is appended by the caller.
67
+ */
68
+ expandCategory(name: string): string;
69
+ collapseCategory(name: string): string;
70
+ /**
71
+ * What the closed control invites, and it differs by mode: picking ONE
72
+ * category is a move, picking several is a filter. Both were pt-BR defaults
73
+ * behind an optional prop, which is the arrangement this port removes.
74
+ */
75
+ placeholderSingle: string;
76
+ placeholderMulti: string;
48
77
  /** The empty state, when the host has no categories at all. */
49
78
  emptyTitle: string;
50
79
  createCategory: string;
package/src/pt-BR.ts CHANGED
@@ -26,11 +26,20 @@ export const PT_BR_CONFIRM_ACTION_COPY: ConfirmActionCopy = {
26
26
  cancel: "Cancelar",
27
27
  defaultError: "Não foi possível concluir a ação. Tente novamente.",
28
28
  irreversible: "Esta ação não pode ser desfeita.",
29
+ typeToConfirm: (expected) => `Digite "${expected}" para confirmar.`,
29
30
  };
30
31
 
31
32
  export const PT_BR_CATEGORY_SELECT_COPY: CategorySelectCopy = {
32
33
  purpose: "Categorias organizam o cardápio e os filtros da loja.",
33
34
  clearSelection: "Limpar seleção",
35
+ selectAll: "Marcar tudo",
36
+ deselectAll: "Desmarcar tudo",
37
+ expandAll: "Expandir tudo",
38
+ collapseAll: "Recolher tudo",
39
+ expandCategory: (name) => `Expandir ${name}`,
40
+ collapseCategory: (name) => `Recolher ${name}`,
41
+ placeholderSingle: "Mover para…",
42
+ placeholderMulti: "Categoria",
34
43
  emptyTitle: "Nenhuma categoria ainda",
35
44
  createCategory: "Criar categoria",
36
45
  noResults: {
@@ -102,19 +111,23 @@ export const PT_BR_DATA_VIEWS_COPY: DataViewsCopy = {
102
111
  countOnPage: (label, count) => `${label}: ${count} nesta página`,
103
112
  onThisPage: (count) => `${count} nesta página`,
104
113
  pageSum: (sum) => `Soma desta página: ${sum}`,
114
+ emptyColumn: "Nada nesta etapa",
105
115
  pageScopeNote:
106
116
  "As contagens e somas abaixo referem-se apenas aos itens desta página.",
107
117
  },
108
118
  deleteView: {
109
119
  title: "Excluir visão",
120
+ target: (viewName) => `Excluir "${viewName}"?`,
110
121
  sharedWarning: "Esta visão está compartilhada — a equipe perderá o acesso a ela.",
111
122
  rowsUnaffected: (entityLabel) =>
112
123
  `${entityLabel} não são afetados; apenas o recorte salvo é removido.`,
113
124
  confirm: "Excluir visão",
125
+ cancel: "Cancelar",
114
126
  },
115
127
  manageViews: {
116
128
  title: "Gerenciar visões",
117
129
  empty: "Nenhuma visão salva.",
130
+ edit: "Editar",
118
131
  defaultTag: "Padrão",
119
132
  otherUserTag: "De outro usuário",
120
133
  deleteTitle: "Excluir a visão salva?",
@@ -148,6 +161,9 @@ export const PT_BR_DATA_VIEWS_COPY: DataViewsCopy = {
148
161
  visibleCount: (visible, total) => `${visible} de ${total} visíveis`,
149
162
  reset: "Padrão",
150
163
  showAll: "Mostrar todas",
164
+ dragHint: "Arraste para reordenar as colunas.",
165
+ moveUp: (columnLabel) => `Mover ${columnLabel} para cima`,
166
+ moveDown: (columnLabel) => `Mover ${columnLabel} para baixo`,
151
167
  },
152
168
  display: {
153
169
  sortTab: "Ordenar",
@@ -187,6 +203,9 @@ export const PT_BR_DATA_VIEWS_COPY: DataViewsCopy = {
187
203
  grid: {
188
204
  rowActions: "Ações",
189
205
  bulkActions: (selectedCount) => `Ações (${selectedCount}) ▾`,
206
+ emptyFilteredTitle: "Nenhum resultado para esses filtros",
207
+ emptyFilteredHint: "Ajuste ou remova os filtros.",
208
+ emptyFilteredAction: "Limpar filtros",
190
209
  },
191
210
  filters: {
192
211
  moreFilters: (count) => `Mais filtros: ${count} sem espaço na barra`,
@@ -217,6 +236,8 @@ export const PT_BR_DATA_VIEWS_COPY: DataViewsCopy = {
217
236
  selectAll: "Selecionar tudo",
218
237
  onThisPage: (count) => `${count} nesta página`,
219
238
  selectRow: "Selecionar",
239
+ expandRow: "Expandir detalhes",
240
+ collapseRow: "Recolher detalhes",
220
241
  },
221
242
  nav: {
222
243
  mainView: "Visão principal",
@@ -229,6 +250,8 @@ export const PT_BR_DATA_VIEWS_COPY: DataViewsCopy = {
229
250
  label: "Visão",
230
251
  update: "Atualizar visão",
231
252
  save: "Salvar visão",
253
+ saveAs: "Salvar como nova",
254
+ reset: "Redefinir",
232
255
  updateFailed: "Não foi possível atualizar a visão",
233
256
  saveFailed: "Não foi possível salvar a alteração da visão.",
234
257
  editView: "Renomear e editar",