mirror of https://github.com/buster-so/buster.git
dashboard and select updates
This commit is contained in:
parent
869eecc80c
commit
5d632413ec
|
@ -1,7 +1,7 @@
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
import { BusterList } from './index';
|
import { BusterList } from './index';
|
||||||
import { BusterListRow } from './interfaces';
|
import { BusterListRow } from './interfaces';
|
||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
import { ContextMenuProps } from '../../context/ContextMenu';
|
import { ContextMenuProps } from '../../context/ContextMenu';
|
||||||
|
|
||||||
|
@ -40,23 +40,13 @@ const sampleColumns = [
|
||||||
{
|
{
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
title: 'Name',
|
title: 'Name',
|
||||||
width: 200
|
width: 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
dataIndex: 'age',
|
dataIndex: 'age',
|
||||||
title: 'Age',
|
title: 'Age',
|
||||||
width: 100
|
width: 100
|
||||||
},
|
},
|
||||||
{
|
|
||||||
dataIndex: 'address',
|
|
||||||
title: 'Address',
|
|
||||||
width: 200
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'email',
|
|
||||||
title: 'Email',
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
dataIndex: 'actions',
|
dataIndex: 'actions',
|
||||||
title: 'Actions',
|
title: 'Actions',
|
||||||
|
@ -83,7 +73,7 @@ const generateSampleRows = (count: number): BusterListRow[] => {
|
||||||
|
|
||||||
if (i === 3) {
|
if (i === 3) {
|
||||||
rows.push({
|
rows.push({
|
||||||
id: 'section1',
|
id: 'section' + i,
|
||||||
data: null,
|
data: null,
|
||||||
rowSection: {
|
rowSection: {
|
||||||
title: faker.company.name(),
|
title: faker.company.name(),
|
||||||
|
@ -96,7 +86,7 @@ const generateSampleRows = (count: number): BusterListRow[] => {
|
||||||
// Add a section row in the middle
|
// Add a section row in the middle
|
||||||
const sectionIndex = Math.floor(count / 2);
|
const sectionIndex = Math.floor(count / 2);
|
||||||
rows.splice(sectionIndex, 0, {
|
rows.splice(sectionIndex, 0, {
|
||||||
id: 'section1',
|
id: 'section' + sectionIndex,
|
||||||
data: null,
|
data: null,
|
||||||
rowSection: {
|
rowSection: {
|
||||||
title: faker.company.name(),
|
title: faker.company.name(),
|
||||||
|
@ -142,19 +132,31 @@ export const Default: Story = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WithSelection: Story = {
|
export const WithSelection: Story = {
|
||||||
args: {
|
render: () => {
|
||||||
columns: sampleColumns,
|
const sampleRows = useMemo(() => {
|
||||||
rows: sampleRows,
|
return generateSampleRows(50);
|
||||||
selectedRowKeys: [sampleRows[0].id, sampleRows[2].id],
|
}, []);
|
||||||
showHeader: true,
|
|
||||||
showSelectAll: true,
|
const [selectedKeys, setSelectedKeys] = React.useState<string[]>([]);
|
||||||
onSelectChange: (selectedRowKeys) => alert(`Selected ${selectedRowKeys.join(', ')}`)
|
|
||||||
},
|
return (
|
||||||
render: (args) => (
|
|
||||||
<div style={{ height: '400px', width: '800px' }}>
|
<div style={{ height: '400px', width: '800px' }}>
|
||||||
<BusterList {...args} />
|
<div className="mb-4">
|
||||||
|
<p className="rounded border border-blue-200 bg-blue-300 p-1 text-sm text-blue-900">
|
||||||
|
Selected rows: {selectedKeys.join(', ') || 'None'}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
<BusterList
|
||||||
|
columns={sampleColumns}
|
||||||
|
rows={sampleRows}
|
||||||
|
selectedRowKeys={selectedKeys}
|
||||||
|
onSelectChange={setSelectedKeys}
|
||||||
|
showHeader={true}
|
||||||
|
showSelectAll={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WithContextMenu: Story = {
|
export const WithContextMenu: Story = {
|
||||||
|
@ -173,19 +175,28 @@ export const WithContextMenu: Story = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WithRowClickSelection: Story = {
|
export const WithRowClickSelection: Story = {
|
||||||
args: {
|
render: () => {
|
||||||
columns: sampleColumns,
|
const [selectedKeys, setSelectedKeys] = React.useState<string[]>([]);
|
||||||
rows: sampleRows,
|
|
||||||
useRowClickSelectChange: true,
|
return (
|
||||||
showHeader: true,
|
|
||||||
showSelectAll: true,
|
|
||||||
onSelectChange: (selectedRowKeys) => alert(`Selected ${selectedRowKeys.join(', ')}`)
|
|
||||||
},
|
|
||||||
render: (args) => (
|
|
||||||
<div style={{ height: '400px', width: '800px' }}>
|
<div style={{ height: '400px', width: '800px' }}>
|
||||||
<BusterList {...args} />
|
<div className="mb-4">
|
||||||
|
<p className="text-sm text-gray-500">
|
||||||
|
Selected rows: {selectedKeys.join(', ') || 'None'}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
<BusterList
|
||||||
|
columns={sampleColumns}
|
||||||
|
rows={sampleRows}
|
||||||
|
selectedRowKeys={selectedKeys}
|
||||||
|
onSelectChange={setSelectedKeys}
|
||||||
|
useRowClickSelectChange={true}
|
||||||
|
showHeader={true}
|
||||||
|
showSelectAll={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WithoutHeader: Story = {
|
export const WithoutHeader: Story = {
|
||||||
|
@ -216,18 +227,15 @@ export const EmptyState: Story = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BorderVariant: Story = {
|
export const BorderVariant: Story = {
|
||||||
args: {
|
render: () => {
|
||||||
columns: sampleColumns,
|
const [selectedKeys, setSelectedKeys] = React.useState<string[]>(
|
||||||
rows: generateSampleRows(30),
|
generateSampleRows(30)
|
||||||
hideLastRowBorder: true,
|
|
||||||
showHeader: true,
|
|
||||||
showSelectAll: true,
|
|
||||||
onSelectChange: (selectedRowKeys) => alert(`Selected ${selectedRowKeys.join(', ')}`),
|
|
||||||
selectedRowKeys: generateSampleRows(30)
|
|
||||||
.filter((_, index) => index % 3 === 0)
|
.filter((_, index) => index % 3 === 0)
|
||||||
.map((row) => row.id)
|
.map((row) => row.id)
|
||||||
},
|
);
|
||||||
render: (args) => (
|
const rows = generateSampleRows(30);
|
||||||
|
|
||||||
|
return (
|
||||||
<div className="flex flex-col gap-4" style={{ height: '900px', width: '800px' }}>
|
<div className="flex flex-col gap-4" style={{ height: '900px', width: '800px' }}>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h3 className="text-lg font-medium">Border Variant with Many Rows</h3>
|
<h3 className="text-lg font-medium">Border Variant with Many Rows</h3>
|
||||||
|
@ -235,19 +243,43 @@ export const BorderVariant: Story = {
|
||||||
This variant remove the border of the last row. This is useful when you want to put this
|
This variant remove the border of the last row. This is useful when you want to put this
|
||||||
list inside of a container that already contains a border
|
list inside of a container that already contains a border
|
||||||
</p>
|
</p>
|
||||||
|
<div className="mb-4">
|
||||||
|
<p className="text-sm text-gray-500">
|
||||||
|
Selected rows: {selectedKeys.join(', ') || 'None'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div className="min-h-[300px]">
|
<div className="min-h-[300px]">
|
||||||
<BusterList {...args} />
|
<BusterList
|
||||||
|
columns={sampleColumns}
|
||||||
|
rows={rows}
|
||||||
|
selectedRowKeys={selectedKeys}
|
||||||
|
onSelectChange={setSelectedKeys}
|
||||||
|
hideLastRowBorder={true}
|
||||||
|
showHeader={true}
|
||||||
|
showSelectAll={true}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h3 className="text-lg font-medium">Default Variant with Many Rows</h3>
|
<h3 className="text-lg font-medium">Default Variant with Many Rows</h3>
|
||||||
<p className="text-sm text-gray-500">This variant shows rows without container styling.</p>
|
<p className="text-sm text-gray-500">
|
||||||
|
This variant shows rows without container styling.
|
||||||
|
</p>
|
||||||
<div className="min-h-[300px]">
|
<div className="min-h-[300px]">
|
||||||
<BusterList {...args} hideLastRowBorder={false} />
|
<BusterList
|
||||||
|
columns={sampleColumns}
|
||||||
|
rows={rows}
|
||||||
|
selectedRowKeys={selectedKeys}
|
||||||
|
onSelectChange={setSelectedKeys}
|
||||||
|
hideLastRowBorder={false}
|
||||||
|
showHeader={true}
|
||||||
|
showSelectAll={true}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Story with many rows to demonstrate virtualization
|
// Story with many rows to demonstrate virtualization
|
||||||
|
@ -279,3 +311,28 @@ export const ManyRowsWithContextMenu: Story = {
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const InteractiveSelection: Story = {
|
||||||
|
render: () => {
|
||||||
|
const [selectedKeys, setSelectedKeys] = React.useState<string[]>([]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: '400px', width: '800px' }}>
|
||||||
|
<div className="mb-4">
|
||||||
|
<p className="text-sm text-gray-500">
|
||||||
|
Selected rows: {selectedKeys.join(', ') || 'None'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<BusterList
|
||||||
|
columns={sampleColumns}
|
||||||
|
rows={sampleRows}
|
||||||
|
selectedRowKeys={selectedKeys}
|
||||||
|
onSelectChange={setSelectedKeys}
|
||||||
|
showHeader={true}
|
||||||
|
showSelectAll={true}
|
||||||
|
useRowClickSelectChange={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const BusterListRowComponent = React.memo(
|
||||||
row: BusterListRow;
|
row: BusterListRow;
|
||||||
columns: BusterListColumn[];
|
columns: BusterListColumn[];
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
onSelectChange?: (v: boolean, id: string) => void;
|
onSelectChange?: (v: boolean, id: string, e: React.MouseEvent) => void;
|
||||||
onContextMenuClick?: (e: React.MouseEvent<HTMLDivElement>, id: string) => void;
|
onContextMenuClick?: (e: React.MouseEvent<HTMLDivElement>, id: string) => void;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
hideLastRowBorder: NonNullable<BusterListProps['hideLastRowBorder']>;
|
hideLastRowBorder: NonNullable<BusterListProps['hideLastRowBorder']>;
|
||||||
|
@ -44,13 +44,13 @@ export const BusterListRowComponent = React.memo(
|
||||||
onContextMenuClick?.(e, row.id);
|
onContextMenuClick?.(e, row.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
const onChange = useMemoizedFn((newChecked: boolean) => {
|
const onChange = useMemoizedFn((newChecked: boolean, e: React.MouseEvent) => {
|
||||||
onSelectChange?.(newChecked, row.id);
|
onSelectChange?.(newChecked, row.id, e);
|
||||||
});
|
});
|
||||||
|
|
||||||
const onContainerClick = useMemoizedFn(() => {
|
const onContainerClick = useMemoizedFn((e: React.MouseEvent) => {
|
||||||
if (useRowClickSelectChange) {
|
if (useRowClickSelectChange) {
|
||||||
onChange(!checked);
|
onChange(!checked, e);
|
||||||
}
|
}
|
||||||
row.onClick?.();
|
row.onClick?.();
|
||||||
});
|
});
|
||||||
|
@ -110,7 +110,7 @@ const BusterListCellComponent: React.FC<{
|
||||||
isFirstCell?: boolean;
|
isFirstCell?: boolean;
|
||||||
isLastCell?: boolean;
|
isLastCell?: boolean;
|
||||||
width?: number | undefined;
|
width?: number | undefined;
|
||||||
onSelectChange?: (v: boolean, id: string) => void;
|
onSelectChange?: (v: boolean, id: string, e: React.MouseEvent) => void;
|
||||||
render?: (data: string | number | React.ReactNode, row: BusterListRowItem) => React.ReactNode;
|
render?: (data: string | number | React.ReactNode, row: BusterListRowItem) => React.ReactNode;
|
||||||
}> = React.memo(({ data, width, row, render, isFirstCell, isLastCell, onSelectChange }) => {
|
}> = React.memo(({ data, width, row, render, isFirstCell, isLastCell, onSelectChange }) => {
|
||||||
const memoizedStyle = useMemo(() => {
|
const memoizedStyle = useMemo(() => {
|
||||||
|
|
|
@ -9,7 +9,7 @@ export const BusterListRowComponentSelector = React.forwardRef<
|
||||||
row: BusterListRow;
|
row: BusterListRow;
|
||||||
columns: BusterListColumn[];
|
columns: BusterListColumn[];
|
||||||
id: string;
|
id: string;
|
||||||
onSelectChange?: (v: boolean, id: string) => void;
|
onSelectChange?: (v: boolean, id: string, e: React.MouseEvent) => void;
|
||||||
onSelectSectionChange?: (v: boolean, id: string) => void;
|
onSelectSectionChange?: (v: boolean, id: string) => void;
|
||||||
onContextMenuClick?: (e: React.MouseEvent<HTMLDivElement>, id: string) => void;
|
onContextMenuClick?: (e: React.MouseEvent<HTMLDivElement>, id: string) => void;
|
||||||
selectedRowKeys?: string[];
|
selectedRowKeys?: string[];
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { VList } from 'virtua';
|
import { VList } from 'virtua';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo, useRef } from 'react';
|
||||||
import { BusterListProps } from './interfaces';
|
import { BusterListProps } from './interfaces';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn } from '@/hooks';
|
||||||
import { getAllIdsInSection } from './helpers';
|
import { getAllIdsInSection } from './helpers';
|
||||||
|
@ -26,6 +26,7 @@ export const BusterListVirtua = React.memo(
|
||||||
}: BusterListProps) => {
|
}: BusterListProps) => {
|
||||||
const showEmptyState = (!rows || rows.length === 0) && !!emptyState;
|
const showEmptyState = (!rows || rows.length === 0) && !!emptyState;
|
||||||
const lastChildIndex = rows.length - 1;
|
const lastChildIndex = rows.length - 1;
|
||||||
|
const lastSelectedIdRef = useRef<string | null>(null);
|
||||||
|
|
||||||
const globalCheckStatus = useMemo(() => {
|
const globalCheckStatus = useMemo(() => {
|
||||||
if (!selectedRowKeys) return 'unchecked';
|
if (!selectedRowKeys) return 'unchecked';
|
||||||
|
@ -49,15 +50,45 @@ export const BusterListVirtua = React.memo(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSelectChangePreflight = useMemoizedFn((v: boolean, id: string) => {
|
const getItemsBetween = useMemoizedFn((startId: string, endId: string) => {
|
||||||
if (!onSelectChange || !selectedRowKeys) return;
|
const startIndex = rows.findIndex((row) => row.id === startId);
|
||||||
if (v === false) {
|
const endIndex = rows.findIndex((row) => row.id === endId);
|
||||||
onSelectChange(selectedRowKeys?.filter((d) => d !== id));
|
|
||||||
} else {
|
if (startIndex === -1 || endIndex === -1) return [];
|
||||||
onSelectChange(selectedRowKeys?.concat(id) || []);
|
|
||||||
}
|
const start = Math.min(startIndex, endIndex);
|
||||||
|
const end = Math.max(startIndex, endIndex);
|
||||||
|
|
||||||
|
return rows
|
||||||
|
.slice(start, end + 1)
|
||||||
|
.filter((row) => !row.rowSection && !row.hidden)
|
||||||
|
.map((row) => row.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const onSelectChangePreflight = useMemoizedFn(
|
||||||
|
(v: boolean, id: string, event?: React.MouseEvent) => {
|
||||||
|
if (!onSelectChange || !selectedRowKeys) return;
|
||||||
|
|
||||||
|
if (event?.shiftKey && lastSelectedIdRef.current) {
|
||||||
|
const itemsBetween = getItemsBetween(lastSelectedIdRef.current, id);
|
||||||
|
if (v) {
|
||||||
|
const newSelectedKeys = Array.from(new Set([...selectedRowKeys, ...itemsBetween]));
|
||||||
|
onSelectChange(newSelectedKeys);
|
||||||
|
} else {
|
||||||
|
onSelectChange(selectedRowKeys.filter((key) => !itemsBetween.includes(key)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (v === false) {
|
||||||
|
onSelectChange(selectedRowKeys.filter((d) => d !== id));
|
||||||
|
} else {
|
||||||
|
onSelectChange(selectedRowKeys.concat(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastSelectedIdRef.current = id;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const itemSize = useMemoizedFn((index: number) => {
|
const itemSize = useMemoizedFn((index: number) => {
|
||||||
const row = rows[index];
|
const row = rows[index];
|
||||||
return row.rowSection ? HEIGHT_OF_SECTION_ROW : HEIGHT_OF_ROW;
|
return row.rowSection ? HEIGHT_OF_SECTION_ROW : HEIGHT_OF_ROW;
|
||||||
|
@ -83,10 +114,11 @@ export const BusterListVirtua = React.memo(
|
||||||
hideLastRowBorder
|
hideLastRowBorder
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const WrapperNode = !!contextMenu ? ContextMenu : React.Fragment;
|
const [WrapperNode, wrapperNodeProps] = useMemo(() => {
|
||||||
const wrapperNodeProps: ContextMenuProps = !!contextMenu
|
const node = !!contextMenu ? ContextMenu : React.Fragment;
|
||||||
? contextMenu
|
const props: ContextMenuProps = !!contextMenu ? contextMenu : ({} as ContextMenuProps);
|
||||||
: ({} as ContextMenuProps);
|
return [node, props];
|
||||||
|
}, [contextMenu]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WrapperNode {...wrapperNodeProps}>
|
<WrapperNode {...wrapperNodeProps}>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { cn } from '@/lib/classMerge';
|
||||||
|
|
||||||
export const CheckboxColumn: React.FC<{
|
export const CheckboxColumn: React.FC<{
|
||||||
checkStatus: 'checked' | 'unchecked' | 'indeterminate' | undefined;
|
checkStatus: 'checked' | 'unchecked' | 'indeterminate' | undefined;
|
||||||
onChange: (v: boolean) => void;
|
onChange: (v: boolean, e: React.MouseEvent) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
}> = React.memo(({ checkStatus, onChange, className = '' }) => {
|
}> = React.memo(({ checkStatus, onChange, className = '' }) => {
|
||||||
const showBox = checkStatus === 'checked'; //|| checkStatus === 'indeterminate';
|
const showBox = checkStatus === 'checked'; //|| checkStatus === 'indeterminate';
|
||||||
|
@ -16,10 +16,6 @@ export const CheckboxColumn: React.FC<{
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
const onChangePreflight = useMemoizedFn((e: boolean) => {
|
|
||||||
onChange(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={onClickStopPropagation}
|
onClick={onClickStopPropagation}
|
||||||
|
@ -35,7 +31,7 @@ export const CheckboxColumn: React.FC<{
|
||||||
<MemoizedCheckbox
|
<MemoizedCheckbox
|
||||||
checked={checkStatus === 'checked'}
|
checked={checkStatus === 'checked'}
|
||||||
indeterminate={checkStatus === 'indeterminate'}
|
indeterminate={checkStatus === 'indeterminate'}
|
||||||
onChange={onChangePreflight}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,14 +11,20 @@ export const MemoizedCheckbox = React.memo(
|
||||||
}: {
|
}: {
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
indeterminate: boolean;
|
indeterminate: boolean;
|
||||||
onChange: (v: boolean) => void;
|
onChange: (v: boolean, e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const handleChange = useMemoizedFn((checkedState: CheckedState) => {
|
const handleChange = useMemoizedFn(
|
||||||
onChange?.(checkedState === true);
|
(checkedState: CheckedState, e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||||
});
|
onChange?.(checkedState === true, e);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Checkbox checked={checked} indeterminate={indeterminate} onCheckedChange={handleChange} />
|
<Checkbox
|
||||||
|
checked={checked}
|
||||||
|
indeterminate={indeterminate}
|
||||||
|
onClick={(e) => handleChange(!checked, e)}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,13 +8,11 @@ import { AddTypeModal } from '@/components/features/modal/AddTypeModal';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn } from '@/hooks';
|
||||||
import { useGetDashboard } from '@/api/buster_rest/dashboards';
|
import { useGetDashboard } from '@/api/buster_rest/dashboards';
|
||||||
|
|
||||||
export const DashboardController: React.FC<{ dashboardId: string; readOnly?: boolean }> = ({
|
export const DashboardController: React.FC<{ dashboardId: string }> = ({ dashboardId }) => {
|
||||||
dashboardId,
|
|
||||||
readOnly = false
|
|
||||||
}) => {
|
|
||||||
const { data: dashboardResponse, isFetched: isFetchedDashboard } = useGetDashboard(dashboardId);
|
const { data: dashboardResponse, isFetched: isFetchedDashboard } = useGetDashboard(dashboardId);
|
||||||
const selectedFileView = useChatLayoutContextSelector((x) => x.selectedFileView) || 'dashboard';
|
const selectedFileView = useChatLayoutContextSelector((x) => x.selectedFileView) || 'dashboard';
|
||||||
const [openAddTypeModal, setOpenAddTypeModal] = useState(false);
|
const [openAddTypeModal, setOpenAddTypeModal] = useState(false);
|
||||||
|
|
||||||
const onCloseModal = useMemoizedFn(() => {
|
const onCloseModal = useMemoizedFn(() => {
|
||||||
setOpenAddTypeModal(false);
|
setOpenAddTypeModal(false);
|
||||||
});
|
});
|
||||||
|
|
|
@ -41,7 +41,7 @@ SaveToCollectionButton.displayName = 'SaveToCollectionButton';
|
||||||
|
|
||||||
const AddContentToDashboardButton = React.memo(() => {
|
const AddContentToDashboardButton = React.memo(() => {
|
||||||
return (
|
return (
|
||||||
<AppTooltip title="Add to dashboard">
|
<AppTooltip title="Add content">
|
||||||
<Button variant="ghost" prefix={<Plus />} />
|
<Button variant="ghost" prefix={<Plus />} />
|
||||||
</AppTooltip>
|
</AppTooltip>
|
||||||
);
|
);
|
||||||
|
|
|
@ -164,7 +164,7 @@ const useDashboardSelectMenu = ({ metricId }: { metricId: string }) => {
|
||||||
|
|
||||||
const dashboardDropdownItem: DropdownItem = useMemo(
|
const dashboardDropdownItem: DropdownItem = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
label: 'Add to dashboard',
|
label: 'Add content',
|
||||||
value: 'add-to-dashboard',
|
value: 'add-to-dashboard',
|
||||||
icon: <ASSET_ICONS.dashboardAdd />,
|
icon: <ASSET_ICONS.dashboardAdd />,
|
||||||
items: [<React.Fragment key="dashboard-sub-menu">{dashboardSubMenu}</React.Fragment>]
|
items: [<React.Fragment key="dashboard-sub-menu">{dashboardSubMenu}</React.Fragment>]
|
||||||
|
|
Loading…
Reference in New Issue