Skip to content

Commit 9ff4091

Browse files
authored
feat(FR-2425): add clear-all-selection button to table row selection indicator (#6321)
1 parent 9cd3513 commit 9ff4091

57 files changed

Lines changed: 246 additions & 59 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
'use memo';
2+
3+
import BAISelectionLabel from './BAISelectionLabel';
4+
import type { Meta, StoryObj } from '@storybook/react-vite';
5+
import { action } from 'storybook/actions';
6+
7+
const meta: Meta<typeof BAISelectionLabel> = {
8+
title: 'DataDisplay/BAISelectionLabel',
9+
component: BAISelectionLabel,
10+
tags: ['autodocs'],
11+
parameters: {
12+
layout: 'padded',
13+
docs: {
14+
description: {
15+
component: `
16+
**BAISelectionLabel** displays the number of selected items with an optional clear-all button.
17+
18+
## Props
19+
| Prop | Type | Default | Description |
20+
|------|------|---------|-------------|
21+
| \`count\` | \`number\` | — | Number of selected items. Renders nothing when \`count <= 0\`. |
22+
| \`onClearSelection\` | \`() => void\` | — | Callback when the clear icon is clicked. Icon is hidden when omitted. |
23+
`,
24+
},
25+
},
26+
},
27+
argTypes: {
28+
count: {
29+
control: { type: 'number', min: 0, max: 100 },
30+
description: 'Number of selected items. Renders nothing when 0.',
31+
table: {
32+
type: { summary: 'number' },
33+
},
34+
},
35+
onClearSelection: {
36+
description:
37+
'Callback fired when the clear icon is clicked. Icon is hidden when omitted.',
38+
table: {
39+
type: { summary: '() => void' },
40+
},
41+
},
42+
},
43+
};
44+
45+
export default meta;
46+
type Story = StoryObj<typeof BAISelectionLabel>;
47+
48+
export const Default: Story = {
49+
name: 'Basic',
50+
args: {
51+
count: 3,
52+
onClearSelection: action('onClearSelection'),
53+
},
54+
};
55+
56+
export const WithoutClearButton: Story = {
57+
parameters: {
58+
docs: {
59+
description: {
60+
story:
61+
'When `onClearSelection` is not provided, the clear icon is hidden.',
62+
},
63+
},
64+
},
65+
args: {
66+
count: 5,
67+
},
68+
};
69+
70+
export const Comparison: Story = {
71+
parameters: {
72+
docs: {
73+
description: {
74+
story:
75+
'Side-by-side comparison: with and without the clear button, and different counts.',
76+
},
77+
},
78+
},
79+
render: () => (
80+
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
81+
<BAISelectionLabel count={1} onClearSelection={action('clear-1')} />
82+
<BAISelectionLabel count={10} onClearSelection={action('clear-10')} />
83+
<BAISelectionLabel count={99} onClearSelection={action('clear-99')} />
84+
<BAISelectionLabel count={5} />
85+
<BAISelectionLabel count={0} onClearSelection={action('clear-0')} />
86+
</div>
87+
),
88+
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { theme, Tooltip, Typography } from 'antd';
2+
import { CircleXIcon } from 'lucide-react';
3+
import React from 'react';
4+
import { useTranslation } from 'react-i18next';
5+
6+
export interface BAISelectionLabelProps {
7+
count: number;
8+
onClearSelection?: () => void;
9+
}
10+
11+
const BAISelectionLabel: React.FC<BAISelectionLabelProps> = ({
12+
count,
13+
onClearSelection,
14+
}) => {
15+
'use memo';
16+
17+
const { t } = useTranslation();
18+
const { token } = theme.useToken();
19+
20+
if (count <= 0) return null;
21+
22+
return (
23+
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 4 }}>
24+
<Typography.Text>{t('general.NSelected', { count })}</Typography.Text>
25+
{onClearSelection && (
26+
<Tooltip title={t('general.DeselectAll')}>
27+
<CircleXIcon
28+
size={16}
29+
tabIndex={0}
30+
role="button"
31+
aria-label={t('general.DeselectAll')}
32+
style={{
33+
cursor: 'pointer',
34+
color: token.colorTextSecondary,
35+
flexShrink: 0,
36+
}}
37+
onClick={onClearSelection}
38+
/>
39+
</Tooltip>
40+
)}
41+
</span>
42+
);
43+
};
44+
45+
export default BAISelectionLabel;

