This is an automated email from the ASF dual-hosted git repository. mintsweet pushed a commit to branch feat-miller-columns in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
commit 569953f6059b76d9292c3f824b1851a2e2bd847c Author: mintsweet <[email protected]> AuthorDate: Fri Sep 27 18:32:52 2024 +1200 feat: simplify the miller columns --- config-ui/package.json | 4 +- .../components/data-scope-remote/search-local.tsx | 129 +++++++------------- .../components/data-scope-remote/search-remote.tsx | 135 ++++++++++----------- .../plugins/components/data-scope-select/index.tsx | 95 ++++++++++----- .../webhook/components/selector-dialog.tsx | 19 +-- config-ui/yarn.lock | 20 +-- 6 files changed, 189 insertions(+), 213 deletions(-) diff --git a/config-ui/package.json b/config-ui/package.json index 6bf071988..7e1305d72 100644 --- a/config-ui/package.json +++ b/config-ui/package.json @@ -24,8 +24,8 @@ "dependencies": { "@ant-design/icons": "^5.4.0", "@fontsource/roboto": "^5.0.14", - "@mints/hooks": "^1.0.0-beta.2", - "@mints/miller-columns": "^2.0.0-beta.5", + "@mints/hooks": "^1.0.0-beta.9", + "@mints/miller-columns": "^2.0.0-beta.10", "@mui/icons-material": "^5.16.7", "@mui/material": "^5.16.7", "@mui/styled-engine-sc": "^6.0.0-alpha.18", diff --git a/config-ui/src/plugins/components/data-scope-remote/search-local.tsx b/config-ui/src/plugins/components/data-scope-remote/search-local.tsx index 67b8f4a7f..961ca68e9 100644 --- a/config-ui/src/plugins/components/data-scope-remote/search-local.tsx +++ b/config-ui/src/plugins/components/data-scope-remote/search-local.tsx @@ -16,11 +16,12 @@ * */ -import { useState, useReducer, useCallback } from 'react'; +import { useState, useReducer } from 'react'; import { CheckCircleFilled, SearchOutlined } from '@ant-design/icons'; import { Space, Tag, Button, Input, Modal } from 'antd'; -import { MillerColumns } from '@mints/miller-columns'; import { useDebounce } from '@mints/hooks'; +import type { IDType } from '@mints/miller-columns'; +import { MillerColumns } from '@mints/miller-columns'; import API from '@/api'; import { Block, Loading, Message } from '@/components'; @@ -76,43 +77,26 @@ export const SearchLocal = ({ mode, plugin, connectionId, config, disabledScope, const searchDebounce = useDebounce(search, { wait: 500 }); - const request = useCallback( - async (groupId?: string | number, params?: any) => { - const res = await API.scope.remote(plugin, connectionId, { - groupId: groupId ?? null, - pageToken: params?.nextPageToken, - }); - - return { - data: res.children.map((it) => ({ - parentId: it.parentId, - id: it.id, - title: it.name ?? it.fullName, - canExpand: it.type === 'group', - original: it, - })), - hasMore: !!res.nextPageToken, - params: { - nextPageToken: res.nextPageToken, - }, - }; - }, - [plugin, connectionId, scope, searchDebounce], - ); + const request = async (groupId?: string | number, params?: any) => { + const res = await API.scope.remote(plugin, connectionId, { + groupId: groupId ?? null, + pageToken: params?.nextPageToken, + }); - const requestAll = useCallback( - async (groupId?: string | number) => { - return { - data: searchDebounce - ? scope - .filter((it) => it.title.includes(searchDebounce) && !it.canExpand) - .map((it) => ({ ...it, parentId: null })) - : scope.filter((it) => it.parentId === (groupId ?? null)), - hasMore: false, - }; - }, - [scope, searchDebounce], - ); + return { + data: res.children.map((it) => ({ + parentId: it.parentId, + id: it.id, + title: it.name ?? it.fullName, + canExpand: it.type === 'group', + original: it, + })), + hasMore: !!res.nextPageToken, + params: { + nextPageToken: res.nextPageToken, + }, + }; + }; const handleRequestAll = async () => { setOpen(false); @@ -145,6 +129,24 @@ export const SearchLocal = ({ mode, plugin, connectionId, config, disabledScope, dispatch({ type: 'DONE' }); }; + const millerColumnsProps = { + bordered: true, + theme: { + colorPrimary: '#7497f7', + borderColor: '#dbe4fd', + }, + columnHeight: 300, + mode, + renderTitle: (id?: IDType) => + !id && + config.millerColumn?.firstColumnTitle && <S.ColumnTitle>{config.millerColumn.firstColumnTitle}</S.ColumnTitle>, + renderLoading: () => <Loading size={20} style={{ padding: '4px 12px' }} />, + selectable: true, + disabledIds: disabledScope.map((it) => it.id), + selectedIds: selectedScope.map((it) => it.id), + onSelectedIds: (_: IDType[], data?: any) => onChange(data ?? []), + }; + return ( <> <Block title={config.title} required> @@ -189,56 +191,19 @@ export const SearchLocal = ({ mode, plugin, connectionId, config, disabledScope, )} </Block> <Block> - {status === 'idle' && ( + {status === 'idle' ? ( <MillerColumns - bordered - theme={{ - colorPrimary: '#7497f7', - borderColor: '#dbe4fd', - }} + {...millerColumnsProps} request={request} - columnCount={search ? 1 : config.millerColumn?.columnCount ?? 1} - columnHeight={300} - mode={mode} - renderTitle={(id) => - !id && - config.millerColumn?.firstColumnTitle && ( - <S.ColumnTitle>{config.millerColumn.firstColumnTitle}</S.ColumnTitle> - ) - } - renderLoading={() => <Loading size={20} style={{ padding: '4px 12px' }} />} - renderError={() => <span style={{ color: 'red' }}>Something Error</span>} - selectable - disabledIds={(disabledScope ?? []).map((it) => it.id)} - selectedIds={selectedScope.map((it) => it.id)} - onSelectedIds={(_, data) => onChange(data ?? [])} + columnCount={config.millerColumn?.columnCount ?? 1} /> - )} - {status === 'done' && ( + ) : ( <> <Input prefix={<SearchOutlined />} value={search} onChange={(e) => setSearch(e.target.value)} /> <MillerColumns - bordered - theme={{ - colorPrimary: '#7497f7', - borderColor: '#dbe4fd', - }} - request={requestAll} - columnCount={search ? 1 : config.millerColumn?.columnCount ?? 1} - columnHeight={300} - mode={mode} - renderTitle={(id) => - !id && - config.millerColumn?.firstColumnTitle && ( - <S.ColumnTitle>{config.millerColumn.firstColumnTitle}</S.ColumnTitle> - ) - } - renderLoading={() => <Loading size={20} style={{ padding: '4px 12px' }} />} - renderError={() => <span style={{ color: 'red' }}>Something Error</span>} - selectable - disabledIds={(disabledScope ?? []).map((it) => it.id)} - selectedIds={selectedScope.map((it) => it.id)} - onSelectedIds={(_, data) => onChange(data ?? [])} + {...millerColumnsProps} + loading={status === 'loading'} + items={searchDebounce ? scope.filter((it) => it.title.includes(searchDebounce) && !it.canExpand) : scope} /> </> )} diff --git a/config-ui/src/plugins/components/data-scope-remote/search-remote.tsx b/config-ui/src/plugins/components/data-scope-remote/search-remote.tsx index 0510ad29a..1841d3403 100644 --- a/config-ui/src/plugins/components/data-scope-remote/search-remote.tsx +++ b/config-ui/src/plugins/components/data-scope-remote/search-remote.tsx @@ -16,11 +16,13 @@ * */ -import { useState, useCallback } from 'react'; +import { useState } from 'react'; import { SearchOutlined } from '@ant-design/icons'; import { Space, Tag, Input } from 'antd'; -import { MillerColumns } from '@mints/miller-columns'; +import { useRequest } from '@mints/hooks'; import { useDebounce } from '@mints/hooks'; +import type { IDType } from '@mints/miller-columns'; +import { MillerColumns } from '@mints/miller-columns'; import API from '@/api'; import { Block, Loading } from '@/components'; @@ -43,60 +45,62 @@ export const SearchRemote = ({ mode, plugin, connectionId, config, disabledScope const searchDebounce = useDebounce(search, { wait: 500 }); - const request = useCallback( - async (groupId?: string | number, params?: any) => { - let data = []; - let hasMore = false; - let newParams = {}; - - if (!searchDebounce) { - const res = await API.scope.remote(plugin, connectionId, { - groupId: groupId ?? null, - pageToken: params?.pageToken, - }); - - data = res.children.map((it) => ({ - parentId: it.parentId, - id: it.id, - title: it.name ?? it.fullName, - canExpand: it.type === 'group', - original: it, - })); + const { loading, data } = useRequest(async () => { + if (!searchDebounce) { + return []; + } + const res = await API.scope.searchRemote(plugin, connectionId, { + search: searchDebounce, + page: 1, + pageSize: 50, + }); + return res.children.map((it) => ({ + parentId: it.parentId, + id: it.id, + title: it.fullName ?? it.name, + canExpand: it.type === 'group', + original: it, + })); + }, [plugin, connectionId, searchDebounce]); - hasMore = !!res.nextPageToken; - newParams = { - pageToken: res.nextPageToken, - }; - } else { - const res = await API.scope.searchRemote(plugin, connectionId, { - search: searchDebounce, - page: params?.page ?? 1, - pageSize: 20, - }); + const request = async (groupId?: string | number, params?: any) => { + const res = await API.scope.remote(plugin, connectionId, { + groupId: groupId ?? null, + pageToken: params?.pageToken, + }); - data = res.children.map((it) => ({ - parentId: it.parentId, - id: it.id, - title: it.fullName ?? it.name, - canExpand: it.type === 'group', - original: it, - })); + return { + data: res.children.map((it) => ({ + parentId: it.parentId, + id: it.id, + title: it.name ?? it.fullName, + canExpand: it.type === 'group', + original: it, + })), + hasMore: !!res.nextPageToken, + params: { + pageToken: res.nextPageToken, + }, + }; + }; - hasMore = res.children.length === res.pageSize; - newParams = { - page: (params?.page ?? 0) + 1, - count: (params?.count ?? 0) + res.children.length, - }; - } - - return { - data, - hasMore, - params: newParams, - }; + const millerColumnsProps = { + bordered: true, + theme: { + colorPrimary: '#7497f7', + borderColor: '#dbe4fd', }, - [plugin, connectionId, searchDebounce], - ); + columnHeight: 300, + mode, + renderTitle: (id?: IDType) => + !id && + config.millerColumn?.firstColumnTitle && <S.ColumnTitle>{config.millerColumn.firstColumnTitle}</S.ColumnTitle>, + renderLoading: () => <Loading size={20} style={{ padding: '4px 12px' }} />, + selectable: true, + disabledIds: disabledScope.map((it) => it.id), + selectedIds: selectedScope.map((it) => it.id), + onSelectedIds: (_: IDType[], data?: any) => onChange(data ?? []), + }; return ( <> @@ -125,28 +129,11 @@ export const SearchRemote = ({ mode, plugin, connectionId, config, disabledScope value={search} onChange={(e) => setSearch(e.target.value)} /> - <MillerColumns - bordered - theme={{ - colorPrimary: '#7497f7', - borderColor: '#dbe4fd', - }} - request={request} - columnCount={searchDebounce ? 1 : config.millerColumn?.columnCount ?? 1} - columnHeight={300} - mode={mode} - renderTitle={(id?) => - !id && - config.millerColumn?.firstColumnTitle && ( - <S.ColumnTitle>{config.millerColumn.firstColumnTitle}</S.ColumnTitle> - ) - } - renderLoading={() => <Loading size={20} style={{ padding: '4px 12px' }} />} - selectable - disabledIds={disabledScope.map((it) => it.id)} - selectedIds={selectedScope.map((it) => it.id)} - onSelectedIds={(_, data) => onChange(data ?? [])} - /> + {searchDebounce ? ( + <MillerColumns {...millerColumnsProps} loading={loading} items={data ?? []} columnCount={1} /> + ) : ( + <MillerColumns {...millerColumnsProps} request={request} columnCount={config.millerColumn?.columnCount} /> + )} </Block> </> ); diff --git a/config-ui/src/plugins/components/data-scope-select/index.tsx b/config-ui/src/plugins/components/data-scope-select/index.tsx index d6913cee0..6bee1af1e 100644 --- a/config-ui/src/plugins/components/data-scope-select/index.tsx +++ b/config-ui/src/plugins/components/data-scope-select/index.tsx @@ -19,12 +19,15 @@ import { useState, useEffect, useCallback } from 'react'; import { RedoOutlined, PlusOutlined } from '@ant-design/icons'; import { Flex, Button, Input, Space, Tag } from 'antd'; +import { useRequest } from '@mints/hooks'; +import type { IDType } from '@mints/miller-columns'; import { MillerColumns } from '@mints/miller-columns'; import { useDebounce } from '@mints/hooks'; import API from '@/api'; import { Loading, Block, ExternalLink, Message } from '@/components'; import { getPluginScopeId } from '@/plugins'; +import type { IDataScope } from '@/types'; interface Props { plugin: string; @@ -65,6 +68,25 @@ export const DataScopeSelect = ({ ); }, []); + const { loading, data } = useRequest(async () => { + if (!searchDebounce) { + return []; + } + const res = await API.scope.list(plugin, connectionId, { + page: 1, + pageSize: 50, + searchTerm: searchDebounce, + }); + + return res.scopes.map((it) => ({ + parentId: null, + id: getPluginScopeId(plugin, it.scope), + title: it.scope.fullName ?? it.scope.name, + canExpand: false, + original: it, + })); + }, [plugin, connectionId, searchDebounce, version]); + const request = useCallback( async (_?: string | number, params?: any) => { const res = await API.scope.list(plugin, connectionId, { @@ -87,11 +109,46 @@ export const DataScopeSelect = ({ }, }; }, - [plugin, connectionId, searchDebounce, version], + [plugin, connectionId], ); const handleSubmit = () => onSubmit?.(selectedIds); + const millerColumnsProps = { + bordered: true, + theme: { + colorPrimary: '#7497f7', + borderColor: '#dbe4fd', + }, + columnHeight: 200, + renderLoading: () => <Loading size={20} style={{ padding: '4px 12px' }} />, + renderNoData: () => ( + <Flex style={{ height: '100%' }} justify="center" align="center"> + <ExternalLink link={`/connections/${plugin}/${connectionId}`}> + <Button type="primary" icon={<PlusOutlined />}> + Add Data Scope + </Button> + </ExternalLink> + </Flex> + ), + selectable: true, + selectedIds, + onSelectedIds: ( + ids: IDType[], + data?: Array<{ + scope: IDataScope; + }>, + ) => { + setSelectedIds(ids); + setSelectedScope( + (data ?? []).map((it) => ({ + id: getPluginScopeId(plugin, it.scope), + name: it.scope.fullName ?? it.scope.name, + })), + ); + }, + }; + return ( <Block title="Select Data Scope" @@ -142,37 +199,11 @@ export const DataScopeSelect = ({ </Space> <div> <Input.Search value={search} onChange={(e) => setSearch(e.target.value)} /> - <MillerColumns - bordered - theme={{ - colorPrimary: '#7497f7', - borderColor: '#dbe4fd', - }} - request={request} - columnHeight={200} - renderLoading={() => <Loading size={20} style={{ padding: '4px 12px' }} />} - renderError={() => <span style={{ color: 'red' }}>Something Error</span>} - renderNoData={() => ( - <Flex style={{ height: '100%' }} justify="center" align="center"> - <ExternalLink link={`/connections/${plugin}/${connectionId}`}> - <Button type="primary" icon={<PlusOutlined />}> - Add Data Scope - </Button> - </ExternalLink> - </Flex> - )} - selectable - selectedIds={selectedIds} - onSelectedIds={(ids, data) => { - setSelectedIds(ids); - setSelectedScope( - (data ?? []).map((it) => ({ - id: it.scope.id, - name: it.scope.name, - })), - ); - }} - /> + {searchDebounce ? ( + <MillerColumns {...millerColumnsProps} loading={loading} items={data ?? []} /> + ) : ( + <MillerColumns {...millerColumnsProps} request={request} rootId={version} /> + )} </div> <Flex justify="flex-end" gap="small"> <Button onClick={onCancel}>Cancel</Button> diff --git a/config-ui/src/plugins/register/webhook/components/selector-dialog.tsx b/config-ui/src/plugins/register/webhook/components/selector-dialog.tsx index a0c972dee..516298572 100644 --- a/config-ui/src/plugins/register/webhook/components/selector-dialog.tsx +++ b/config-ui/src/plugins/register/webhook/components/selector-dialog.tsx @@ -16,7 +16,7 @@ * */ -import { useCallback, useState } from 'react'; +import { useState } from 'react'; import { Modal } from 'antd'; import { MillerColumns } from '@mints/miller-columns'; @@ -39,17 +39,6 @@ export const SelectorDialog = ({ open, saving, onCancel, onSubmit }: Props) => { const webhooks = useAppSelector(selectWebhooks); - const request = useCallback(async () => { - return { - data: webhooks.map((it) => ({ - parentId: null, - id: it.id, - title: it.name, - })), - hasMore: false, - }; - }, [webhooks]); - const handleSubmit = () => onSubmit(webhooks.filter((it) => selectedIds.includes(it.id))); return ( @@ -74,7 +63,11 @@ export const SelectorDialog = ({ open, saving, onCancel, onSubmit }: Props) => { colorPrimary: '#7497f7', borderColor: '#dbe4fd', }} - request={request} + items={webhooks.map((it) => ({ + parentId: null, + id: it.id, + title: it.name, + }))} columnHeight={160} renderLoading={() => <Loading size={20} style={{ padding: '4px 12px' }} />} selectable diff --git a/config-ui/yarn.lock b/config-ui/yarn.lock index 9e36a73e3..c7db434e4 100644 --- a/config-ui/yarn.lock +++ b/config-ui/yarn.lock @@ -2073,9 +2073,9 @@ __metadata: languageName: node linkType: hard -"@mints/hooks@npm:^1.0.0-beta.2": - version: 1.0.0-beta.2 - resolution: "@mints/hooks@npm:1.0.0-beta.2" +"@mints/hooks@npm:^1.0.0-beta.9": + version: 1.0.0-beta.9 + resolution: "@mints/hooks@npm:1.0.0-beta.9" dependencies: lodash: ^4.17.21 react: ^18.3.1 @@ -2083,13 +2083,13 @@ __metadata: peerDependencies: react: ^18.3.1 react-dom: ^18.3.1 - checksum: b11707b70aca8c931d247e18065046d7f5bbef4eee7e17bbae1ff1c85a4f90e830d6af0d3397bd8bec63fa81b5f44055363184b162b4cd90edc0316d4d53251b + checksum: 03f146f2e1881dee18383502dc7631f630c7b99fd7da67d54d37a7b3abac6518e0a302e34b968c72d27cb21ae0c45c39e34d668930881d3e1fd2fbb7dac34b85 languageName: node linkType: hard -"@mints/miller-columns@npm:^2.0.0-beta.5": - version: 2.0.0-beta.5 - resolution: "@mints/miller-columns@npm:2.0.0-beta.5" +"@mints/miller-columns@npm:^2.0.0-beta.10": + version: 2.0.0-beta.10 + resolution: "@mints/miller-columns@npm:2.0.0-beta.10" dependencies: "@fontsource/roboto": ^5.0.14 "@mui/material": ^5.16.7 @@ -2108,7 +2108,7 @@ __metadata: react-dom: ^18.2.0 react-infinite-scroll-component: ^6.1.0 styled-components: ^6.1.12 - checksum: 2ff5e7e942a1f5e592d7fe53d5522a59d72075604e80cfcf816d70d67ad33d3982b699770cdab01065bff19a219117b51017ea878ce5f015f120fb6623431839 + checksum: 94c3ba41210f2ccbddc413ef6c26e5615f11c5a3aac7954484d838d1b05713543def70b6714e79cfc396efea522f553b2d648b36af994bf66456a440836a1386 languageName: node linkType: hard @@ -3910,8 +3910,8 @@ __metadata: dependencies: "@ant-design/icons": ^5.4.0 "@fontsource/roboto": ^5.0.14 - "@mints/hooks": ^1.0.0-beta.2 - "@mints/miller-columns": ^2.0.0-beta.5 + "@mints/hooks": ^1.0.0-beta.9 + "@mints/miller-columns": ^2.0.0-beta.10 "@mui/icons-material": ^5.16.7 "@mui/material": ^5.16.7 "@mui/styled-engine-sc": ^6.0.0-alpha.18
