@12-apps/ui 6.3.0 → 6.4.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.
- package/dist/{chunk-MXPXKORR.js → chunk-5TCPZ6GR.js} +8 -6
- package/dist/chunk-5TCPZ6GR.js.map +1 -0
- package/dist/{chunk-R5ZQZSEQ.js → chunk-NUCLEOHF.js} +12 -6
- package/dist/chunk-NUCLEOHF.js.map +1 -0
- package/dist/{chunk-TPQRIPCN.js → chunk-ZF5ERIC4.js} +7 -6
- package/dist/chunk-ZF5ERIC4.js.map +1 -0
- package/dist/data-display/CardKit.js +1 -1
- package/dist/data-display/DataViews.js +28 -22
- package/dist/data-display/DataViews.js.map +1 -1
- package/dist/feedback/ConfirmAction.js +1 -1
- package/dist/form/CategorySelect.js +1 -1
- package/dist/form/CreatableSelect.js +1 -1
- package/dist/form/CreatableSelect.js.map +1 -1
- package/dist/form/SearchPalette.js +3 -4
- package/dist/form/SearchPalette.js.map +1 -1
- package/dist/form/total-form.js +1 -1
- package/dist/layout/ContentToolbar.js +1 -1
- package/dist/pt-BR.js +23 -4
- package/dist/pt-BR.js.map +1 -1
- package/dist/types/components/data-display/DataViews/DataViewsBoard.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-category-pill.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-copy.d.ts +38 -1
- package/dist/types/components/data-display/DataViews/data-views-copy.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-empty.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-more-fields.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-more-filters.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-range-pill.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-toolbar.d.ts.map +1 -1
- package/dist/types/components/feedback/ConfirmAction/ConfirmAction.parts.d.ts.map +1 -1
- package/dist/types/components/form/CategorySelect/CategoryQuickActions.d.ts +4 -1
- package/dist/types/components/form/CategorySelect/CategoryQuickActions.d.ts.map +1 -1
- package/dist/types/components/form/CreatableSelect/CreatableSelect.d.ts.map +1 -1
- package/dist/types/components/form/CreatableSelect/CreatableSelect.types.d.ts +2 -1
- package/dist/types/components/form/CreatableSelect/CreatableSelect.types.d.ts.map +1 -1
- package/dist/types/components/form/SearchPalette/SearchPalette.d.ts.map +1 -1
- package/dist/types/components/form/SearchPalette/SearchPalette.types.d.ts +3 -3
- package/dist/types/components/form/SearchPalette/SearchPalette.types.d.ts.map +1 -1
- package/dist/types/components/layout/ContentToolbar/ContentToolbar.d.ts +1 -1
- package/dist/types/components/layout/ContentToolbar/ContentToolbar.d.ts.map +1 -1
- package/dist/types/components/layout/ContentToolbar/ContentToolbar.types.d.ts +12 -0
- package/dist/types/components/layout/ContentToolbar/ContentToolbar.types.d.ts.map +1 -1
- package/dist/types/components/layout/ContentToolbar/MultiSelectDropdown.d.ts +1 -1
- package/dist/types/components/layout/ContentToolbar/MultiSelectDropdown.d.ts.map +1 -1
- package/dist/types/components/layout/ContentToolbar/MultiSelectTriggers.d.ts +10 -2
- package/dist/types/components/layout/ContentToolbar/MultiSelectTriggers.d.ts.map +1 -1
- package/dist/types/copy.d.ts +22 -0
- package/dist/types/copy.d.ts.map +1 -1
- package/dist/types/pt-BR.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/data-display/DataViews/DataViewsBoard.tsx +2 -1
- package/src/components/data-display/DataViews/DeleteViewDialog.tsx +2 -2
- package/src/components/data-display/DataViews/ManageViewsDialog.tsx +1 -1
- package/src/components/data-display/DataViews/data-views-category-pill.tsx +1 -0
- package/src/components/data-display/DataViews/data-views-columns-tab.tsx +4 -3
- package/src/components/data-display/DataViews/data-views-copy.ts +38 -1
- package/src/components/data-display/DataViews/data-views-empty.tsx +6 -3
- package/src/components/data-display/DataViews/data-views-more-fields.tsx +3 -1
- package/src/components/data-display/DataViews/data-views-more-filters.tsx +2 -1
- package/src/components/data-display/DataViews/data-views-range-pill.tsx +2 -1
- package/src/components/data-display/DataViews/data-views-toolbar.tsx +3 -0
- package/src/components/data-display/DataViews/data-views-view-nav.tsx +2 -2
- package/src/components/feedback/ConfirmAction/ConfirmAction.parts.tsx +14 -3
- package/src/components/form/CategorySelect/CategoryPanel.tsx +1 -1
- package/src/components/form/CategorySelect/CategoryQuickActions.tsx +7 -2
- package/src/components/form/CategorySelect/CategorySelect.tsx +1 -1
- package/src/components/form/CreatableSelect/CreatableSelect.md +2 -2
- package/src/components/form/CreatableSelect/CreatableSelect.tsx +1 -1
- package/src/components/form/CreatableSelect/CreatableSelect.types.ts +2 -1
- package/src/components/form/SearchPalette/SearchPalette.tsx +3 -4
- package/src/components/form/SearchPalette/SearchPalette.types.ts +3 -3
- package/src/components/layout/ContentToolbar/ContentToolbar.tsx +13 -2
- package/src/components/layout/ContentToolbar/ContentToolbar.types.ts +12 -0
- package/src/components/layout/ContentToolbar/MultiSelectDropdown.tsx +2 -0
- package/src/components/layout/ContentToolbar/MultiSelectTriggers.tsx +15 -2
- package/src/copy.ts +22 -0
- package/src/pt-BR.ts +19 -0
- package/dist/chunk-MXPXKORR.js.map +0 -1
- package/dist/chunk-R5ZQZSEQ.js.map +0 -1
- package/dist/chunk-TPQRIPCN.js.map +0 -1
|
@@ -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 ??
|
|
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 ??
|
|
87
|
+
'aria-label': label ?? copy.typeToConfirm(expected),
|
|
85
88
|
'data-testid': `${dataTestId}-type-to-confirm`,
|
|
86
89
|
}}
|
|
87
90
|
/>
|
|
@@ -132,7 +135,14 @@ export function ConfirmActionDialog({
|
|
|
132
135
|
title={title}
|
|
133
136
|
description={confirmBody(description, copy, entityName)}
|
|
134
137
|
confirmText={confirmText}
|
|
135
|
-
|
|
138
|
+
// `copy.cancel` is the fallback, NOT `AlertDialog`'s own. The 6.3.0 copy
|
|
139
|
+
// sweep deleted this component's `cancelText = 'Cancelar'` default and
|
|
140
|
+
// put the word in `ConfirmActionCopy` — where nothing then read it, so
|
|
141
|
+
// every confirmation that names no `cancelText` of its own fell through
|
|
142
|
+
// to `AlertDialog`'s English `'Cancel'`, in a pt-BR dialog. That is the
|
|
143
|
+
// precise failure the sweep exists to prevent, one layer down: a port
|
|
144
|
+
// that is required, supplied, and ignored.
|
|
145
|
+
cancelText={cancelText ?? copy.cancel}
|
|
136
146
|
loading={state.pending}
|
|
137
147
|
confirmDisabled={gateUnmet}
|
|
138
148
|
initialFocus={resolveInitialFocus(tone, initialFocus)}
|
|
@@ -154,6 +164,7 @@ export function ConfirmActionDialog({
|
|
|
154
164
|
onChange={setTyped}
|
|
155
165
|
disabled={state.pending}
|
|
156
166
|
dataTestId={dataTestId}
|
|
167
|
+
copy={copy}
|
|
157
168
|
/>
|
|
158
169
|
)}
|
|
159
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}
|
|
@@ -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 ?
|
|
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 ?
|
|
70
|
+
{allExpanded ? copy.collapseAll : copy.expandAll}
|
|
66
71
|
</Box>
|
|
67
72
|
</>
|
|
68
73
|
);
|
|
@@ -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 ?
|
|
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
|
|
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.
|
|
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
|
|
|
@@ -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
|
-
|
|
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
|
|
60
|
-
noResultsLabel: props.noResultsLabel
|
|
61
|
-
placeholder: props.placeholder
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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':
|
|
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
|
-
|
|
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={
|
|
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,22 @@ 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
|
+
* What the closed control invites, and it differs by mode: picking ONE
|
|
65
|
+
* category is a move, picking several is a filter. Both were pt-BR defaults
|
|
66
|
+
* behind an optional prop, which is the arrangement this port removes.
|
|
67
|
+
*/
|
|
68
|
+
placeholderSingle: string;
|
|
69
|
+
placeholderMulti: string;
|
|
48
70
|
/** The empty state, when the host has no categories at all. */
|
|
49
71
|
emptyTitle: string;
|
|
50
72
|
createCategory: string;
|
package/src/pt-BR.ts
CHANGED
|
@@ -26,11 +26,18 @@ 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
|
+
placeholderSingle: "Mover para…",
|
|
40
|
+
placeholderMulti: "Categoria",
|
|
34
41
|
emptyTitle: "Nenhuma categoria ainda",
|
|
35
42
|
createCategory: "Criar categoria",
|
|
36
43
|
noResults: {
|
|
@@ -102,19 +109,23 @@ export const PT_BR_DATA_VIEWS_COPY: DataViewsCopy = {
|
|
|
102
109
|
countOnPage: (label, count) => `${label}: ${count} nesta página`,
|
|
103
110
|
onThisPage: (count) => `${count} nesta página`,
|
|
104
111
|
pageSum: (sum) => `Soma desta página: ${sum}`,
|
|
112
|
+
emptyColumn: "Nada nesta etapa",
|
|
105
113
|
pageScopeNote:
|
|
106
114
|
"As contagens e somas abaixo referem-se apenas aos itens desta página.",
|
|
107
115
|
},
|
|
108
116
|
deleteView: {
|
|
109
117
|
title: "Excluir visão",
|
|
118
|
+
target: (viewName) => `Excluir "${viewName}"?`,
|
|
110
119
|
sharedWarning: "Esta visão está compartilhada — a equipe perderá o acesso a ela.",
|
|
111
120
|
rowsUnaffected: (entityLabel) =>
|
|
112
121
|
`${entityLabel} não são afetados; apenas o recorte salvo é removido.`,
|
|
113
122
|
confirm: "Excluir visão",
|
|
123
|
+
cancel: "Cancelar",
|
|
114
124
|
},
|
|
115
125
|
manageViews: {
|
|
116
126
|
title: "Gerenciar visões",
|
|
117
127
|
empty: "Nenhuma visão salva.",
|
|
128
|
+
edit: "Editar",
|
|
118
129
|
defaultTag: "Padrão",
|
|
119
130
|
otherUserTag: "De outro usuário",
|
|
120
131
|
deleteTitle: "Excluir a visão salva?",
|
|
@@ -148,6 +159,9 @@ export const PT_BR_DATA_VIEWS_COPY: DataViewsCopy = {
|
|
|
148
159
|
visibleCount: (visible, total) => `${visible} de ${total} visíveis`,
|
|
149
160
|
reset: "Padrão",
|
|
150
161
|
showAll: "Mostrar todas",
|
|
162
|
+
dragHint: "Arraste para reordenar as colunas.",
|
|
163
|
+
moveUp: (columnLabel) => `Mover ${columnLabel} para cima`,
|
|
164
|
+
moveDown: (columnLabel) => `Mover ${columnLabel} para baixo`,
|
|
151
165
|
},
|
|
152
166
|
display: {
|
|
153
167
|
sortTab: "Ordenar",
|
|
@@ -187,6 +201,9 @@ export const PT_BR_DATA_VIEWS_COPY: DataViewsCopy = {
|
|
|
187
201
|
grid: {
|
|
188
202
|
rowActions: "Ações",
|
|
189
203
|
bulkActions: (selectedCount) => `Ações (${selectedCount}) ▾`,
|
|
204
|
+
emptyFilteredTitle: "Nenhum resultado para esses filtros",
|
|
205
|
+
emptyFilteredHint: "Ajuste ou remova os filtros.",
|
|
206
|
+
emptyFilteredAction: "Limpar filtros",
|
|
190
207
|
},
|
|
191
208
|
filters: {
|
|
192
209
|
moreFilters: (count) => `Mais filtros: ${count} sem espaço na barra`,
|
|
@@ -229,6 +246,8 @@ export const PT_BR_DATA_VIEWS_COPY: DataViewsCopy = {
|
|
|
229
246
|
label: "Visão",
|
|
230
247
|
update: "Atualizar visão",
|
|
231
248
|
save: "Salvar visão",
|
|
249
|
+
saveAs: "Salvar como nova",
|
|
250
|
+
reset: "Redefinir",
|
|
232
251
|
updateFailed: "Não foi possível atualizar a visão",
|
|
233
252
|
saveFailed: "Não foi possível salvar a alteração da visão.",
|
|
234
253
|
editView: "Renomear e editar",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/feedback/ConfirmAction/ConfirmAction.hooks.ts","../src/components/feedback/ConfirmAction/ConfirmAction.parts.tsx","../src/components/feedback/ConfirmAction/ConfirmAction.tsx","../src/components/feedback/ConfirmAction/with-confirmation.tsx","../src/components/feedback/ConfirmAction/ConfirmButton.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport type { ConfirmHandler, UseConfirmActionResult } from './ConfirmAction.types';\n\n/** Used when the guarded action rejects and the caller supplied no wording. */\n\n\n/**\n * What the popup says after a failed attempt.\n *\n * A rejection carrying a message wins: the server's sentence (\"esta categoria\n * ainda tem produtos vinculados\") is the one the operator can act on, and it is\n * strictly better than the generic fallback. Handlers that have nothing useful\n * to say reject with nothing and get `errorText`.\n */\nfunction failureMessage(cause: unknown, errorText: string): string {\n if (cause instanceof Error && cause.message) return cause.message;\n return errorText;\n}\n\n/**\n * The confirmation state machine, headless.\n *\n * It is the whole safety contract in one place, so every consumer gets the same\n * behaviour: the action runs ONLY from `confirm`, never from opening the popup;\n * a second confirm while the first is in flight is dropped (a double-click\n * cannot send two deletes); a rejection keeps the popup open carrying the\n * failure instead of closing over a write that never happened.\n */\nexport function useConfirmAction(\n onConfirm: ConfirmHandler,\n errorText: string,\n): UseConfirmActionResult {\n const [open, setOpen] = useState(false);\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // `pending` is mirrored in a ref because the two guards that matter — drop a\n // second confirm, ignore a cancel mid-flight — are read inside callbacks that\n // must not be rebuilt on every state change. Reading the state variable there\n // would leave a stale closure holding `false` for the whole in-flight window,\n // which is exactly the double-submit this component exists to prevent.\n const busy = useRef(false);\n\n // The action can settle after the trigger has unmounted (a row deleted from\n // under its own menu), so the settle path must not touch state by then.\n const mounted = useRef(true);\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n\n const request = useCallback((): void => {\n setError(null);\n setOpen(true);\n }, []);\n\n const cancel = useCallback((): void => {\n // Inert mid-flight: the write has already left, and closing here would hide\n // its outcome.\n if (busy.current) return;\n setOpen(false);\n setError(null);\n }, []);\n\n const settle = useCallback((failure: string | null): void => {\n busy.current = false;\n if (!mounted.current) return;\n setPending(false);\n setError(failure);\n if (!failure) setOpen(false);\n }, []);\n\n const confirm = useCallback((): void => {\n if (busy.current) return;\n busy.current = true;\n setPending(true);\n setError(null);\n // The handler is invoked synchronously, exactly as a plain `onClick` would\n // be — only the OUTCOME is awaited. The try/catch is what puts a handler\n // that throws synchronously on the same failure path as one that returns a\n // rejected promise.\n try {\n const result = onConfirm();\n void Promise.resolve(result)\n .then(() => settle(null))\n .catch((cause: unknown) => settle(failureMessage(cause, errorText)));\n } catch (cause) {\n settle(failureMessage(cause, errorText));\n }\n }, [errorText, onConfirm, settle]);\n\n return { open, pending, error, request, cancel, confirm };\n}\n","import type { ConfirmActionCopy } from \"../../../copy\";\nimport { Alert, Stack, TextField, Typography } from '@mui/material';\nimport React, { type ReactNode } from 'react';\n\nimport { AlertDialog } from '../../data-display/AlertDialog';\n\nimport type {\n ConfirmInitialFocus,\n ConfirmOptions,\n ConfirmTone,\n UseConfirmActionResult,\n} from './ConfirmAction.types';\n\nconst DEFAULT_TEST_ID = 'confirm-action';\n\n/**\n * The popup's body: the row being acted on, then what happens to it.\n *\n * The NAME is rendered in its own right rather than woven into the sentence, so\n * it is there whatever the caller wrote — \"the popup names the specific entity\"\n * is the one thing that must not depend on each call site remembering to\n * mention it. A body with neither a name nor a description would leave the\n * operator with only the title to decide on, so a neutral fallback covers that.\n */\nfunction confirmBody(\n description: ReactNode,\n copy: ConfirmActionCopy,\n entityName?: string,\n): ReactNode {\n if (!entityName) return description ?? copy.irreversible;\n return (\n <>\n <Typography component=\"span\" sx={{ fontWeight: 600, display: \"block\" }}>\n {entityName}\n </Typography>\n {description}\n </>\n );\n}\n\n/** Destructive dialogs open on Cancel; anything else opens on the confirm. */\nfunction resolveInitialFocus(\n tone: ConfirmTone,\n initialFocus?: ConfirmInitialFocus,\n): ConfirmInitialFocus {\n if (initialFocus) return initialFocus;\n return tone === 'destructive' ? 'cancel' : 'confirm';\n}\n\n/**\n * The type-to-confirm field. Rendered only when `typeToConfirm` is set, which\n * should be rare: it is the guard for actions with NO recovery path, where a\n * click is too cheap a gesture. Comparison is exact — trimmed of surrounding\n * whitespace only, so a trailing space from a paste does not read as a mismatch.\n */\nfunction TypeToConfirmField({\n expected,\n label,\n value,\n onChange,\n disabled,\n dataTestId,\n}: {\n expected: string;\n label?: string;\n value: string;\n onChange: (next: string) => void;\n disabled: boolean;\n dataTestId: string;\n}): React.ReactElement {\n return (\n <Stack spacing={1} sx={{ mt: 2 }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {label ?? `Digite \"${expected}\" para confirmar.`}\n </Typography>\n <TextField\n size=\"small\"\n value={value}\n disabled={disabled}\n onChange={(event) => onChange(event.target.value)}\n // On the INPUT rather than the wrapper: a test id on MUI's TextField\n // lands on the outer div, which is not the node a test can type into.\n inputProps={{\n 'aria-label': label ?? `Digite ${expected} para confirmar`,\n 'data-testid': `${dataTestId}-type-to-confirm`,\n }}\n />\n </Stack>\n );\n}\n\nexport interface ConfirmActionDialogProps extends ConfirmOptions {\n /** The fallback body when no entity is named. REQUIRED — no default copy. */\n copy: ConfirmActionCopy;\n state: UseConfirmActionResult;\n}\n\n/**\n * The popup itself: an `AlertDialog` in its destructive skin, plus the two\n * things a bare AlertDialog has no opinion about — the failure the last attempt\n * produced (shown IN the dialog, which stays open, rather than closing over a\n * write that never landed) and the optional type-to-confirm gate.\n */\nexport function ConfirmActionDialog({\n state,\n title,\n description,\n entityName,\n confirmText,\n cancelText,\n typeToConfirm,\n typeToConfirmLabel,\n tone = 'destructive',\n initialFocus,\n dataTestId = DEFAULT_TEST_ID,\n copy,\n}: ConfirmActionDialogProps): React.ReactElement {\n const [typed, setTyped] = React.useState('');\n\n // Every fresh open starts from an empty field: carrying the previous answer\n // over would hand the next delete a pre-satisfied gate.\n React.useEffect(() => {\n if (state.open) setTyped('');\n }, [state.open]);\n\n const gateUnmet = typeToConfirm !== undefined && typed.trim() !== typeToConfirm;\n\n return (\n <AlertDialog\n open={state.open}\n variant={tone === 'destructive' ? 'destructive' : 'default'}\n title={title}\n description={confirmBody(description, copy, entityName)}\n confirmText={confirmText}\n cancelText={cancelText}\n loading={state.pending}\n confirmDisabled={gateUnmet}\n initialFocus={resolveInitialFocus(tone, initialFocus)}\n onConfirm={state.confirm}\n onCancel={state.cancel}\n onClose={state.cancel}\n data-testid={dataTestId}\n >\n {state.error && (\n <Alert severity=\"error\" sx={{ mt: 1 }} data-testid={`${dataTestId}-error`}>\n {state.error}\n </Alert>\n )}\n {typeToConfirm !== undefined && (\n <TypeToConfirmField\n expected={typeToConfirm}\n label={typeToConfirmLabel}\n value={typed}\n onChange={setTyped}\n disabled={state.pending}\n dataTestId={dataTestId}\n />\n )}\n </AlertDialog>\n );\n}\n","import React from 'react';\n\nimport { useConfirmAction } from './ConfirmAction.hooks';\nimport { ConfirmActionDialog } from './ConfirmAction.parts';\nimport type { ConfirmActionProps } from './ConfirmAction.types';\n\n/**\n * Wraps any trigger in a confirmation popup.\n *\n * The trigger is a render prop rather than a fixed element, so the same guard\n * covers a button, a dropdown item, an icon or a table row action without this\n * component knowing what any of them look like. The guarded handler runs ONLY\n * after the operator confirms:\n *\n * ```tsx\n * <ConfirmAction\n * title=\"Excluir a categoria?\"\n * entityName={row.name}\n * description=\"Ela vai para a lixeira e pode ser restaurada de lá.\"\n * confirmText=\"Excluir\"\n * onConfirm={() => deleteCategoryAction({ tenantSlug, id: row.id })}\n * >\n * {(request) => <Button color=\"danger\" onClick={request}>Excluir</Button>}\n * </ConfirmAction>\n * ```\n *\n * For the common case — an existing button whose `onClick` is already the\n * destructive write — reach for {@link withConfirmation} instead, which needs\n * no restructuring of the call site.\n */\nexport function ConfirmAction({\n onConfirm,\n children,\n ...options\n}: ConfirmActionProps): React.ReactElement {\n const state = useConfirmAction(onConfirm, options.errorText);\n\n return (\n <>\n {children(state.request, { pending: state.pending })}\n <ConfirmActionDialog state={state} {...options} />\n </>\n );\n}\n\nConfirmAction.displayName = 'ConfirmAction';\n","import type { ConfirmActionCopy } from '../../../copy';\nimport React from 'react';\nimport type { ComponentType } from 'react';\n\nimport { useConfirmAction } from './ConfirmAction.hooks';\nimport { ConfirmActionDialog } from './ConfirmAction.parts';\nimport type {\n ConfirmHandler,\n ConfirmOptions,\n ConfirmableComponent,\n ConfirmableProps,\n WithConfirmationProps,\n} from './ConfirmAction.types';\n\n/**\n * Merge the HOC's baked-in defaults with the per-instance `confirm` prop.\n * Returns null when the instance opts out (`confirm={false}`) or when neither\n * side supplies the two fields a confirmation cannot be written without — the\n * question and the verb. A null result means \"render the plain component\",\n * which keeps the wrapped export usable in harmless contexts.\n */\nfunction resolveOptions(\n defaults: Partial<ConfirmOptions> | undefined,\n instance: Partial<ConfirmOptions> | false | undefined,\n): ConfirmOptions | null {\n if (instance === false) return null;\n const merged = { ...defaults, ...instance };\n if (!merged.title || !merged.confirmText) return null;\n return { ...merged, title: merged.title, confirmText: merged.confirmText };\n}\n\n/**\n * Adapt the wrapped component's `onClick` into a {@link ConfirmHandler}.\n *\n * The click argument is dropped (see `ConfirmHandler`) and the return value is\n * narrowed: a promise is handed back so the popup can hold its pending state\n * until the write settles, anything else becomes `undefined`. A component with\n * no `onClick` has nothing to guard, so confirming is a no-op.\n */\nfunction useGuardedHandler(onClick: ConfirmableProps['onClick']): ConfirmHandler {\n return React.useCallback((): void | Promise<unknown> => {\n const result = onClick?.();\n return result instanceof Promise ? result : undefined;\n }, [onClick]);\n}\n\n/**\n * Give any click-driven component a confirmation popup, without restructuring\n * the call site.\n *\n * The wrapped component's own `onClick` becomes the GUARDED action: clicking\n * now opens the popup, and the handler runs only if the operator confirms. It\n * inherits the whole contract from `useConfirmAction` — cancel sends nothing, a\n * returned promise holds the popup in its pending state so a double-click\n * cannot fire two writes, and a rejection keeps the popup open carrying the\n * error.\n *\n * ```tsx\n * const ConfirmButton = withConfirmation(Button);\n *\n * <ConfirmButton\n * color=\"danger\"\n * onClick={() => deleteCategoryAction({ tenantSlug, id: row.id })}\n * confirm={{\n * title: \"Excluir a categoria?\",\n * entityName: row.name,\n * description: \"Ela vai para a lixeira e pode ser restaurada de lá.\",\n * confirmText: \"Excluir\",\n * }}\n * >\n * Excluir\n * </ConfirmButton>\n * ```\n *\n * Bake the wording in once when the same guard repeats — `withConfirmation(Button,\n * { confirmText: \"Excluir\" })` — and let each instance override what differs.\n * `confirm={false}` falls through to the plain component.\n *\n * Refs are not forwarded: the HOC renders a fragment (trigger + popup), so there\n * is no single node to hand back. Wrap a component that needs a ref at its own\n * call site with {@link ConfirmAction} instead.\n */\nexport function withConfirmation<P extends ConfirmableProps>(\n Wrapped: ComponentType<P>,\n defaults: Partial<ConfirmOptions> & { errorText: string; copy: ConfirmActionCopy },\n): ConfirmableComponent<P> {\n function WithConfirmation(props: P & WithConfirmationProps): React.ReactElement {\n const { confirm, onClick } = props;\n const options = resolveOptions(defaults, confirm);\n const state = useConfirmAction(useGuardedHandler(onClick), defaults.errorText);\n\n // `confirm` is ours and must not reach the DOM — an unknown attribute on a\n // <button> is a React warning at every render. The cast is the standard HOC\n // one: TypeScript cannot prove `Omit<P & X, 'confirm'>` is still a `P`.\n const { confirm: _confirm, ...rest } = props;\n const forwarded = rest as unknown as P;\n\n if (!options) return <Wrapped {...forwarded} />;\n\n return (\n <>\n <Wrapped {...forwarded} onClick={state.request} />\n <ConfirmActionDialog state={state} {...options} copy={defaults.copy} />\n </>\n );\n }\n\n WithConfirmation.displayName = `withConfirmation(${\n Wrapped.displayName ?? Wrapped.name ?? 'Component'\n })`;\n\n return WithConfirmation;\n}\n","import type { ConfirmActionCopy } from '../../../copy';\nimport { Button } from '../../form/Button';\nimport type { ButtonProps } from '../../form/Button/Button.types';\n\nimport { withConfirmation } from './with-confirmation';\nimport type { ConfirmableComponent } from './ConfirmAction.types';\n\n/**\n * The library `Button`, already wrapped — the shape most call sites want.\n *\n * Adding the guard to an existing button is then an import swap plus the\n * `confirm` prop; the button's own props (colour, size, icon) are untouched.\n * The baked-in defaults are only the wording and tone every destructive\n * confirmation shares, and each instance can still override them.\n */\nexport function createConfirmButton(\n copy: ConfirmActionCopy,\n errorText: string,\n): ConfirmableComponent<ButtonProps> {\n return withConfirmation<ButtonProps>(Button, {\n cancelText: copy.cancel,\n tone: 'destructive',\n errorText,\n copy,\n });\n}\n"],"mappings":"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","names":["Fragment","jsx","jsxs","React","Fragment","jsx","jsxs","React"]}
|