packages/backend.ai-ui/src/components/baiClient/FileExplorer/BAIFileExplorer.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ const BAIFileExplorer: React.FC<BAIFileExplorerProps> = ({
317317
enableWrite={enableWrite}
318318
onUpload={(files, currentPath) => onUpload(files, currentPath)}
319319
onDeleteFilesInBackground={onDeleteFilesInBackground}
320+
onClearSelection={() => setSelectedItems([])}
320321
onRequestClose={(
321322
success: boolean,
322323
modifiedItems?: Array<VFolderFile>,

packages/backend.ai-ui/src/components/baiClient/FileExplorer/ExplorerActionControls.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { BAITrashBinIcon } from '../../../icons';
22
import BAIFlex from '../../BAIFlex';
3+
import BAISelectionLabel from '../../BAISelectionLabel';
34
import { VFolderFile } from '../../provider/BAIClientProvider/types';
45
import CreateDirectoryModal from './CreateDirectoryModal';
56
import CreateFileModal from './CreateFileModal';
@@ -40,6 +41,7 @@ interface ExplorerActionControlsProps {
4041
) => void;
4142
onUpload: (files: Array<RcFile>, currentPath: string) => void;
4243
onDeleteFilesInBackground: DeleteSelectedItemsModalProps['onDeleteFilesInBackground'];
44+
onClearSelection?: () => void;
4345
enableDelete?: boolean;
4446
enableWrite?: boolean;
4547
// onClickRefresh?: (key: string) => void;
@@ -51,6 +53,7 @@ const ExplorerActionControls: React.FC<ExplorerActionControlsProps> = ({
5153
onRequestClose,
5254
onUpload,
5355
onDeleteFilesInBackground,
56+
onClearSelection,
5457
enableDelete = false,
5558
enableWrite = false,
5659
extra,
@@ -73,9 +76,10 @@ const ExplorerActionControls: React.FC<ExplorerActionControlsProps> = ({
7376
<BAIFlex gap={'sm'}>
7477
{selectedFiles.length > 0 && (
7578
<>
76-
{t('general.NSelected', {
77-
count: selectedFiles.length,
78-
})}
79+
<BAISelectionLabel
80+
count={selectedFiles.length}
81+
onClearSelection={onClearSelection}
82+
/>
7983
<Tooltip title={t('general.button.Delete')} placement="topLeft">
8084
<Button
8185
disabled={!enableDelete}

packages/backend.ai-ui/src/components/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ export type {
6161
} from './BAIDeleteConfirmModal';
6262
export { default as BAIButton } from './BAIButton';
6363
export type { BAIButtonProps } from './BAIButton';
64+
export { default as BAISelectionLabel } from './BAISelectionLabel';
65+
export type { BAISelectionLabelProps } from './BAISelectionLabel';
6466
export { default as BAIFetchKeyButton } from './BAIFetchKeyButton';
6567
export {
6668
default as BAIResourceNumberWithIcon,

packages/backend.ai-ui/src/locale/de.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@
359359
"UnknownError": "Ein unbekannter Fehler ist aufgetreten. \nBitte versuchen Sie es erneut."
360360
},
361361
"general": {
362+
"DeselectAll": "Alle abwählen",
362363
"NSelected": "{{count}} ausgewählt",
363364
"Optional": "Optional",
364365
"TotalItems": "Total {{total}} Elemente",

packages/backend.ai-ui/src/locale/el.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@
359359
"UnknownError": "Παρουσιάστηκε ένα άγνωστο σφάλμα. \nΔοκιμάστε ξανά."
360360
},
361361
"general": {
362+
"DeselectAll": "Αποεπιλογή όλων",
362363
"NSelected": "{{count}} Επιλεγμένη",
363364
"Optional": "Προαιρετικό",
364365
"TotalItems": "Σύνολο {{total}} στοιχεία",

packages/backend.ai-ui/src/locale/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@
365365
"UnknownError": "An unknown error occurred. Please try again."
366366
},
367367
"general": {
368+
"DeselectAll": "Deselect all",
368369
"NSelected": "{{count}} selected",
369370
"Optional": "Optional",
370371
"TotalItems": "Total {{total}} items",

packages/backend.ai-ui/src/locale/es.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@
359359
"UnknownError": "Ocurrió un error desconocido. \nPor favor intente de nuevo."
360360
},
361361
"general": {
362+
"DeselectAll": "Deseleccionar todo",
362363
"NSelected": "{{count}} seleccionado",
363364
"Optional": "Opcional",
364365
"TotalItems": "Total {{total}} elementos",

packages/backend.ai-ui/src/locale/fi.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@
359359
"UnknownError": "Tapahtui tuntematon virhe. \nYritä uudelleen."
360360
},
361361
"general": {
362+
"DeselectAll": "Poista kaikki valinnat",
362363
"NSelected": "{{count}} valittu",
363364
"Optional": "Valinnainen",
364365
"TotalItems": "Yhteensä {{total}} kohteet",

0 commit comments

Comments
 (0)