Skip to content

Commit 35b01ba

Browse files
committed
feat(FR-2915): add showOpen/showCreate/showRefresh buttons to BAIVFolderSelect
1 parent 806865e commit 35b01ba

24 files changed

Lines changed: 226 additions & 2 deletions

packages/backend.ai-ui/src/components/fragments/BAIVFolderSelect.stories.tsx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ const meta: Meta<typeof BAIVFolderSelect> = {
111111
| \`filter\` | \`string\` | - | Additional filter string for vfolder query |
112112
| \`valuePropName\` | \`'id' \\| 'row_id'\` | \`'id'\` | Which field to use as option value |
113113
| \`excludeDeleted\` | \`boolean\` | \`false\` | Exclude deleted or deleting vfolders |
114+
| \`showOpenButton\` | \`boolean\` | \`false\` | Show button that opens the selected folder in the explorer |
115+
| \`showCreateButton\` | \`boolean\` | \`false\` | Show button that opens \`FolderCreateModalV2\` to create a new folder |
116+
| \`showRefreshButton\` | \`boolean\` | \`false\` | Show button that re-fetches the vfolder list |
114117
| \`ref\` | \`React.Ref<BAIVFolderSelectRef>\` | - | Ref exposing \`refetch()\` method |
115118
116119
## Features
@@ -254,6 +257,31 @@ For all other props, refer to [BAISelect](/?path=/docs/components-input-baiselec
254257
defaultValue: { summary: 'false' },
255258
},
256259
},
260+
showOpenButton: {
261+
control: { type: 'boolean' },
262+
description: 'Show button that opens the selected folder in the explorer',
263+
table: {
264+
type: { summary: 'boolean' },
265+
defaultValue: { summary: 'false' },
266+
},
267+
},
268+
showCreateButton: {
269+
control: { type: 'boolean' },
270+
description:
271+
'Show button that opens FolderCreateModalV2 to create a new folder',
272+
table: {
273+
type: { summary: 'boolean' },
274+
defaultValue: { summary: 'false' },
275+
},
276+
},
277+
showRefreshButton: {
278+
control: { type: 'boolean' },
279+
description: 'Show button that re-fetches the vfolder list',
280+
table: {
281+
type: { summary: 'boolean' },
282+
defaultValue: { summary: 'false' },
283+
},
284+
},
257285
placeholder: {
258286
control: { type: 'text' },
259287
description: 'Placeholder text when no value is selected',
@@ -418,6 +446,41 @@ export const WithClickableNames: Story = {
418446
),
419447
};
420448

449+
/**
450+
* Select with the open, create, and refresh action buttons enabled.
451+
*/
452+
export const WithActionButtons: Story = {
453+
name: 'WithActionButtons',
454+
parameters: {
455+
docs: {
456+
description: {
457+
story:
458+
'Demonstrates `showOpenButton`, `showCreateButton`, and `showRefreshButton`. The open button writes a `folder` query param via `react-router-dom`. The create button opens the project `FolderCreateModalV2`. The refresh button re-fetches the paginated list.',
459+
},
460+
},
461+
},
462+
args: {
463+
allowClear: true,
464+
showOpenButton: true,
465+
showCreateButton: true,
466+
showRefreshButton: true,
467+
},
468+
render: (args) => (
469+
<VFolderRelayResolver
470+
mockResolvers={{
471+
Query: () => ({
472+
vfolder_nodes: {
473+
count: 5,
474+
edges: sampleVFolders,
475+
},
476+
}),
477+
}}
478+
>
479+
<BAIVFolderSelect {...args} style={{ width: '400px' }} />
480+
</VFolderRelayResolver>
481+
),
482+
};
483+
421484
/**
422485
* Select using row_id instead of global ID.
423486
*/

packages/backend.ai-ui/src/components/fragments/BAIVFolderSelect.tsx

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1+
import FolderCreateModalV2 from '../../../../../react/src/components/FolderCreateModalV2';
12
import { BAIVFolderSelectPaginatedQuery } from '../../__generated__/BAIVFolderSelectPaginatedQuery.graphql';
23
import { BAIVFolderSelectValueQuery } from '../../__generated__/BAIVFolderSelectValueQuery.graphql';
34
import { toLocalId } from '../../helper';
45
import useDebouncedDeferredValue from '../../helper/useDebouncedDeferredValue';
56
import { useFetchKey } from '../../hooks';
67
import { useLazyPaginatedQuery } from '../../hooks/usePaginatedQuery';
8+
import BAIFlex from '../BAIFlex';
79
import BAILink from '../BAILink';
810
import { mergeFilterValues } from '../BAIPropertyFilter';
911
import BAISelect, { BAISelectProps } from '../BAISelect';
1012
import BAIText from '../BAIText';
1113
import TotalFooter from '../TotalFooter';
14+
import { ReloadOutlined } from '@ant-design/icons';
1215
import { useControllableValue } from 'ahooks';
13-
import { GetRef, Skeleton } from 'antd';
16+
import { Button, GetRef, Skeleton, Space, Tooltip } from 'antd';
1417
import * as _ from 'lodash-es';
18+
import { FolderOpenIcon, PlusIcon } from 'lucide-react';
1519
import {
1620
useDeferredValue,
1721
useEffect,
@@ -23,6 +27,7 @@ import {
2327
} from 'react';
2428
import { useTranslation } from 'react-i18next';
2529
import { graphql, useLazyLoadQuery } from 'react-relay';
30+
import { useSearchParams } from 'react-router-dom';
2631

2732
export type VFolderNode = NonNullable<
2833
NonNullable<
@@ -44,6 +49,9 @@ export interface BAIVFolderSelectProps extends Omit<
4449
valuePropName?: 'id' | 'row_id';
4550
excludeDeleted?: boolean;
4651
onResolvedNamesChange?: (nameMap: Record<string, string>) => void;
52+
showOpenButton?: boolean;
53+
showCreateButton?: boolean;
54+
showRefreshButton?: boolean;
4755
ref?: React.Ref<BAIVFolderSelectRef>;
4856
}
4957

@@ -59,13 +67,18 @@ const BAIVFolderSelect: React.FC<BAIVFolderSelectProps> = ({
5967
excludeDeleted,
6068
valuePropName = 'id',
6169
onResolvedNamesChange,
70+
showOpenButton,
71+
showCreateButton,
72+
showRefreshButton,
6273
ref,
6374
labelRender: userLabelRender,
6475
...selectProps
6576
}) => {
6677
'use memo';
6778
const { t } = useTranslation();
6879
const selectRef = useRef<GetRef<typeof BAISelect>>(null);
80+
const [, setSearchParams] = useSearchParams();
81+
const [isOpenCreateModal, setIsOpenCreateModal] = useState(false);
6982
const [controllableValue, setControllableValue] = useControllableValue<
7083
string | string[] | undefined
7184
>(selectProps);
@@ -261,7 +274,13 @@ const BAIVFolderSelect: React.FC<BAIVFolderSelectProps> = ({
261274
controllableValueWithLabel,
262275
);
263276

264-
return (
277+
const hasActionButton =
278+
showOpenButton || showCreateButton || showRefreshButton;
279+
const singleValueForOpen = _.toString(
280+
_.isArray(controllableValue) ? controllableValue[0] : controllableValue,
281+
);
282+
283+
const baiSelectElement = (
265284
<BAISelect
266285
ref={selectRef}
267286
placeholder={t('comp:BAIVFolderSelect.SelectFolder')}
@@ -392,6 +411,79 @@ const BAIVFolderSelect: React.FC<BAIVFolderSelectProps> = ({
392411
}
393412
/>
394413
);
414+
415+
if (!hasActionButton) {
416+
return baiSelectElement;
417+
}
418+
419+
return (
420+
<BAIFlex direction="row" gap="xs">
421+
{baiSelectElement}
422+
<Space.Compact>
423+
{showOpenButton ? (
424+
<Tooltip title={t('comp:BAIVFolderSelect.OpenFolder')}>
425+
<Button
426+
icon={<FolderOpenIcon />}
427+
disabled={!singleValueForOpen}
428+
onClick={() => {
429+
const folderId =
430+
valuePropName === 'id'
431+
? toLocalId(singleValueForOpen)
432+
: singleValueForOpen;
433+
if (folderId) {
434+
setSearchParams(
435+
(prev) => {
436+
prev.set('folder', folderId);
437+
return prev;
438+
},
439+
{ replace: false },
440+
);
441+
}
442+
}}
443+
/>
444+
</Tooltip>
445+
) : null}
446+
{showCreateButton ? (
447+
<Tooltip title={t('comp:BAIVFolderSelect.CreateANewStorageFolder')}>
448+
<Button
449+
icon={<PlusIcon />}
450+
variant="text"
451+
onClick={() => {
452+
setIsOpenCreateModal(true);
453+
}}
454+
/>
455+
</Tooltip>
456+
) : null}
457+
{showRefreshButton ? (
458+
<Tooltip title={t('comp:BAIVFolderSelect.Refresh')}>
459+
<Button
460+
icon={<ReloadOutlined />}
461+
variant="text"
462+
onClick={() => {
463+
startRefetchTransition(() => {
464+
updateFetchKey();
465+
});
466+
}}
467+
/>
468+
</Tooltip>
469+
) : null}
470+
</Space.Compact>
471+
{showCreateButton ? (
472+
<FolderCreateModalV2
473+
open={isOpenCreateModal}
474+
initialValues={{ usage_mode: 'model' }}
475+
onRequestClose={(result) => {
476+
setIsOpenCreateModal(false);
477+
if (result) {
478+
startRefetchTransition(() => {
479+
updateFetchKey();
480+
});
481+
}
482+
}}
483+
/>
484+
) : null}
485+
</BAIFlex>
486+
);
395487
};
396488

397489
export default BAIVFolderSelect;

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@
310310
"SelectUser": "Benutzer auswählen"
311311
},
312312
"comp:BAIVFolderSelect": {
313+
"CreateANewStorageFolder": "Erstellen Sie einen neuen Speicherordner",
314+
"OpenFolder": "Zum Ordner gehen",
315+
"Refresh": "Aktualisierung",
313316
"SelectFolder": "Ordner auswählen"
314317
},
315318
"comp:FileExplorer": {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@
310310
"SelectUser": "Επιλογή χρήστη"
311311
},
312312
"comp:BAIVFolderSelect": {
313+
"CreateANewStorageFolder": "Δημιουργήστε έναν νέο φάκελο αποθήκευσης",
314+
"OpenFolder": "Μετάβαση στον φάκελο",
315+
"Refresh": "Φρεσκάρω",
313316
"SelectFolder": "Επιλογή φακέλου"
314317
},
315318
"comp:FileExplorer": {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,9 @@
316316
"SelectUser": "Select User"
317317
},
318318
"comp:BAIVFolderSelect": {
319+
"CreateANewStorageFolder": "Create a new storage folder",
320+
"OpenFolder": "Open Folder",
321+
"Refresh": "Refresh",
319322
"SelectFolder": "Select Folder"
320323
},
321324
"comp:FileExplorer": {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@
310310
"SelectUser": "Seleccionar usuario"
311311
},
312312
"comp:BAIVFolderSelect": {
313+
"CreateANewStorageFolder": "Crear una nueva carpeta de almacenamiento",
314+
"OpenFolder": "Ir a la carpeta",
315+
"Refresh": "Actualizar",
313316
"SelectFolder": "Seleccionar carpeta"
314317
},
315318
"comp:FileExplorer": {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@
310310
"SelectUser": "Valitse käyttäjä"
311311
},
312312
"comp:BAIVFolderSelect": {
313+
"CreateANewStorageFolder": "Luo uusi tallennuskansio",
314+
"OpenFolder": "Siirry kansioon",
315+
"Refresh": "Päivitä",
313316
"SelectFolder": "Valitse kansio"
314317
},
315318
"comp:FileExplorer": {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@
310310
"SelectUser": "Sélectionner un utilisateur"
311311
},
312312
"comp:BAIVFolderSelect": {
313+
"CreateANewStorageFolder": "Créer un nouveau dossier de stockage",
314+
"OpenFolder": "Aller au dossier",
315+
"Refresh": "Rafraîchir",
313316
"SelectFolder": "Sélectionner un dossier"
314317
},
315318
"comp:FileExplorer": {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@
310310
"SelectUser": "Pilih Pengguna"
311311
},
312312
"comp:BAIVFolderSelect": {
313+
"CreateANewStorageFolder": "Buat folder penyimpanan baru",
314+
"OpenFolder": "Buka folder",
315+
"Refresh": "Segarkan",
313316
"SelectFolder": "Pilih Folder"
314317
},
315318
"comp:FileExplorer": {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@
310310
"SelectUser": "Seleziona utente"
311311
},
312312
"comp:BAIVFolderSelect": {
313+
"CreateANewStorageFolder": "Crea una nuova cartella di archiviazione",
314+
"OpenFolder": "Vai alla cartella",
315+
"Refresh": "ricaricare",
313316
"SelectFolder": "Seleziona cartella"
314317
},
315318
"comp:FileExplorer": {

0 commit comments

Comments
 (0)