R: Refactor table usage
This commit is contained in:
parent
28c84d90ba
commit
b9e5331d91
|
@ -7,11 +7,12 @@ import { HelpTopic } from '../models/helpTopic';
|
||||||
|
|
||||||
export function HelpRSLang() {
|
export function HelpRSLang() {
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
|
const isSmall = windowSize.isSmall;
|
||||||
|
|
||||||
const videoHeight = (() => {
|
const videoHeight = (() => {
|
||||||
const viewH = windowSize.height ?? 0;
|
const viewH = windowSize.height ?? 0;
|
||||||
const viewW = windowSize.width ?? 0;
|
const viewW = windowSize.width ?? 0;
|
||||||
const availableWidth = viewW - (windowSize.isSmall ? 35 : 310);
|
const availableWidth = viewW - (isSmall ? 35 : 310);
|
||||||
return Math.min(1080, Math.max(viewH - 450, 300), Math.floor((availableWidth * 9) / 16));
|
return Math.min(1080, Math.max(viewH - 450, 300), Math.floor((availableWidth * 9) / 16));
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
|
@ -1,43 +1,48 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useLayoutEffect, useState } from 'react';
|
|
||||||
import { useIntl } from 'react-intl';
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { urls, useConceptNavigation } from '@/app';
|
import { urls, useConceptNavigation } from '@/app';
|
||||||
import { useLabelUser } from '@/features/users';
|
|
||||||
|
|
||||||
import { FlexColumn } from '@/components/Container';
|
import { FlexColumn } from '@/components/Container';
|
||||||
import { MiniButton, TextURL } from '@/components/Control';
|
import { TextURL } from '@/components/Control';
|
||||||
import { createColumnHelper, DataTable, type IConditionalStyle, type VisibilityState } from '@/components/DataTable';
|
import { DataTable, type IConditionalStyle, type VisibilityState } from '@/components/DataTable';
|
||||||
import { IconFolderTree } from '@/components/Icons';
|
|
||||||
import { useWindowSize } from '@/hooks/useWindowSize';
|
import { useWindowSize } from '@/hooks/useWindowSize';
|
||||||
import { useFitHeight } from '@/stores/appLayout';
|
import { useFitHeight } from '@/stores/appLayout';
|
||||||
import { usePreferencesStore } from '@/stores/preferences';
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
import { APP_COLORS } from '@/styling/colors';
|
import { APP_COLORS } from '@/styling/colors';
|
||||||
|
|
||||||
import { type ILibraryItem, LibraryItemType } from '../../backend/types';
|
import { type ILibraryItem, LibraryItemType } from '../../backend/types';
|
||||||
import { BadgeLocation } from '../../components/BadgeLocation';
|
|
||||||
import { useLibrarySearchStore } from '../../stores/librarySearch';
|
import { useLibrarySearchStore } from '../../stores/librarySearch';
|
||||||
|
|
||||||
|
import { useLibraryColumns } from './useLibraryColumns';
|
||||||
|
|
||||||
interface TableLibraryItemsProps {
|
interface TableLibraryItemsProps {
|
||||||
items: ILibraryItem[];
|
items: ILibraryItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<ILibraryItem>();
|
|
||||||
|
|
||||||
export function TableLibraryItems({ items }: TableLibraryItemsProps) {
|
export function TableLibraryItems({ items }: TableLibraryItemsProps) {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const intl = useIntl();
|
const { isSmall } = useWindowSize();
|
||||||
const getUserLabel = useLabelUser();
|
|
||||||
|
|
||||||
const folderMode = useLibrarySearchStore(state => state.folderMode);
|
const folderMode = useLibrarySearchStore(state => state.folderMode);
|
||||||
const toggleFolderMode = useLibrarySearchStore(state => state.toggleFolderMode);
|
|
||||||
const resetFilter = useLibrarySearchStore(state => state.resetFilter);
|
const resetFilter = useLibrarySearchStore(state => state.resetFilter);
|
||||||
|
|
||||||
const itemsPerPage = usePreferencesStore(state => state.libraryPagination);
|
const itemsPerPage = usePreferencesStore(state => state.libraryPagination);
|
||||||
const setItemsPerPage = usePreferencesStore(state => state.setLibraryPagination);
|
const setItemsPerPage = usePreferencesStore(state => state.setLibraryPagination);
|
||||||
|
|
||||||
|
const columns = useLibraryColumns();
|
||||||
|
const columnVisibility: VisibilityState = { owner: !isSmall };
|
||||||
|
const conditionalRowStyles: IConditionalStyle<ILibraryItem>[] = [
|
||||||
|
{
|
||||||
|
when: (item: ILibraryItem) => item.item_type === LibraryItemType.OSS,
|
||||||
|
style: {
|
||||||
|
color: APP_COLORS.fgGreen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const tableHeight = useFitHeight('2.2rem');
|
||||||
|
|
||||||
function handleOpenItem(item: ILibraryItem, event: React.MouseEvent<Element>) {
|
function handleOpenItem(item: ILibraryItem, event: React.MouseEvent<Element>) {
|
||||||
const selection = window.getSelection();
|
const selection = window.getSelection();
|
||||||
if (!!selection && selection.toString().length > 0) {
|
if (!!selection && selection.toString().length > 0) {
|
||||||
|
@ -50,108 +55,6 @@ export function TableLibraryItems({ items }: TableLibraryItemsProps) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const windowSize = useWindowSize();
|
|
||||||
|
|
||||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
setColumnVisibility({
|
|
||||||
owner: !windowSize.isSmall
|
|
||||||
});
|
|
||||||
}, [windowSize]);
|
|
||||||
|
|
||||||
function handleToggleFolder(event: React.MouseEvent<Element>) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
toggleFolderMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
...(folderMode
|
|
||||||
? []
|
|
||||||
: [
|
|
||||||
columnHelper.accessor('location', {
|
|
||||||
id: 'location',
|
|
||||||
header: () => (
|
|
||||||
<MiniButton
|
|
||||||
noPadding
|
|
||||||
noHover
|
|
||||||
className='pl-2 max-h-[1rem] translate-y-[-0.125rem]'
|
|
||||||
onClick={handleToggleFolder}
|
|
||||||
titleHtml='Переключение в режим Проводник'
|
|
||||||
icon={<IconFolderTree size='1.25rem' className='clr-text-controls' />}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
size: 50,
|
|
||||||
minSize: 50,
|
|
||||||
maxSize: 50,
|
|
||||||
enableSorting: true,
|
|
||||||
cell: props => <BadgeLocation location={props.getValue()} />,
|
|
||||||
sortingFn: 'text'
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
columnHelper.accessor('alias', {
|
|
||||||
id: 'alias',
|
|
||||||
header: 'Шифр',
|
|
||||||
size: 150,
|
|
||||||
minSize: 80,
|
|
||||||
maxSize: 150,
|
|
||||||
enableSorting: true,
|
|
||||||
cell: props => <div className='min-w-[5rem]'>{props.getValue()}</div>,
|
|
||||||
sortingFn: 'text'
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('title', {
|
|
||||||
id: 'title',
|
|
||||||
header: 'Название',
|
|
||||||
size: 1200,
|
|
||||||
minSize: 200,
|
|
||||||
maxSize: 1200,
|
|
||||||
enableSorting: true,
|
|
||||||
sortingFn: 'text'
|
|
||||||
}),
|
|
||||||
columnHelper.accessor(item => item.owner ?? 0, {
|
|
||||||
id: 'owner',
|
|
||||||
header: 'Владелец',
|
|
||||||
size: 400,
|
|
||||||
minSize: 100,
|
|
||||||
maxSize: 400,
|
|
||||||
cell: props => getUserLabel(props.getValue()),
|
|
||||||
enableSorting: true,
|
|
||||||
sortingFn: 'text'
|
|
||||||
}),
|
|
||||||
columnHelper.accessor('time_update', {
|
|
||||||
id: 'time_update',
|
|
||||||
header: windowSize.isSmall ? 'Дата' : 'Обновлена',
|
|
||||||
cell: props => (
|
|
||||||
<div className='whitespace-nowrap'>
|
|
||||||
{new Date(props.getValue()).toLocaleString(intl.locale, {
|
|
||||||
year: '2-digit',
|
|
||||||
month: '2-digit',
|
|
||||||
day: '2-digit',
|
|
||||||
...(!windowSize.isSmall && {
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit'
|
|
||||||
})
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
enableSorting: true,
|
|
||||||
sortingFn: 'datetime',
|
|
||||||
sortDescFirst: true
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
const tableHeight = useFitHeight('2.2rem');
|
|
||||||
|
|
||||||
const conditionalRowStyles: IConditionalStyle<ILibraryItem>[] = [
|
|
||||||
{
|
|
||||||
when: (item: ILibraryItem) => item.item_type === LibraryItemType.OSS,
|
|
||||||
style: {
|
|
||||||
color: APP_COLORS.fgGreen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataTable
|
<DataTable
|
||||||
id='library_data'
|
id='library_data'
|
||||||
|
|
|
@ -26,7 +26,7 @@ interface ViewSideLocationProps {
|
||||||
export function ViewSideLocation({ isVisible, onRenameLocation }: ViewSideLocationProps) {
|
export function ViewSideLocation({ isVisible, onRenameLocation }: ViewSideLocationProps) {
|
||||||
const { user, isAnonymous } = useAuthSuspense();
|
const { user, isAnonymous } = useAuthSuspense();
|
||||||
const { items } = useLibrary();
|
const { items } = useLibrary();
|
||||||
const windowSize = useWindowSize();
|
const { isSmall } = useWindowSize();
|
||||||
|
|
||||||
const location = useLibrarySearchStore(state => state.location);
|
const location = useLibrarySearchStore(state => state.location);
|
||||||
const setLocation = useLibrarySearchStore(state => state.setLocation);
|
const setLocation = useLibrarySearchStore(state => state.setLocation);
|
||||||
|
@ -68,7 +68,7 @@ export function ViewSideLocation({ isVisible, onRenameLocation }: ViewSideLocati
|
||||||
transitionProperty: 'width, min-width, opacity',
|
transitionProperty: 'width, min-width, opacity',
|
||||||
transitionDuration: `${PARAMETER.moveDuration}ms`,
|
transitionDuration: `${PARAMETER.moveDuration}ms`,
|
||||||
transitionTimingFunction: 'ease-out',
|
transitionTimingFunction: 'ease-out',
|
||||||
minWidth: isVisible ? (windowSize.isSmall ? '10rem' : '15rem') : '0',
|
minWidth: isVisible ? (isSmall ? '10rem' : '15rem') : '0',
|
||||||
width: isVisible ? '100%' : '0',
|
width: isVisible ? '100%' : '0',
|
||||||
opacity: isVisible ? 1 : 0
|
opacity: isVisible ? 1 : 0
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { useLabelUser } from '@/features/users';
|
||||||
|
|
||||||
|
import { MiniButton } from '@/components/Control';
|
||||||
|
import { createColumnHelper } from '@/components/DataTable';
|
||||||
|
import { IconFolderTree } from '@/components/Icons';
|
||||||
|
import { useWindowSize } from '@/hooks/useWindowSize';
|
||||||
|
|
||||||
|
import { type ILibraryItem } from '../../backend/types';
|
||||||
|
import { BadgeLocation } from '../../components/BadgeLocation';
|
||||||
|
import { useLibrarySearchStore } from '../../stores/librarySearch';
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<ILibraryItem>();
|
||||||
|
|
||||||
|
export function useLibraryColumns() {
|
||||||
|
const { isSmall } = useWindowSize();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const getUserLabel = useLabelUser();
|
||||||
|
const folderMode = useLibrarySearchStore(state => state.folderMode);
|
||||||
|
const toggleFolderMode = useLibrarySearchStore(state => state.toggleFolderMode);
|
||||||
|
|
||||||
|
function handleToggleFolder(event: React.MouseEvent<Element>) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
toggleFolderMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
...(folderMode
|
||||||
|
? []
|
||||||
|
: [
|
||||||
|
columnHelper.accessor('location', {
|
||||||
|
id: 'location',
|
||||||
|
header: () => (
|
||||||
|
<MiniButton
|
||||||
|
noPadding
|
||||||
|
noHover
|
||||||
|
className='pl-2 max-h-[1rem] translate-y-[-0.125rem]'
|
||||||
|
onClick={handleToggleFolder}
|
||||||
|
titleHtml='Переключение в режим Проводник'
|
||||||
|
icon={<IconFolderTree size='1.25rem' className='clr-text-controls' />}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
size: 50,
|
||||||
|
minSize: 50,
|
||||||
|
maxSize: 50,
|
||||||
|
enableSorting: true,
|
||||||
|
cell: props => <BadgeLocation location={props.getValue()} />,
|
||||||
|
sortingFn: 'text'
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
columnHelper.accessor('alias', {
|
||||||
|
id: 'alias',
|
||||||
|
header: 'Шифр',
|
||||||
|
size: 150,
|
||||||
|
minSize: 80,
|
||||||
|
maxSize: 150,
|
||||||
|
enableSorting: true,
|
||||||
|
cell: props => <div className='min-w-[5rem]'>{props.getValue()}</div>,
|
||||||
|
sortingFn: 'text'
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('title', {
|
||||||
|
id: 'title',
|
||||||
|
header: 'Название',
|
||||||
|
size: 1200,
|
||||||
|
minSize: 200,
|
||||||
|
maxSize: 1200,
|
||||||
|
enableSorting: true,
|
||||||
|
sortingFn: 'text'
|
||||||
|
}),
|
||||||
|
columnHelper.accessor(item => item.owner ?? 0, {
|
||||||
|
id: 'owner',
|
||||||
|
header: 'Владелец',
|
||||||
|
size: 400,
|
||||||
|
minSize: 100,
|
||||||
|
maxSize: 400,
|
||||||
|
cell: props => getUserLabel(props.getValue()),
|
||||||
|
enableSorting: true,
|
||||||
|
sortingFn: 'text'
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('time_update', {
|
||||||
|
id: 'time_update',
|
||||||
|
header: isSmall ? 'Дата' : 'Обновлена',
|
||||||
|
cell: props => (
|
||||||
|
<div className='whitespace-nowrap'>
|
||||||
|
{new Date(props.getValue()).toLocaleString(intl.locale, {
|
||||||
|
year: '2-digit',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
...(!isSmall && {
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
})
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
enableSorting: true,
|
||||||
|
sortingFn: 'datetime',
|
||||||
|
sortDescFirst: true
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useLayoutEffect, useState } from 'react';
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { TextURL } from '@/components/Control';
|
import { TextURL } from '@/components/Control';
|
||||||
|
@ -49,16 +48,6 @@ export function TableRSList({
|
||||||
}: TableRSListProps) {
|
}: TableRSListProps) {
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
|
|
||||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
setColumnVisibility({
|
|
||||||
type: (windowSize.width ?? 0) >= COLUMN_TYPE_HIDE_THRESHOLD,
|
|
||||||
convention: (windowSize.width ?? 0) >= COLUMN_CONVENTION_HIDE_THRESHOLD,
|
|
||||||
definition: (windowSize.width ?? 0) >= COLUMN_DEFINITION_HIDE_THRESHOLD
|
|
||||||
});
|
|
||||||
}, [windowSize]);
|
|
||||||
|
|
||||||
function handleRowClicked(cst: IConstituenta, event: React.MouseEvent<Element>) {
|
function handleRowClicked(cst: IConstituenta, event: React.MouseEvent<Element>) {
|
||||||
if (event.altKey) {
|
if (event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -127,6 +116,12 @@ export function TableRSList({
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const columnVisibility: VisibilityState = {
|
||||||
|
type: (windowSize.width ?? 0) >= COLUMN_TYPE_HIDE_THRESHOLD,
|
||||||
|
convention: (windowSize.width ?? 0) >= COLUMN_CONVENTION_HIDE_THRESHOLD,
|
||||||
|
definition: (windowSize.width ?? 0) >= COLUMN_DEFINITION_HIDE_THRESHOLD
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataTable
|
<DataTable
|
||||||
dense
|
dense
|
||||||
|
@ -140,7 +135,6 @@ export function TableRSList({
|
||||||
onRowClicked={handleRowClicked}
|
onRowClicked={handleRowClicked}
|
||||||
enableHiding
|
enableHiding
|
||||||
columnVisibility={columnVisibility}
|
columnVisibility={columnVisibility}
|
||||||
onColumnVisibilityChange={setColumnVisibility}
|
|
||||||
enableRowSelection={enableSelection}
|
enableRowSelection={enableSelection}
|
||||||
rowSelection={selected}
|
rowSelection={selected}
|
||||||
onRowSelectionChange={setSelected}
|
onRowSelectionChange={setSelected}
|
||||||
|
|
|
@ -21,7 +21,7 @@ interface ViewHiddenProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ViewHidden({ items }: ViewHiddenProps) {
|
export function ViewHidden({ items }: ViewHiddenProps) {
|
||||||
const windowSize = useWindowSize();
|
const { isSmall } = useWindowSize();
|
||||||
const coloring = useTermGraphStore(state => state.coloring);
|
const coloring = useTermGraphStore(state => state.coloring);
|
||||||
const { navigateCst, setFocus, schema, selected, toggleSelect } = useRSEdit();
|
const { navigateCst, setFocus, schema, selected, toggleSelect } = useRSEdit();
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ export function ViewHidden({ items }: ViewHiddenProps) {
|
||||||
const isFolded = useTermGraphStore(state => state.foldHidden);
|
const isFolded = useTermGraphStore(state => state.foldHidden);
|
||||||
const toggleFolded = useTermGraphStore(state => state.toggleFoldHidden);
|
const toggleFolded = useTermGraphStore(state => state.toggleFoldHidden);
|
||||||
const setActiveCst = useCstTooltipStore(state => state.setActiveCst);
|
const setActiveCst = useCstTooltipStore(state => state.setActiveCst);
|
||||||
const hiddenHeight = useFitHeight(windowSize.isSmall ? '10.4rem + 2px' : '12.5rem + 2px');
|
const hiddenHeight = useFitHeight(isSmall ? '10.4rem + 2px' : '12.5rem + 2px');
|
||||||
|
|
||||||
function handleClick(event: React.MouseEvent<Element>, cstID: number) {
|
function handleClick(event: React.MouseEvent<Element>, cstID: number) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user