mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Refactoring: replacing data table component pt1
This commit is contained in:
parent
82801e81c0
commit
86c2965820
|
@ -19,11 +19,11 @@ This readme file is used mostly to document project dependencies
|
||||||
- js-file-download
|
- js-file-download
|
||||||
- react-tabs
|
- react-tabs
|
||||||
- react-intl
|
- react-intl
|
||||||
- react-data-table-component
|
|
||||||
- react-select
|
- react-select
|
||||||
- react-error-boundary
|
- react-error-boundary
|
||||||
- reagraph
|
- reagraph
|
||||||
- react-tooltip
|
- react-tooltip
|
||||||
|
- @tanstack/react-table
|
||||||
- @uiw/react-codemirror
|
- @uiw/react-codemirror
|
||||||
- @uiw/codemirror-themes
|
- @uiw/codemirror-themes
|
||||||
- @lezer/lr
|
- @lezer/lr
|
||||||
|
|
46
rsconcept/frontend/package-lock.json
generated
46
rsconcept/frontend/package-lock.json
generated
|
@ -9,12 +9,12 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/lr": "^1.3.10",
|
"@lezer/lr": "^1.3.10",
|
||||||
|
"@tanstack/react-table": "^8.9.7",
|
||||||
"@uiw/codemirror-themes": "^4.21.13",
|
"@uiw/codemirror-themes": "^4.21.13",
|
||||||
"@uiw/react-codemirror": "^4.21.13",
|
"@uiw/react-codemirror": "^4.21.13",
|
||||||
"axios": "^1.5.0",
|
"axios": "^1.5.0",
|
||||||
"js-file-download": "^0.4.12",
|
"js-file-download": "^0.4.12",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-data-table-component": "^7.5.4",
|
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-error-boundary": "^4.0.11",
|
"react-error-boundary": "^4.0.11",
|
||||||
"react-intl": "^6.4.4",
|
"react-intl": "^6.4.4",
|
||||||
|
@ -3931,6 +3931,37 @@
|
||||||
"@sinonjs/commons": "^3.0.0"
|
"@sinonjs/commons": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/react-table": {
|
||||||
|
"version": "8.9.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.9.7.tgz",
|
||||||
|
"integrity": "sha512-UKUekM8JNUyWbjT1q3s1GpH5OtBL9mJ4258Il23fsahvkh3ou9TuFVmqI0/UPiFROgHkRlCBDNPUhcsC9YPFgg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/table-core": "8.9.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16",
|
||||||
|
"react-dom": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/table-core": {
|
||||||
|
"version": "8.9.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.9.7.tgz",
|
||||||
|
"integrity": "sha512-lkhVcGDxa9GSoDFPkplPDvzsiUACPZrxT3U1edPs0DCMKFhBDgZ7d1DPd7cqHH0JoybfbQ/qiTQYOQBg8sinJg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tweenjs/tween.js": {
|
"node_modules/@tweenjs/tween.js": {
|
||||||
"version": "18.6.4",
|
"version": "18.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-18.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-18.6.4.tgz",
|
||||||
|
@ -5477,6 +5508,7 @@
|
||||||
"version": "4.3.1",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
||||||
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
|
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
|
||||||
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
@ -9318,18 +9350,6 @@
|
||||||
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-data-table-component": {
|
|
||||||
"version": "7.5.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-data-table-component/-/react-data-table-component-7.5.4.tgz",
|
|
||||||
"integrity": "sha512-6DGVj3urJZfEEMuP652fSjxdRVKeyb+9d0YounVc+MX8jwoyXQW6KO10eyZqElE9QtVrKrCeJxR7vht9yxyJiw==",
|
|
||||||
"dependencies": {
|
|
||||||
"deepmerge": "^4.2.2"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">= 16.8.3",
|
|
||||||
"styled-components": ">= 4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-dom": {
|
"node_modules/react-dom": {
|
||||||
"version": "18.2.0",
|
"version": "18.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||||
|
|
|
@ -13,12 +13,12 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/lr": "^1.3.10",
|
"@lezer/lr": "^1.3.10",
|
||||||
|
"@tanstack/react-table": "^8.9.7",
|
||||||
"@uiw/codemirror-themes": "^4.21.13",
|
"@uiw/codemirror-themes": "^4.21.13",
|
||||||
"@uiw/react-codemirror": "^4.21.13",
|
"@uiw/react-codemirror": "^4.21.13",
|
||||||
"axios": "^1.5.0",
|
"axios": "^1.5.0",
|
||||||
"js-file-download": "^0.4.12",
|
"js-file-download": "^0.4.12",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-data-table-component": "^7.5.4",
|
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-error-boundary": "^4.0.11",
|
"react-error-boundary": "^4.0.11",
|
||||||
"react-intl": "^6.4.4",
|
"react-intl": "^6.4.4",
|
||||||
|
|
|
@ -27,7 +27,7 @@ function Button({
|
||||||
disabled={disabled ?? loading}
|
disabled={disabled ?? loading}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
className={`inline-flex items-center gap-2 align-middle justify-center select-none ${padding} ${borderClass} ${colorClass} ${widthClass} ${cursor}`}
|
className={`inline-flex items-center gap-2 align-middle justify-center select-none ${padding} ${colorClass} ${widthClass} ${borderClass} ${cursor}`}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{icon && <span>{icon}</span>}
|
{icon && <span>{icon}</span>}
|
||||||
|
|
|
@ -3,7 +3,8 @@ import { useMemo } from 'react';
|
||||||
import { CheckboxChecked } from '../Icons';
|
import { CheckboxChecked } from '../Icons';
|
||||||
import Label from './Label';
|
import Label from './Label';
|
||||||
|
|
||||||
export interface CheckboxProps {
|
export interface CheckboxProps
|
||||||
|
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'children' | 'title' | 'value' | 'onClick' > {
|
||||||
id?: string
|
id?: string
|
||||||
label?: string
|
label?: string
|
||||||
required?: boolean
|
required?: boolean
|
||||||
|
@ -15,7 +16,10 @@ export interface CheckboxProps {
|
||||||
setValue?: (newValue: boolean) => void
|
setValue?: (newValue: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-fit', value, setValue }: CheckboxProps) {
|
function Checkbox({
|
||||||
|
id, required, disabled, tooltip, label,
|
||||||
|
widthClass = 'w-fit', value, setValue, ...props
|
||||||
|
}: CheckboxProps) {
|
||||||
const cursor = useMemo(
|
const cursor = useMemo(
|
||||||
() => {
|
() => {
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
|
@ -46,8 +50,11 @@ function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-fit'
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
|
{...props}
|
||||||
>
|
>
|
||||||
<div className={`relative peer w-4 h-4 shrink-0 mt-0.5 border rounded-sm appearance-none ${bgColor} ${cursor}`} />
|
<div className={`max-w-[1rem] min-w-[1rem] h-4 mt-0.5 border rounded-sm ${bgColor} ${cursor}`} >
|
||||||
|
{ value && <div className='mt-[1px] ml-[1px]'><CheckboxChecked /></div>}
|
||||||
|
</div>
|
||||||
{ label &&
|
{ label &&
|
||||||
<Label
|
<Label
|
||||||
className={`${cursor} px-2 text-start`}
|
className={`${cursor} px-2 text-start`}
|
||||||
|
@ -55,7 +62,6 @@ function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-fit'
|
||||||
required={required}
|
required={required}
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
/>}
|
/>}
|
||||||
{value && <CheckboxChecked />}
|
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
import DataTable, { createTheme, type TableProps } from 'react-data-table-component';
|
|
||||||
|
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
|
||||||
import { dataTableDarkT, dataTableLightT } from '../../utils/color';
|
|
||||||
|
|
||||||
export interface SelectionInfo<T> {
|
|
||||||
allSelected: boolean
|
|
||||||
selectedCount: number
|
|
||||||
selectedRows: T[]
|
|
||||||
}
|
|
||||||
|
|
||||||
createTheme('customDark', dataTableDarkT, 'dark');
|
|
||||||
createTheme('customLight', dataTableLightT, 'light');
|
|
||||||
|
|
||||||
interface ConceptDataTableProps<T>
|
|
||||||
extends Omit<TableProps<T>, 'paginationComponentOptions'> {}
|
|
||||||
|
|
||||||
function ConceptDataTable<T>({ theme, ...props }: ConceptDataTableProps<T>) {
|
|
||||||
const { darkMode } = useConceptTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DataTable<T>
|
|
||||||
theme={ theme ?? (darkMode ? 'customDark' : 'customLight')}
|
|
||||||
paginationComponentOptions={{
|
|
||||||
rowsPerPageText: 'строк на страницу'
|
|
||||||
}}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ConceptDataTable;
|
|
|
@ -17,34 +17,27 @@ function ConceptSelectSingle<
|
||||||
> ({ ...props }: ConceptSelectSingleProps<Option, Group>) {
|
> ({ ...props }: ConceptSelectSingleProps<Option, Group>) {
|
||||||
const { darkMode, colors } = useConceptTheme();
|
const { darkMode, colors } = useConceptTheme();
|
||||||
const themeColors = useMemo(
|
const themeColors = useMemo(
|
||||||
() => {
|
() => !darkMode ? selectLightT : selectDarkT
|
||||||
return !darkMode ? selectLightT : selectDarkT;
|
, [darkMode]);
|
||||||
}, [darkMode]);
|
|
||||||
|
|
||||||
const adjustedStyles: StylesConfig<Option, false, Group> = useMemo(
|
const adjustedStyles: StylesConfig<Option, false, Group> = useMemo(
|
||||||
() => {
|
() => ({
|
||||||
return {
|
control: (styles, { isDisabled }) => ({
|
||||||
control: (styles, { isDisabled }) => {
|
...styles,
|
||||||
return {
|
borderRadius: '0.25rem',
|
||||||
...styles,
|
cursor: isDisabled ? 'not-allowed' : 'pointer'
|
||||||
borderRadius: '0.25rem',
|
}),
|
||||||
cursor: isDisabled ? 'not-allowed' : 'pointer'
|
option: (styles, { isSelected }) => ({
|
||||||
};
|
...styles,
|
||||||
},
|
backgroundColor: isSelected ? colors.bgSelected : styles.backgroundColor,
|
||||||
option: (styles, { isSelected }) => {
|
color: isSelected ? colors.fgSelected : styles.color,
|
||||||
return {
|
borderWidth: '1px',
|
||||||
...styles,
|
borderColor: colors.border
|
||||||
backgroundColor: isSelected ? colors.bgSelected : styles.backgroundColor,
|
}),
|
||||||
color: isSelected ? colors.fgSelected : styles.color,
|
input: (styles) => ({...styles}),
|
||||||
borderWidth: '1px',
|
placeholder: (styles) => ({...styles}),
|
||||||
borderColor: colors.border
|
singleValue: (styles) => ({...styles}),
|
||||||
};
|
}), [colors]);
|
||||||
},
|
|
||||||
input: (styles) => ({...styles}),
|
|
||||||
placeholder: (styles) => ({...styles}),
|
|
||||||
singleValue: (styles) => ({...styles}),
|
|
||||||
};
|
|
||||||
}, [colors]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
|
|
|
@ -12,7 +12,7 @@ function ConceptTooltip({ className, layer, place='bottom', ...props }: ConceptT
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
opacity={0.95}
|
opacity={0.97}
|
||||||
className={`overflow-auto border shadow-md ${layer ?? 'z-tooltip'} ${className}`}
|
className={`overflow-auto border shadow-md ${layer ?? 'z-tooltip'} ${className}`}
|
||||||
variant={(darkMode ? 'dark' : 'light')}
|
variant={(darkMode ? 'dark' : 'light')}
|
||||||
place={place}
|
place={place}
|
||||||
|
|
265
rsconcept/frontend/src/components/Common/DataTable.tsx
Normal file
265
rsconcept/frontend/src/components/Common/DataTable.tsx
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
import { Cell, flexRender, getCoreRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
Header, HeaderGroup, Row, RowData, TableOptions, useReactTable
|
||||||
|
} from '@tanstack/react-table';
|
||||||
|
|
||||||
|
import Checkbox from './Checkbox';
|
||||||
|
import Tristate from './Tristate';
|
||||||
|
|
||||||
|
export interface DataTableProps<TData extends RowData>
|
||||||
|
extends Omit<TableOptions<TData>, 'getCoreRowModel' | 'getSortedRowModel'| 'getPaginationRowModel'> {
|
||||||
|
onRowClicked?: (row: TData, event: React.MouseEvent<Element, MouseEvent>) => void
|
||||||
|
onRowDoubleClicked?: (row: TData, event: React.MouseEvent<Element, MouseEvent>) => void
|
||||||
|
noDataComponent?: React.ReactNode
|
||||||
|
pagination?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultNoDataComponent() {
|
||||||
|
return (
|
||||||
|
<div className='p-2 text-center'>
|
||||||
|
Данные отсутствуют
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DataTable<TData extends RowData>({
|
||||||
|
onRowClicked, onRowDoubleClicked, noDataComponent=defaultNoDataComponent(),
|
||||||
|
enableRowSelection, enableMultiRowSelection,
|
||||||
|
pagination,
|
||||||
|
...options
|
||||||
|
}: DataTableProps<TData>) {
|
||||||
|
// const [sorting, setSorting] = React.useState<SortingState>([])
|
||||||
|
|
||||||
|
const tableImpl = useReactTable({
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getSortedRowModel: options.enableSorting ? getSortedRowModel() : undefined,
|
||||||
|
getPaginationRowModel: pagination ? getPaginationRowModel() : undefined,
|
||||||
|
|
||||||
|
state: {
|
||||||
|
...options.state
|
||||||
|
},
|
||||||
|
// onSortingChange: setSorting,
|
||||||
|
enableRowSelection: enableRowSelection,
|
||||||
|
enableMultiRowSelection: enableMultiRowSelection,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
|
||||||
|
const isEmpty = tableImpl.getRowModel().rows.length === 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='w-full'>
|
||||||
|
{isEmpty && noDataComponent}
|
||||||
|
{!isEmpty &&
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
{tableImpl.getHeaderGroups().map(
|
||||||
|
(headerGroup: HeaderGroup<TData>) => (
|
||||||
|
<tr key={headerGroup.id}>
|
||||||
|
{(enableRowSelection ?? enableMultiRowSelection) &&
|
||||||
|
<th className='pl-3 pr-1'>
|
||||||
|
<Tristate
|
||||||
|
tabIndex={-1}
|
||||||
|
value={
|
||||||
|
!tableImpl.getIsAllPageRowsSelected() && tableImpl.getIsSomePageRowsSelected() ? null :
|
||||||
|
tableImpl.getIsAllPageRowsSelected()
|
||||||
|
}
|
||||||
|
tooltip='Выделить все'
|
||||||
|
setValue={value => tableImpl.toggleAllPageRowsSelected(value !== false)}
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
|
}
|
||||||
|
{headerGroup.headers.map(
|
||||||
|
(header: Header<TData, unknown>) => (
|
||||||
|
<th key={header.id}
|
||||||
|
colSpan={header.colSpan}
|
||||||
|
className='p-2 text-xs font-semibold select-none whitespace-nowrap'
|
||||||
|
style={{
|
||||||
|
textAlign: header.getSize() > 100 ? 'left': 'center',
|
||||||
|
width: header.getSize()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* {header.isPlaceholder ? null : (
|
||||||
|
<div
|
||||||
|
{...{
|
||||||
|
className: header.column.getCanSort()
|
||||||
|
? 'cursor-pointer select-none'
|
||||||
|
: '',
|
||||||
|
onClick: header.column.getToggleSortingHandler(),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
{{
|
||||||
|
asc: ' 🔼',
|
||||||
|
desc: ' 🔽',
|
||||||
|
}[header.column.getIsSorted() as string] ?? null}
|
||||||
|
</div>
|
||||||
|
)} */}
|
||||||
|
{header.isPlaceholder ? null
|
||||||
|
: flexRender(header.column.columnDef.header, header.getContext())
|
||||||
|
}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{tableImpl.getRowModel().rows.map(
|
||||||
|
(row: Row<TData>) => (
|
||||||
|
<tr
|
||||||
|
key={row.id}
|
||||||
|
className={
|
||||||
|
row.getIsSelected() ? 'clr-selected clr-hover' :
|
||||||
|
row.index % 2 === 0 ? 'clr-controls clr-hover' :
|
||||||
|
'clr-app clr-hover'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{(enableRowSelection ?? enableMultiRowSelection) &&
|
||||||
|
<td className='pl-3 pr-1 border-y'>
|
||||||
|
<Checkbox
|
||||||
|
tabIndex={-1}
|
||||||
|
value={row.getIsSelected()}
|
||||||
|
setValue={row.getToggleSelectedHandler()}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
}
|
||||||
|
{row.getVisibleCells().map(
|
||||||
|
(cell: Cell<TData, unknown>) => (
|
||||||
|
<td
|
||||||
|
key={cell.id}
|
||||||
|
className='px-2 py-1 border-y'
|
||||||
|
style={{
|
||||||
|
cursor: onRowClicked || onRowDoubleClicked ? 'pointer': 'auto'
|
||||||
|
}}
|
||||||
|
onClick={event => onRowClicked && onRowClicked(row.original, event)}
|
||||||
|
onDoubleClick={event => onRowDoubleClicked && onRowDoubleClicked(row.original, event)}
|
||||||
|
>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
<tfoot>
|
||||||
|
{tableImpl.getFooterGroups().map(
|
||||||
|
(footerGroup: HeaderGroup<TData>) => (
|
||||||
|
<tr key={footerGroup.id}>
|
||||||
|
{footerGroup.headers.map(
|
||||||
|
(header: Header<TData, unknown>) => (
|
||||||
|
<th key={header.id}>
|
||||||
|
{header.isPlaceholder ? null
|
||||||
|
: flexRender(header.column.columnDef.footer, header.getContext())
|
||||||
|
}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tfoot>
|
||||||
|
</table>}
|
||||||
|
{/*
|
||||||
|
<div className="h-2" />
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<button
|
||||||
|
className="p-1 border rounded"
|
||||||
|
onClick={() => table.setPageIndex(0)}
|
||||||
|
disabled={!table.getCanPreviousPage()}
|
||||||
|
>
|
||||||
|
{'<<'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="p-1 border rounded"
|
||||||
|
onClick={() => table.previousPage()}
|
||||||
|
disabled={!table.getCanPreviousPage()}
|
||||||
|
>
|
||||||
|
{'<'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="p-1 border rounded"
|
||||||
|
onClick={() => table.nextPage()}
|
||||||
|
disabled={!table.getCanNextPage()}
|
||||||
|
>
|
||||||
|
{'>'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="p-1 border rounded"
|
||||||
|
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||||
|
disabled={!table.getCanNextPage()}
|
||||||
|
>
|
||||||
|
{'>>'}
|
||||||
|
</button>
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<div>Page</div>
|
||||||
|
<strong>
|
||||||
|
{table.getState().pagination.pageIndex + 1} of{' '}
|
||||||
|
{table.getPageCount()}
|
||||||
|
</strong>
|
||||||
|
</span>
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
| Go to page:
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
defaultValue={table.getState().pagination.pageIndex + 1}
|
||||||
|
onChange={e => {
|
||||||
|
const page = e.target.value ? Number(e.target.value) - 1 : 0
|
||||||
|
table.setPageIndex(page)
|
||||||
|
}}
|
||||||
|
className="w-16 p-1 border rounded"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<select
|
||||||
|
value={table.getState().pagination.pageSize}
|
||||||
|
onChange={e => {
|
||||||
|
table.setPageSize(Number(e.target.value))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{[10, 20, 30, 40, 50].map(pageSize => (
|
||||||
|
<option key={pageSize} value={pageSize}>
|
||||||
|
Show {pageSize}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="h-4" />
|
||||||
|
<button onClick={() => rerender()} className="p-2 border">
|
||||||
|
Rerender
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) */}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// import { TableOptions, useReactTable } from '@tanstack/react-table'
|
||||||
|
|
||||||
|
// import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
|
// import { dataTableDarkT, dataTableLightT } from '../../utils/color';
|
||||||
|
|
||||||
|
// export interface SelectionInfo<T> {
|
||||||
|
// allSelected: boolean
|
||||||
|
// selectedCount: number
|
||||||
|
// selectedRows: T[]
|
||||||
|
// }
|
||||||
|
|
||||||
|
// interface DataTableProps<T>
|
||||||
|
// extends TableOptions<T>{}
|
||||||
|
|
||||||
|
// function DataTable<T>({ ...props }: DataTableProps<T>) {
|
||||||
|
// const { darkMode } = useConceptTheme();
|
||||||
|
// const table = useReactTable(props);
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <DataTable<T>
|
||||||
|
// theme={ theme ?? (darkMode ? 'customDark' : 'customLight')}
|
||||||
|
// paginationComponentOptions={{
|
||||||
|
// rowsPerPageText: 'строк на страницу'
|
||||||
|
// }}
|
||||||
|
// {...props}
|
||||||
|
// />
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export default DataTable;
|
|
@ -21,7 +21,7 @@ function TextArea({
|
||||||
<div className='flex flex-col items-start [&:not(:first-child)]:mt-3'>
|
<div className='flex flex-col items-start [&:not(:first-child)]:mt-3'>
|
||||||
<Label
|
<Label
|
||||||
text={label}
|
text={label}
|
||||||
required={required}
|
required={!props.disabled && required}
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
/>
|
/>
|
||||||
<textarea id={id}
|
<textarea id={id}
|
||||||
|
|
|
@ -20,7 +20,7 @@ function TextInput({
|
||||||
<div className={`flex [&:not(:first-child)]:mt-3 ${singleRow ? 'items-center gap-4 ' + widthClass : 'flex-col items-start'}`}>
|
<div className={`flex [&:not(:first-child)]:mt-3 ${singleRow ? 'items-center gap-4 ' + widthClass : 'flex-col items-start'}`}>
|
||||||
<Label
|
<Label
|
||||||
text={label}
|
text={label}
|
||||||
required={required}
|
required={!props.disabled && required}
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
/>
|
/>
|
||||||
<input id={id}
|
<input id={id}
|
||||||
|
|
|
@ -11,7 +11,10 @@ extends Omit<CheckboxProps, 'value' | 'setValue'> {
|
||||||
setValue?: (newValue: boolean | null) => void
|
setValue?: (newValue: boolean | null) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-fit', value, setValue }: TristateProps) {
|
function Tristate({
|
||||||
|
id, required, disabled, tooltip, label,
|
||||||
|
widthClass = 'w-fit', value, setValue, ...props
|
||||||
|
}: TristateProps) {
|
||||||
const cursor = useMemo(
|
const cursor = useMemo(
|
||||||
() => {
|
() => {
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
|
@ -33,9 +36,11 @@ function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-fit'
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (value === false) {
|
if (value === false) {
|
||||||
setValue(null);
|
setValue(null);
|
||||||
|
} else if (value === null) {
|
||||||
|
setValue(true);
|
||||||
} else {
|
} else {
|
||||||
setValue(!value);
|
setValue(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,8 +51,12 @@ function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-fit'
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
|
{...props}
|
||||||
>
|
>
|
||||||
<div className={`relative peer w-4 h-4 shrink-0 mt-0.5 border rounded-sm appearance-none ${bgColor} ${cursor}`} />
|
<div className={`w-4 h-4 shrink-0 mt-0.5 border rounded-sm ${bgColor} ${cursor}`} >
|
||||||
|
{ value && <div className='mt-[1px] ml-[1px]'><CheckboxChecked /></div>}
|
||||||
|
{ value == null && <div className='mt-[1px] ml-[1px]'><CheckboxNull /></div>}
|
||||||
|
</div>
|
||||||
{ label &&
|
{ label &&
|
||||||
<Label
|
<Label
|
||||||
className={`${cursor} px-2 text-start`}
|
className={`${cursor} px-2 text-start`}
|
||||||
|
@ -55,10 +64,8 @@ function Checkbox({ id, required, disabled, tooltip, label, widthClass = 'w-fit'
|
||||||
required={required}
|
required={required}
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
/>}
|
/>}
|
||||||
{value && <CheckboxChecked />}
|
|
||||||
{value === null && <CheckboxNull />}
|
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Checkbox;
|
export default Tristate;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
import { prefixes } from '../../utils/constants';
|
import { prefixes } from '../../utils/constants';
|
||||||
import { getCstStatusColor, mapStatusInfo } from '../../utils/staticUI';
|
import { getCstStatusBgColor, mapStatusInfo } from '../../utils/staticUI';
|
||||||
|
|
||||||
interface InfoCstStatusProps {
|
interface InfoCstStatusProps {
|
||||||
title?: string
|
title?: string
|
||||||
|
@ -18,7 +18,7 @@ function InfoCstStatus({ title }: InfoCstStatusProps) {
|
||||||
<p key={`${prefixes.cst_status_list}${index}`}>
|
<p key={`${prefixes.cst_status_list}${index}`}>
|
||||||
<span
|
<span
|
||||||
className='px-1 inline-block font-semibold min-w-[5rem] text-center border text-sm'
|
className='px-1 inline-block font-semibold min-w-[5rem] text-center border text-sm'
|
||||||
style={{backgroundColor: getCstStatusColor(status, colors)}}
|
style={{backgroundColor: getCstStatusBgColor(status, colors)}}
|
||||||
>
|
>
|
||||||
{info.text}
|
{info.text}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -321,7 +321,7 @@ export function InDoor(props: IconProps) {
|
||||||
export function CheckboxChecked() {
|
export function CheckboxChecked() {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
className='absolute w-3 h-3 mt-1 ml-0.5'
|
className='w-3 h-3'
|
||||||
viewBox='0 0 512 512'
|
viewBox='0 0 512 512'
|
||||||
fill='#ffffff'
|
fill='#ffffff'
|
||||||
>
|
>
|
||||||
|
@ -333,8 +333,8 @@ export function CheckboxChecked() {
|
||||||
export function CheckboxNull() {
|
export function CheckboxNull() {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
className='absolute w-3 h-3 mt-1 ml-0.5'
|
className='w-3 h-3'
|
||||||
viewBox='0 0 512 512'
|
viewBox='0 0 16 16'
|
||||||
fill='#ffffff'
|
fill='#ffffff'
|
||||||
>
|
>
|
||||||
<path d='M2 7.75A.75.75 0 012.75 7h10a.75.75 0 010 1.5h-10A.75.75 0 012 7.75z' />
|
<path d='M2 7.75A.75.75 0 012.75 7h10a.75.75 0 010 1.5h-10A.75.75 0 012 7.75z' />
|
||||||
|
|
|
@ -7,18 +7,18 @@
|
||||||
:root {
|
:root {
|
||||||
/* Light Theme */
|
/* Light Theme */
|
||||||
--cl-bg-120: hsl(000, 000%, 100%);
|
--cl-bg-120: hsl(000, 000%, 100%);
|
||||||
--cl-bg-100: hsl(220, 020%, 098%);
|
--cl-bg-100: hsl(000, 000%, 098%);
|
||||||
--cl-bg-80: hsl(220, 014%, 096%);
|
--cl-bg-80: hsl(000, 000%, 094%);
|
||||||
--cl-bg-60: hsl(220, 013%, 091%);
|
--cl-bg-60: hsl(000, 000%, 091%);
|
||||||
--cl-bg-40: hsl(216, 012%, 084%);
|
--cl-bg-40: hsl(000, 000%, 080%);
|
||||||
|
|
||||||
--cl-fg-60: hsl(000, 000%, 055%);
|
--cl-fg-60: hsl(000, 000%, 055%);
|
||||||
--cl-fg-80: hsl(000, 000%, 047%);
|
--cl-fg-80: hsl(000, 000%, 047%);
|
||||||
--cl-fg-100: hsl(000, 000%, 000%);
|
--cl-fg-100: hsl(000, 000%, 000%);
|
||||||
|
|
||||||
--cl-prim-bg-100: hsl(220, 100%, 060%);
|
--cl-prim-bg-100: hsl(220, 100%, 060%);
|
||||||
--cl-prim-bg-80: hsl(220, 100%, 090%);
|
--cl-prim-bg-80: hsl(220, 080%, 092%);
|
||||||
--cl-prim-bg-60: hsl(220, 100%, 094%);
|
--cl-prim-bg-60: hsl(190, 080%, 094%);
|
||||||
|
|
||||||
--cl-prim-fg-80: hsl(220, 100%, 050%);
|
--cl-prim-fg-80: hsl(220, 100%, 050%);
|
||||||
--cl-prim-fg-100: hsl(000, 000%, 100%);
|
--cl-prim-fg-100: hsl(000, 000%, 100%);
|
||||||
|
@ -38,24 +38,16 @@
|
||||||
--cd-fg-80: hsl(000, 000%, 080%);
|
--cd-fg-80: hsl(000, 000%, 080%);
|
||||||
--cd-fg-100: hsl(000, 000%, 093%);
|
--cd-fg-100: hsl(000, 000%, 093%);
|
||||||
|
|
||||||
/* --cd-prim-bg-100: hsl(025, 079%, 052%);
|
--cd-prim-bg-100: hsl(267, 050%, 050%);
|
||||||
--cd-prim-bg-80: hsl(035, 080%, 043%);
|
--cd-prim-bg-80: hsl(267, 050%, 032%);
|
||||||
--cd-prim-bg-60: hsl(045, 080%, 031%);
|
--cd-prim-bg-60: hsl(269, 030%, 028%);
|
||||||
|
|
||||||
--cd-prim-fg-80: hsl(025, 080%, 050%);
|
--cd-prim-fg-80: hsl(267, 070%, 070%);
|
||||||
--cd-prim-fg-100: hsl(000, 000%, 100%); */
|
--cd-prim-fg-100: hsl(000, 000%, 100%);
|
||||||
|
|
||||||
--cd-prim-bg-100: hsl(267, 50%, 50%);
|
|
||||||
--cd-prim-bg-80: hsl(267, 50%, 35%);
|
|
||||||
--cd-prim-bg-60: hsl(269, 50%, 20%);
|
|
||||||
|
|
||||||
--cd-prim-fg-60: hsl(267, 50%, 35%);
|
|
||||||
--cd-prim-fg-80: hsl(267, 50%, 50%);
|
|
||||||
--cd-prim-fg-100: #ffffff;
|
|
||||||
|
|
||||||
--cd-red-bg-100: hsl(000, 100%, 015%);
|
--cd-red-bg-100: hsl(000, 100%, 015%);
|
||||||
--cd-red-fg-100: hsl(000, 100%, 060%);
|
--cd-red-fg-100: hsl(000, 080%, 055%);
|
||||||
--cd-green-fg-100: hsl(120, 80%, 40%);
|
--cd-green-fg-100: hsl(120, 080%, 040%);
|
||||||
|
|
||||||
/* Import overrides */
|
/* Import overrides */
|
||||||
--toastify-color-dark: var(--cd-bg-60);
|
--toastify-color-dark: var(--cd-bg-60);
|
||||||
|
@ -246,14 +238,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.clr-btn-nav {
|
:is(.text-controls,
|
||||||
color: var(--cl-fg-80);
|
.clr-btn-nav,
|
||||||
.dark & {
|
.clr-btn-clear
|
||||||
color: var(--cd-fg-80);
|
) {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.clr-btn-clear {
|
|
||||||
color: var(--cl-fg-80);
|
color: var(--cl-fg-80);
|
||||||
&:disabled {
|
&:disabled {
|
||||||
color: var(--cl-fg-60);
|
color: var(--cl-fg-60);
|
||||||
|
|
|
@ -26,7 +26,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
||||||
return (
|
return (
|
||||||
<div ref={pickerMenu.ref} className='h-full text-right'>
|
<div ref={pickerMenu.ref} className='h-full text-right'>
|
||||||
<Button
|
<Button
|
||||||
icon={<FilterCogIcon size={6} />}
|
icon={<FilterCogIcon color='text-controls' size={6} />}
|
||||||
dense
|
dense
|
||||||
tooltip='Фильтры'
|
tooltip='Фильтры'
|
||||||
colorClass='clr-input clr-hover text-btn'
|
colorClass='clr-input clr-hover text-btn'
|
||||||
|
|
|
@ -37,16 +37,14 @@ function SearchPanel({ total, filtered, setFilter }: SearchPanelProps) {
|
||||||
function handleChangeQuery(event: React.ChangeEvent<HTMLInputElement>) {
|
function handleChangeQuery(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
const newQuery = event.target.value;
|
const newQuery = event.target.value;
|
||||||
setQuery(newQuery);
|
setQuery(newQuery);
|
||||||
setFilter(prev => {
|
setFilter(prev => ({
|
||||||
return {
|
query: newQuery,
|
||||||
query: newQuery,
|
is_owned: prev.is_owned,
|
||||||
is_owned: prev.is_owned,
|
is_common: prev.is_common,
|
||||||
is_common: prev.is_common,
|
is_canonical: prev.is_canonical,
|
||||||
is_canonical: prev.is_canonical,
|
is_subscribed: prev.is_subscribed,
|
||||||
is_subscribed: prev.is_subscribed,
|
is_personal: prev.is_personal
|
||||||
is_personal: prev.is_personal
|
}));
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
|
@ -83,7 +81,7 @@ function SearchPanel({ total, filtered, setFilter }: SearchPanelProps) {
|
||||||
onChange={handleChangeStrategy}
|
onChange={handleChangeStrategy}
|
||||||
/>
|
/>
|
||||||
<div className='relative w-96 min-w-[10rem]'>
|
<div className='relative w-96 min-w-[10rem]'>
|
||||||
<div className='absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none'>
|
<div className='absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none text-controls'>
|
||||||
<MagnifyingGlassIcon />
|
<MagnifyingGlassIcon />
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
import { createColumnHelper } from '@tanstack/react-table';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
import ConceptDataTable from '../../components/Common/ConceptDataTable';
|
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
||||||
|
import DataTable from '../../components/Common/DataTable';
|
||||||
import TextURL from '../../components/Common/TextURL';
|
import TextURL from '../../components/Common/TextURL';
|
||||||
import HelpLibrary from '../../components/Help/HelpLibrary';
|
import HelpLibrary from '../../components/Help/HelpLibrary';
|
||||||
import { EducationIcon, EyeIcon, GroupIcon, HelpIcon } from '../../components/Icons';
|
import { EducationIcon, EyeIcon, GroupIcon, HelpIcon } from '../../components/Icons';
|
||||||
|
@ -17,6 +18,8 @@ interface ViewLibraryProps {
|
||||||
cleanQuery: () => void
|
cleanQuery: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<ILibraryItem>();
|
||||||
|
|
||||||
function ViewLibrary({ items, cleanQuery }: ViewLibraryProps) {
|
function ViewLibrary({ items, cleanQuery }: ViewLibraryProps) {
|
||||||
const { navigateTo } = useConceptNavigation();
|
const { navigateTo } = useConceptNavigation();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
@ -27,12 +30,13 @@ function ViewLibrary({ items, cleanQuery }: ViewLibraryProps) {
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
columnHelper.display({
|
||||||
name: '',
|
|
||||||
id: 'status',
|
id: 'status',
|
||||||
minWidth: '60px',
|
header: '',
|
||||||
maxWidth: '60px',
|
size: 60,
|
||||||
cell: (item: ILibraryItem) => {
|
maxSize: 60,
|
||||||
|
cell: props => {
|
||||||
|
const item = props.row.original;
|
||||||
return (<>
|
return (<>
|
||||||
<div
|
<div
|
||||||
className='flex items-center justify-start gap-1'
|
className='flex items-center justify-start gap-1'
|
||||||
|
@ -44,51 +48,50 @@ function ViewLibrary({ items, cleanQuery }: ViewLibraryProps) {
|
||||||
</div>
|
</div>
|
||||||
</>);
|
</>);
|
||||||
},
|
},
|
||||||
sortable: true,
|
}),
|
||||||
reorder: true
|
columnHelper.accessor('alias', {
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Шифр',
|
|
||||||
id: 'alias',
|
id: 'alias',
|
||||||
maxWidth: '140px',
|
header: 'Шифр',
|
||||||
selector: (item: ILibraryItem) => item.alias,
|
size: 200,
|
||||||
sortable: true,
|
minSize: 200,
|
||||||
reorder: true
|
maxSize: 200,
|
||||||
},
|
enableSorting: true
|
||||||
{
|
}),
|
||||||
name: 'Название',
|
columnHelper.accessor('title', {
|
||||||
id: 'title',
|
id: 'title',
|
||||||
minWidth: '50%',
|
header: 'Название',
|
||||||
selector: (item: ILibraryItem) => item.title,
|
minSize: 200,
|
||||||
sortable: true,
|
size: 1000,
|
||||||
reorder: true
|
maxSize: 1000,
|
||||||
},
|
enableSorting: true
|
||||||
{
|
}),
|
||||||
name: 'Владелец',
|
columnHelper.accessor(item => item.owner ?? 0, {
|
||||||
id: 'owner',
|
id: 'owner',
|
||||||
selector: (item: ILibraryItem) => item.owner ?? 0,
|
header: 'Владелец',
|
||||||
format: (item: ILibraryItem) => {
|
cell: props => getUserLabel(props.cell.getValue()),
|
||||||
return getUserLabel(item.owner);
|
enableSorting: true,
|
||||||
},
|
enableResizing: false,
|
||||||
sortable: true,
|
minSize: 200,
|
||||||
reorder: true
|
size: 300,
|
||||||
},
|
maxSize: 300
|
||||||
{
|
}),
|
||||||
name: 'Обновлена',
|
columnHelper.accessor('time_update', {
|
||||||
id: 'time_update',
|
id: 'time_update',
|
||||||
selector: (item: ILibraryItem) => item.time_update,
|
header: 'Обновлена',
|
||||||
format: (item: ILibraryItem) => new Date(item.time_update).toLocaleString(intl.locale),
|
minSize: 200,
|
||||||
sortable: true,
|
size: 200,
|
||||||
reorder: true
|
maxSize: 200,
|
||||||
}
|
cell: props => new Date(props.cell.getValue()).toLocaleString(intl.locale),
|
||||||
|
enableSorting: true
|
||||||
|
})
|
||||||
], [intl, getUserLabel, user]);
|
], [intl, getUserLabel, user]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className='relative w-full'>
|
<div className='relative w-full'>
|
||||||
<div className='absolute top-0 left-0 flex gap-1 mt-1 ml-5 z-pop'>
|
<div className='absolute top-[-0.125rem] left-0 flex gap-1 ml-3 z-pop'>
|
||||||
<div id='library-help' className='py-2'>
|
<div id='library-help' className='py-2'>
|
||||||
<HelpIcon color='text-primary' size={6} />
|
<HelpIcon color='text-primary' size={5} />
|
||||||
</div>
|
</div>
|
||||||
<ConceptTooltip anchorSelect='#library-help'>
|
<ConceptTooltip anchorSelect='#library-help'>
|
||||||
<div className='max-w-[35rem]'>
|
<div className='max-w-[35rem]'>
|
||||||
|
@ -97,14 +100,11 @@ function ViewLibrary({ items, cleanQuery }: ViewLibraryProps) {
|
||||||
</ConceptTooltip>
|
</ConceptTooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ConceptDataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={items}
|
data={items}
|
||||||
defaultSortFieldId='time_update'
|
// defaultSortFieldId='time_update'
|
||||||
defaultSortAsc={false}
|
// defaultSortAsc={false}
|
||||||
striped
|
|
||||||
highlightOnHover
|
|
||||||
pointerOnHover
|
|
||||||
|
|
||||||
noDataComponent={
|
noDataComponent={
|
||||||
<div className='flex flex-col gap-4 justify-center p-2 text-center min-h-[10rem]'>
|
<div className='flex flex-col gap-4 justify-center p-2 text-center min-h-[10rem]'>
|
||||||
|
@ -120,10 +120,9 @@ function ViewLibrary({ items, cleanQuery }: ViewLibraryProps) {
|
||||||
</p>
|
</p>
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
|
// pagination
|
||||||
pagination
|
// paginationPerPage={50}
|
||||||
paginationPerPage={50}
|
// paginationRowsPerPageOptions={[10, 20, 30, 50, 100]}
|
||||||
paginationRowsPerPageOptions={[10, 20, 30, 50, 100]}
|
|
||||||
onRowClicked={openRSForm}
|
onRowClicked={openRSForm}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,18 +18,15 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
||||||
const { darkMode, colors } = useConceptTheme();
|
const { darkMode, colors } = useConceptTheme();
|
||||||
const [hoverID, setHoverID] = useState<number | undefined>(undefined);
|
const [hoverID, setHoverID] = useState<number | undefined>(undefined);
|
||||||
const hoverNode = useMemo(
|
const hoverNode = useMemo(
|
||||||
() => {
|
() => syntaxTree.find(node => node.uid === hoverID)
|
||||||
return syntaxTree.find(node => node.uid === hoverID);
|
, [hoverID, syntaxTree]);
|
||||||
}, [hoverID, syntaxTree]);
|
|
||||||
|
|
||||||
const nodes: GraphNode[] = useMemo(
|
const nodes: GraphNode[] = useMemo(
|
||||||
() => syntaxTree.map(node => {
|
() => syntaxTree.map(node => ({
|
||||||
return {
|
id: String(node.uid),
|
||||||
id: String(node.uid),
|
label: getASTNodeLabel(node),
|
||||||
label: getASTNodeLabel(node),
|
fill: getASTNodeColor(node, colors),
|
||||||
fill: getASTNodeColor(node, colors),
|
})), [syntaxTree, colors]);
|
||||||
};
|
|
||||||
}), [syntaxTree, colors]);
|
|
||||||
|
|
||||||
const edges: GraphEdge[] = useMemo(
|
const edges: GraphEdge[] = useMemo(
|
||||||
() => {
|
() => {
|
||||||
|
@ -47,14 +44,12 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
||||||
}, [syntaxTree]);
|
}, [syntaxTree]);
|
||||||
|
|
||||||
const handleHoverIn = useCallback(
|
const handleHoverIn = useCallback(
|
||||||
(node: GraphNode) => {
|
(node: GraphNode) => setHoverID(Number(node.id))
|
||||||
setHoverID(Number(node.id));
|
, []);
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleHoverOut = useCallback(
|
const handleHoverOut = useCallback(
|
||||||
() => {
|
() => setHoverID(undefined)
|
||||||
setHoverID(undefined);
|
, []);
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { createColumnHelper,RowSelectionState } from '@tanstack/react-table';
|
||||||
|
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import Button from '../../components/Common/Button';
|
import Button from '../../components/Common/Button';
|
||||||
import ConceptDataTable from '../../components/Common/ConceptDataTable';
|
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
||||||
|
import DataTable from '../../components/Common/DataTable';
|
||||||
import Divider from '../../components/Common/Divider';
|
import Divider from '../../components/Common/Divider';
|
||||||
import HelpRSFormItems from '../../components/Help/HelpRSFormItems';
|
import HelpRSFormItems from '../../components/Help/HelpRSFormItems';
|
||||||
import { ArrowDownIcon, ArrowUpIcon, DumpBinIcon, HelpIcon, MeshIcon, SmallPlusIcon } from '../../components/Icons';
|
import { ArrowDownIcon, ArrowUpIcon, DumpBinIcon, HelpIcon, MeshIcon, SmallPlusIcon } from '../../components/Icons';
|
||||||
|
@ -11,7 +12,9 @@ import { useRSForm } from '../../context/RSFormContext';
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
import { prefixes } from '../../utils/constants';
|
import { prefixes } from '../../utils/constants';
|
||||||
import { CstType, IConstituenta, ICstCreateData, ICstMovetoData } from '../../utils/models'
|
import { CstType, IConstituenta, ICstCreateData, ICstMovetoData } from '../../utils/models'
|
||||||
import { getCstStatusColor, getCstTypePrefix, getCstTypeShortcut, getCstTypificationLabel, mapStatusInfo } from '../../utils/staticUI';
|
import { getCstStatusFgColor, getCstTypePrefix, getCstTypeShortcut, getCstTypificationLabel, mapStatusInfo } from '../../utils/staticUI';
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<IConstituenta>();
|
||||||
|
|
||||||
interface EditorItemsProps {
|
interface EditorItemsProps {
|
||||||
onOpenEdit: (cstID: number) => void
|
onOpenEdit: (cstID: number) => void
|
||||||
|
@ -25,7 +28,7 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
||||||
const [selected, setSelected] = useState<number[]>([]);
|
const [selected, setSelected] = useState<number[]>([]);
|
||||||
const nothingSelected = useMemo(() => selected.length === 0, [selected]);
|
const nothingSelected = useMemo(() => selected.length === 0, [selected]);
|
||||||
|
|
||||||
const [toggledClearRows, setToggledClearRows] = useState(false);
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||||
|
|
||||||
// Delete selected constituents
|
// Delete selected constituents
|
||||||
function handleDelete() {
|
function handleDelete() {
|
||||||
|
@ -33,8 +36,7 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onDeleteCst(selected, () => {
|
onDeleteCst(selected, () => {
|
||||||
setToggledClearRows(prev => !prev);
|
setRowSelection({});
|
||||||
setSelected([]);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,12 +55,16 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
||||||
}, -1);
|
}, -1);
|
||||||
const target = Math.max(0, currentIndex - 1) + 1
|
const target = Math.max(0, currentIndex - 1) + 1
|
||||||
const data = {
|
const data = {
|
||||||
items: selected.map(id => {
|
items: selected.map(id => ({ id: id })),
|
||||||
return { id: id };
|
|
||||||
}),
|
|
||||||
move_to: target
|
move_to: target
|
||||||
}
|
}
|
||||||
cstMoveTo(data);
|
cstMoveTo(data, () => {
|
||||||
|
const newSelection: RowSelectionState = {};
|
||||||
|
selected.forEach((_, index) => {
|
||||||
|
newSelection[String(target + index - 1)] = true;
|
||||||
|
})
|
||||||
|
setRowSelection(newSelection);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move selected cst down
|
// Move selected cst down
|
||||||
|
@ -80,12 +86,16 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
||||||
}, -1);
|
}, -1);
|
||||||
const target = Math.min(schema.items.length - 1, currentIndex - count + 2) + 1
|
const target = Math.min(schema.items.length - 1, currentIndex - count + 2) + 1
|
||||||
const data: ICstMovetoData = {
|
const data: ICstMovetoData = {
|
||||||
items: selected.map(id => {
|
items: selected.map(id => ({ id: id })),
|
||||||
return { id: id };
|
|
||||||
}),
|
|
||||||
move_to: target
|
move_to: target
|
||||||
}
|
}
|
||||||
cstMoveTo(data);
|
cstMoveTo(data, () => {
|
||||||
|
const newSelection: RowSelectionState = {};
|
||||||
|
selected.forEach((_, index) => {
|
||||||
|
newSelection[String(target + index - 1)] = true;
|
||||||
|
})
|
||||||
|
setRowSelection(newSelection);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate new names for all constituents
|
// Generate new names for all constituents
|
||||||
|
@ -156,41 +166,53 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
||||||
const handleRowClicked = useCallback(
|
const handleRowClicked = useCallback(
|
||||||
(cst: IConstituenta, event: React.MouseEvent<Element, MouseEvent>) => {
|
(cst: IConstituenta, event: React.MouseEvent<Element, MouseEvent>) => {
|
||||||
if (event.altKey) {
|
if (event.altKey) {
|
||||||
|
event.preventDefault();
|
||||||
onOpenEdit(cst.id);
|
onOpenEdit(cst.id);
|
||||||
}
|
}
|
||||||
}, [onOpenEdit]);
|
}, [onOpenEdit]);
|
||||||
|
|
||||||
|
const handleRowDoubleClicked = useCallback(
|
||||||
|
(cst: IConstituenta, event: React.MouseEvent<Element, MouseEvent>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
onOpenEdit(cst.id);
|
||||||
|
}, [onOpenEdit]);
|
||||||
|
|
||||||
const handleSelectionChange = useCallback(
|
useLayoutEffect(
|
||||||
({ selectedRows }: {
|
() => {
|
||||||
allSelected: boolean
|
if (!schema || Object.keys(rowSelection).length === 0) {
|
||||||
selectedCount: number
|
setSelected([]);
|
||||||
selectedRows: IConstituenta[]
|
} else {
|
||||||
}) => {
|
const selected: number[] = [];
|
||||||
setSelected(selectedRows.map(cst => cst.id));
|
schema.items.forEach((cst, index) => {
|
||||||
}, [setSelected]);
|
if (rowSelection[String(index)] === true) {
|
||||||
|
selected.push(cst.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setSelected(selected);
|
||||||
|
}
|
||||||
|
}, [rowSelection, schema]);
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
columnHelper.accessor('alias', {
|
||||||
name: 'ID',
|
|
||||||
id: 'id',
|
|
||||||
selector: (cst: IConstituenta) => cst.id,
|
|
||||||
omit: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Имя',
|
|
||||||
id: 'alias',
|
id: 'alias',
|
||||||
cell: (cst: IConstituenta) => {
|
header: 'Имя',
|
||||||
const info = mapStatusInfo.get(cst.status)!;
|
size: 65,
|
||||||
|
minSize: 65,
|
||||||
|
cell: props => {
|
||||||
|
const cst = props.row.original;
|
||||||
|
const info = mapStatusInfo.get(cst.status);
|
||||||
return (<>
|
return (<>
|
||||||
<div
|
<div
|
||||||
id={`${prefixes.cst_list}${cst.alias}`}
|
id={`${prefixes.cst_list}${cst.alias}`}
|
||||||
className='w-full px-1 text-center rounded-md whitespace-nowrap'
|
className='w-full px-1 text-center rounded-md whitespace-nowrap'
|
||||||
style={{borderWidth: "1px",
|
style={{
|
||||||
borderColor: getCstStatusColor(cst.status, colors),
|
borderWidth: "1px",
|
||||||
color: getCstStatusColor(cst.status, colors),
|
borderColor: getCstStatusFgColor(cst.status, colors),
|
||||||
|
color: getCstStatusFgColor(cst.status, colors),
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
backgroundColor: colors.bgDefault}}
|
backgroundColor: colors.bgInput
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{cst.alias}
|
{cst.alias}
|
||||||
</div>
|
</div>
|
||||||
|
@ -198,68 +220,64 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
||||||
anchorSelect={`#${prefixes.cst_list}${cst.alias}`}
|
anchorSelect={`#${prefixes.cst_list}${cst.alias}`}
|
||||||
place='right'
|
place='right'
|
||||||
>
|
>
|
||||||
<p><b>Статус: </b> {info.tooltip}</p>
|
<p><span className='font-semibold'>Статус</span>: {info!.tooltip}</p>
|
||||||
</ConceptTooltip>
|
</ConceptTooltip>
|
||||||
</>);
|
</>);
|
||||||
},
|
}
|
||||||
width: '65px',
|
}),
|
||||||
maxWidth: '65px',
|
columnHelper.accessor(cst => getCstTypificationLabel(cst), {
|
||||||
reorder: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Типизация',
|
|
||||||
id: 'type',
|
id: 'type',
|
||||||
cell: (cst: IConstituenta) => <div style={{ fontSize: 12 }}>{getCstTypificationLabel(cst)}</div>,
|
header: 'Типизация',
|
||||||
width: '175px',
|
size: 175,
|
||||||
maxWidth: '175px',
|
maxSize: 175,
|
||||||
wrap: true,
|
cell: props => <div style={{ fontSize: 12 }}>{props.getValue()}</div>
|
||||||
reorder: true,
|
}),
|
||||||
hide: 1600
|
columnHelper.accessor(cst => cst.term_resolved || cst.term_raw || '', {
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Термин',
|
|
||||||
id: 'term',
|
id: 'term',
|
||||||
selector: (cst: IConstituenta) => cst.term_resolved || cst.term_raw || '',
|
header: 'Термин',
|
||||||
width: '350px',
|
size: 350,
|
||||||
minWidth: '150px',
|
minSize: 150,
|
||||||
maxWidth: '350px',
|
maxSize: 350
|
||||||
wrap: true,
|
}),
|
||||||
reorder: true
|
columnHelper.accessor('definition_formal', {
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Формальное определение',
|
|
||||||
id: 'expression',
|
id: 'expression',
|
||||||
selector: (cst: IConstituenta) => cst.definition_formal || '',
|
header: 'Формальное определение',
|
||||||
minWidth: '300px',
|
size: 300,
|
||||||
maxWidth: '500px',
|
minSize: 300,
|
||||||
grow: 2,
|
maxSize: 500
|
||||||
wrap: true,
|
}),
|
||||||
reorder: true
|
columnHelper.accessor(cst => cst.definition_resolved || cst.definition_raw || '', {
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Текстовое определение',
|
|
||||||
id: 'definition',
|
id: 'definition',
|
||||||
cell: (cst: IConstituenta) => (
|
header: 'Текстовое определение',
|
||||||
<div style={{ fontSize: 12 }}>
|
size: 200,
|
||||||
{cst.definition_resolved || cst.definition_raw || ''}
|
minSize: 200,
|
||||||
</div>
|
cell: props => <div style={{ fontSize: 12 }}>{props.getValue()}</div>
|
||||||
),
|
}),
|
||||||
minWidth: '200px',
|
columnHelper.accessor('convention', {
|
||||||
grow: 2,
|
|
||||||
wrap: true,
|
|
||||||
reorder: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Конвенция / Комментарий',
|
|
||||||
id: 'convention',
|
id: 'convention',
|
||||||
cell: (cst: IConstituenta) => <div style={{ fontSize: 12 }}>{cst.convention ?? ''}</div>,
|
header: 'Конвенция / Комментарий',
|
||||||
minWidth: '100px',
|
minSize: 100,
|
||||||
wrap: true,
|
maxSize: undefined,
|
||||||
reorder: true,
|
cell: props => <div style={{ fontSize: 12 }}>{props.getValue()}</div>
|
||||||
hide: 1800
|
}),
|
||||||
}
|
|
||||||
], [colors]);
|
], [colors]);
|
||||||
|
|
||||||
|
// name: 'Типизация',
|
||||||
|
// hide: 1600
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'Формальное определение',
|
||||||
|
// grow: 2,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'Текстовое определение',
|
||||||
|
// grow: 2,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'Конвенция / Комментарий',
|
||||||
|
// id: 'convention',
|
||||||
|
// hide: 1800
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
<div
|
<div
|
||||||
|
@ -330,30 +348,30 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
||||||
</ConceptTooltip>
|
</ConceptTooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='w-full h-full' onKeyDown={handleTableKey}>
|
<div className='w-full h-full text-sm' onKeyDown={handleTableKey}>
|
||||||
<ConceptDataTable
|
<DataTable
|
||||||
data={schema?.items ?? []}
|
data={schema?.items ?? []}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
keyField='id'
|
state={{
|
||||||
|
rowSelection: rowSelection
|
||||||
|
}}
|
||||||
|
|
||||||
|
enableMultiRowSelection
|
||||||
|
|
||||||
|
onRowDoubleClicked={handleRowDoubleClicked}
|
||||||
|
onRowClicked={handleRowClicked}
|
||||||
|
onRowSelectionChange={setRowSelection}
|
||||||
|
|
||||||
noDataComponent={
|
noDataComponent={
|
||||||
<span className='flex flex-col justify-center p-2 text-center'>
|
<span className='flex flex-col justify-center p-2 text-center'>
|
||||||
<p>Список пуст</p>
|
<p>Список пуст</p>
|
||||||
<p>Создайте новую конституенту</p>
|
<p
|
||||||
|
className='cursor-pointer text-primary hover:underline'
|
||||||
|
onClick={() => handleCreateCst()}>
|
||||||
|
Создать новую конституенту
|
||||||
|
</p>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
striped
|
|
||||||
highlightOnHover
|
|
||||||
pointerOnHover
|
|
||||||
|
|
||||||
selectableRows
|
|
||||||
selectableRowsHighlight
|
|
||||||
selectableRowsComponentProps={{tabIndex: -1}}
|
|
||||||
onSelectedRowsChange={handleSelectionChange}
|
|
||||||
onRowDoubleClicked={cst => onOpenEdit(cst.id)}
|
|
||||||
onRowClicked={handleRowClicked}
|
|
||||||
clearSelectedRows={toggledClearRows}
|
|
||||||
dense
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { graphDarkT, graphLightT, IColorTheme } from '../../utils/color';
|
||||||
import { prefixes, resources, TIMEOUT_GRAPH_REFRESH } from '../../utils/constants';
|
import { prefixes, resources, TIMEOUT_GRAPH_REFRESH } from '../../utils/constants';
|
||||||
import { Graph } from '../../utils/Graph';
|
import { Graph } from '../../utils/Graph';
|
||||||
import { CstType, IConstituenta, ICstCreateData } from '../../utils/models';
|
import { CstType, IConstituenta, ICstCreateData } from '../../utils/models';
|
||||||
import { getCstClassColor, getCstStatusColor,
|
import { getCstClassColor, getCstStatusBgColor,
|
||||||
GraphColoringSelector, GraphLayoutSelector,
|
GraphColoringSelector, GraphLayoutSelector,
|
||||||
mapColoringLabels, mapLayoutLabels
|
mapColoringLabels, mapLayoutLabels
|
||||||
} from '../../utils/staticUI';
|
} from '../../utils/staticUI';
|
||||||
|
@ -34,7 +34,7 @@ function getCstNodeColor(cst: IConstituenta, coloringScheme: ColoringScheme, col
|
||||||
return getCstClassColor(cst.cst_class, colors);
|
return getCstClassColor(cst.cst_class, colors);
|
||||||
}
|
}
|
||||||
if (coloringScheme === 'status') {
|
if (coloringScheme === 'status') {
|
||||||
return getCstStatusColor(cst.status, colors);
|
return getCstStatusBgColor(cst.status, colors);
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,7 +137,7 @@ function RSTabs() {
|
||||||
if (element) {
|
if (element) {
|
||||||
element.scrollIntoView({
|
element.scrollIntoView({
|
||||||
behavior: 'smooth',
|
behavior: 'smooth',
|
||||||
block: 'end',
|
block: 'nearest',
|
||||||
inline: 'nearest'
|
inline: 'nearest'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -173,9 +173,7 @@ function RSTabs() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const data = {
|
const data = {
|
||||||
items: deleted.map(id => {
|
items: deleted.map(id => ({ id: id }))
|
||||||
return { id: id };
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
let activeIndex = schema.items.findIndex(cst => cst.id === activeID);
|
let activeIndex = schema.items.findIndex(cst => cst.id === activeID);
|
||||||
cstDelete(data, () => {
|
cstDelete(data, () => {
|
||||||
|
@ -345,7 +343,7 @@ function RSTabs() {
|
||||||
showCloneDialog={handleShowClone}
|
showCloneDialog={handleShowClone}
|
||||||
showUploadDialog={() => setShowUpload(true)}
|
showUploadDialog={() => setShowUpload(true)}
|
||||||
/>
|
/>
|
||||||
<ConceptTab className='border-r-2 min-w-[7.8rem]'>Паспорт схемы</ConceptTab>
|
<ConceptTab className='border-x-2 min-w-[7.8rem]'>Паспорт схемы</ConceptTab>
|
||||||
<ConceptTab className='border-r-2 min-w-[10rem] flex justify-between gap-2'>
|
<ConceptTab className='border-r-2 min-w-[10rem] flex justify-between gap-2'>
|
||||||
<span>Конституенты</span>
|
<span>Конституенты</span>
|
||||||
<span>{`${schema.stats?.count_errors ?? 0} | ${schema.stats?.count_all ?? 0}`}</span>
|
<span>{`${schema.stats?.count_errors ?? 0} | ${schema.stats?.count_all ?? 0}`}</span>
|
||||||
|
|
|
@ -71,9 +71,10 @@ function RSTabsMenu({
|
||||||
<div ref={schemaMenu.ref}>
|
<div ref={schemaMenu.ref}>
|
||||||
<Button
|
<Button
|
||||||
tooltip='Действия'
|
tooltip='Действия'
|
||||||
icon={<MenuIcon size={5}/>}
|
icon={<MenuIcon color='text-controls' size={5}/>}
|
||||||
borderClass=''
|
borderClass=''
|
||||||
widthClass='h-full w-fit'
|
widthClass='h-full w-fit'
|
||||||
|
style={{outlineColor: 'transparent'}}
|
||||||
dense
|
dense
|
||||||
onClick={schemaMenu.toggle}
|
onClick={schemaMenu.toggle}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
|
@ -123,6 +124,7 @@ function RSTabsMenu({
|
||||||
tooltip={'измнение: ' + (isEditable ? '[доступно]' : '[запрещено]')}
|
tooltip={'измнение: ' + (isEditable ? '[доступно]' : '[запрещено]')}
|
||||||
borderClass=''
|
borderClass=''
|
||||||
widthClass='h-full w-fit'
|
widthClass='h-full w-fit'
|
||||||
|
style={{outlineColor: 'transparent'}}
|
||||||
icon={<PenIcon size={5} color={isEditable ? 'text-success' : 'text-warning'}/>}
|
icon={<PenIcon size={5} color={isEditable ? 'text-success' : 'text-warning'}/>}
|
||||||
dense
|
dense
|
||||||
onClick={editMenu.toggle}
|
onClick={editMenu.toggle}
|
||||||
|
@ -136,7 +138,7 @@ function RSTabsMenu({
|
||||||
tooltip={!user || !isClaimable ? 'Стать владельцем можно только для общей изменяемой схемы' : ''}
|
tooltip={!user || !isClaimable ? 'Стать владельцем можно только для общей изменяемой схемы' : ''}
|
||||||
>
|
>
|
||||||
<div className='inline-flex items-center gap-1 justify-normal'>
|
<div className='inline-flex items-center gap-1 justify-normal'>
|
||||||
<span className={isOwned ? 'text-success' : ''}><CrownIcon size={4} /></span>
|
<span><CrownIcon size={4} color={isOwned ? 'text-success' : 'text-controls'} /></span>
|
||||||
<p>
|
<p>
|
||||||
{ isOwned && <b>Владелец схемы</b> }
|
{ isOwned && <b>Владелец схемы</b> }
|
||||||
{ !isOwned && <b>Стать владельцем</b> }
|
{ !isOwned && <b>Стать владельцем</b> }
|
||||||
|
@ -165,10 +167,11 @@ function RSTabsMenu({
|
||||||
disabled={processing}
|
disabled={processing}
|
||||||
icon={isTracking
|
icon={isTracking
|
||||||
? <EyeIcon color='text-primary' size={5}/>
|
? <EyeIcon color='text-primary' size={5}/>
|
||||||
: <EyeOffIcon size={5}/>
|
: <EyeOffIcon color='text-controls' size={5}/>
|
||||||
}
|
}
|
||||||
widthClass='h-full w-fit'
|
widthClass='h-full w-fit'
|
||||||
borderClass=''
|
borderClass=''
|
||||||
|
style={{outlineColor: 'transparent'}}
|
||||||
dense
|
dense
|
||||||
onClick={onToggleSubscribe}
|
onClick={onToggleSubscribe}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { useMemo } from 'react';
|
||||||
|
|
||||||
import { useConceptTheme } from '../../../context/ThemeContext';
|
import { useConceptTheme } from '../../../context/ThemeContext';
|
||||||
import { ExpressionStatus, type IConstituenta, IExpressionParse,inferStatus, ParsingStatus } from '../../../utils/models';
|
import { ExpressionStatus, type IConstituenta, IExpressionParse,inferStatus, ParsingStatus } from '../../../utils/models';
|
||||||
import { getCstStatusColor, mapStatusInfo } from '../../../utils/staticUI';
|
import { getCstStatusBgColor, mapStatusInfo } from '../../../utils/staticUI';
|
||||||
|
|
||||||
interface StatusBarProps {
|
interface StatusBarProps {
|
||||||
isModified?: boolean
|
isModified?: boolean
|
||||||
|
@ -27,7 +27,7 @@ function StatusBar({ isModified, constituenta, parseData }: StatusBarProps) {
|
||||||
return (
|
return (
|
||||||
<div title={data.tooltip}
|
<div title={data.tooltip}
|
||||||
className='text-sm h-[1.6rem] w-[10rem] font-semibold inline-flex border items-center select-none justify-center align-middle'
|
className='text-sm h-[1.6rem] w-[10rem] font-semibold inline-flex border items-center select-none justify-center align-middle'
|
||||||
style={{backgroundColor: getCstStatusColor(status, colors)}}
|
style={{backgroundColor: getCstStatusBgColor(status, colors)}}
|
||||||
>
|
>
|
||||||
Статус: [ {data.text} ]
|
Статус: [ {data.text} ]
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
|
import { createColumnHelper } from '@tanstack/react-table';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import ConceptDataTable from '../../../components/Common/ConceptDataTable';
|
import DataTable from '../../../components/Common/DataTable';
|
||||||
import { useRSForm } from '../../../context/RSFormContext';
|
import { useRSForm } from '../../../context/RSFormContext';
|
||||||
import { useConceptTheme } from '../../../context/ThemeContext';
|
import { useConceptTheme } from '../../../context/ThemeContext';
|
||||||
import useLocalStorage from '../../../hooks/useLocalStorage';
|
import useLocalStorage from '../../../hooks/useLocalStorage';
|
||||||
import { prefixes } from '../../../utils/constants';
|
import { prefixes } from '../../../utils/constants';
|
||||||
import { applyGraphFilter, CstMatchMode, CstType, DependencyMode, extractGlobals, IConstituenta, matchConstituenta } from '../../../utils/models';
|
import { applyGraphFilter, CstMatchMode, CstType, DependencyMode, extractGlobals, IConstituenta, matchConstituenta } from '../../../utils/models';
|
||||||
import { getCstDescription, getCstStatusColor, getMockConstituenta } from '../../../utils/staticUI';
|
import { getCstDescription, getCstStatusFgColor, getMockConstituenta } from '../../../utils/staticUI';
|
||||||
import ConstituentaTooltip from './ConstituentaTooltip';
|
import ConstituentaTooltip from './ConstituentaTooltip';
|
||||||
import DependencyModePicker from './DependencyModePicker';
|
import DependencyModePicker from './DependencyModePicker';
|
||||||
import MatchModePicker from './MatchModePicker';
|
import MatchModePicker from './MatchModePicker';
|
||||||
|
@ -25,6 +26,8 @@ function isMockCst(cst: IConstituenta) {
|
||||||
return cst.id <= 0;
|
return cst.id <= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<IConstituenta>();
|
||||||
|
|
||||||
function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }: ViewSideConstituentsProps) {
|
function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }: ViewSideConstituentsProps) {
|
||||||
const { noNavigation, colors } = useConceptTheme();
|
const { noNavigation, colors } = useConceptTheme();
|
||||||
const { schema } = useRSForm();
|
const { schema } = useRSForm();
|
||||||
|
@ -85,75 +88,61 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
||||||
}
|
}
|
||||||
}, [onOpenEdit]);
|
}, [onOpenEdit]);
|
||||||
|
|
||||||
const conditionalRowStyles = useMemo(
|
|
||||||
() => [
|
|
||||||
{
|
|
||||||
when: (cst: IConstituenta) => cst.id === activeID,
|
|
||||||
style: {
|
|
||||||
backgroundColor: colors.bgSelected,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
], [activeID, colors]);
|
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
columnHelper.accessor('alias', {
|
||||||
id: 'id',
|
|
||||||
selector: (cst: IConstituenta) => cst.id,
|
|
||||||
omit: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'ID',
|
|
||||||
id: 'alias',
|
id: 'alias',
|
||||||
cell: (cst: IConstituenta) => {
|
header: 'Имя',
|
||||||
|
size: 65,
|
||||||
|
minSize: 65,
|
||||||
|
cell: props => {
|
||||||
|
const cst = props.row.original;
|
||||||
return (<>
|
return (<>
|
||||||
<div
|
<div
|
||||||
id={`${prefixes.cst_list}${cst.alias}`}
|
id={`${prefixes.cst_list}${cst.alias}`}
|
||||||
className='w-full px-1 text-center rounded-md min-w-fit whitespace-nowrap'
|
className='w-full px-1 text-center rounded-md whitespace-nowrap'
|
||||||
style={{backgroundColor: getCstStatusColor(cst.status, colors)}}
|
style={{
|
||||||
|
borderWidth: '1px',
|
||||||
|
borderColor: getCstStatusFgColor(cst.status, colors),
|
||||||
|
color: getCstStatusFgColor(cst.status, colors),
|
||||||
|
fontWeight: 600,
|
||||||
|
backgroundColor: isMockCst(cst) ? colors.bgWarning : colors.bgInput
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{cst.alias}
|
{cst.alias}
|
||||||
</div>
|
</div>
|
||||||
<ConstituentaTooltip data={cst} anchor={`#${prefixes.cst_list}${cst.alias}`} />
|
<ConstituentaTooltip data={cst} anchor={`#${prefixes.cst_list}${cst.alias}`} />
|
||||||
</>);
|
</>);
|
||||||
},
|
}
|
||||||
width: '65px',
|
}),
|
||||||
maxWidth: '65px',
|
columnHelper.accessor(cst => getCstDescription(cst), {
|
||||||
conditionalCellStyles: [
|
|
||||||
{
|
|
||||||
when: (cst: IConstituenta) => isMockCst(cst),
|
|
||||||
style: {backgroundColor: colors.bgWarning}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Описание',
|
|
||||||
id: 'description',
|
id: 'description',
|
||||||
selector: (cst: IConstituenta) => getCstDescription(cst),
|
header: 'Описание',
|
||||||
minWidth: '350px',
|
size: 350,
|
||||||
wrap: true,
|
minSize: 350,
|
||||||
conditionalCellStyles: [
|
maxSize: 350,
|
||||||
{
|
cell: props =>
|
||||||
when: (cst: IConstituenta) => isMockCst(cst),
|
<div style={{
|
||||||
style: {backgroundColor: colors.bgWarning}
|
fontSize: 12,
|
||||||
}
|
color: isMockCst(props.row.original) ? colors.fgWarning : undefined
|
||||||
]
|
}}>
|
||||||
},
|
{props.getValue()}
|
||||||
{
|
</div>
|
||||||
name: 'Выражение',
|
}),
|
||||||
|
columnHelper.accessor('definition_formal', {
|
||||||
id: 'expression',
|
id: 'expression',
|
||||||
selector: (cst: IConstituenta) => cst.definition_formal || '',
|
header: 'Выражение',
|
||||||
minWidth: '200px',
|
size: 700,
|
||||||
hide: 1600,
|
minSize: 0,
|
||||||
grow: 2,
|
maxSize: 700,
|
||||||
wrap: true,
|
cell: props =>
|
||||||
conditionalCellStyles: [
|
<div style={{
|
||||||
{
|
fontSize: 12,
|
||||||
when: (cst: IConstituenta) => isMockCst(cst),
|
color: isMockCst(props.row.original) ? colors.fgWarning : undefined
|
||||||
style: {backgroundColor: colors.fgWarning}
|
}}>
|
||||||
}
|
{props.getValue()}
|
||||||
]
|
</div>
|
||||||
}
|
})
|
||||||
], [colors]);
|
], [colors]);
|
||||||
|
|
||||||
const maxHeight = useMemo(
|
const maxHeight = useMemo(
|
||||||
|
@ -181,12 +170,14 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
||||||
onChange={setFilterSource}
|
onChange={setFilterSource}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='overflow-y-auto' style={{maxHeight : `${maxHeight}`}}>
|
<div className='overflow-y-auto text-sm' style={{maxHeight : `${maxHeight}`}}>
|
||||||
<ConceptDataTable
|
<DataTable
|
||||||
data={filteredData}
|
data={filteredData}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
keyField='id'
|
|
||||||
conditionalRowStyles={conditionalRowStyles}
|
|
||||||
|
// conditionalRowStyles={conditionalRowStyles}
|
||||||
|
|
||||||
noDataComponent={
|
noDataComponent={
|
||||||
<span className='flex flex-col justify-center p-2 text-center min-h-[5rem]'>
|
<span className='flex flex-col justify-center p-2 text-center min-h-[5rem]'>
|
||||||
<p>Список конституент пуст</p>
|
<p>Список конституент пуст</p>
|
||||||
|
@ -194,13 +185,8 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
striped
|
|
||||||
highlightOnHover
|
|
||||||
pointerOnHover
|
|
||||||
|
|
||||||
onRowDoubleClicked={handleDoubleClick}
|
onRowDoubleClicked={handleDoubleClick}
|
||||||
onRowClicked={handleRowClicked}
|
onRowClicked={handleRowClicked}
|
||||||
dense
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>);
|
</>);
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
import { createColumnHelper } from '@tanstack/react-table';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
import ConceptDataTable from '../../components/Common/ConceptDataTable';
|
import DataTable from '../../components/Common/DataTable';
|
||||||
import { useConceptNavigation } from '../../context/NagivationContext';
|
import { useConceptNavigation } from '../../context/NagivationContext';
|
||||||
import { ILibraryItem } from '../../utils/models';
|
import { ILibraryItem } from '../../utils/models';
|
||||||
|
|
||||||
|
@ -9,6 +10,8 @@ interface ViewSubscriptionsProps {
|
||||||
items: ILibraryItem[]
|
items: ILibraryItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<ILibraryItem>();
|
||||||
|
|
||||||
function ViewSubscriptions({items}: ViewSubscriptionsProps) {
|
function ViewSubscriptions({items}: ViewSubscriptionsProps) {
|
||||||
const { navigateTo } = useConceptNavigation();
|
const { navigateTo } = useConceptNavigation();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
@ -17,50 +20,48 @@ function ViewSubscriptions({items}: ViewSubscriptionsProps) {
|
||||||
|
|
||||||
const columns = useMemo(() =>
|
const columns = useMemo(() =>
|
||||||
[
|
[
|
||||||
{
|
columnHelper.accessor('alias', {
|
||||||
name: 'Шифр',
|
|
||||||
id: 'alias',
|
id: 'alias',
|
||||||
maxWidth: '140px',
|
header: 'Шифр',
|
||||||
selector: (item: ILibraryItem) => item.alias,
|
size: 200,
|
||||||
sortable: true,
|
minSize: 200,
|
||||||
reorder: true
|
maxSize: 200,
|
||||||
},
|
enableSorting: true
|
||||||
{
|
}),
|
||||||
name: 'Название',
|
columnHelper.accessor('title', {
|
||||||
id: 'title',
|
id: 'title',
|
||||||
minWidth: '50%',
|
header: 'Название',
|
||||||
selector: (item: ILibraryItem) => item.title,
|
minSize: 200,
|
||||||
sortable: true,
|
size: 800,
|
||||||
reorder: true
|
maxSize: 800,
|
||||||
},
|
enableSorting: true
|
||||||
{
|
}),
|
||||||
name: 'Обновлена',
|
columnHelper.accessor('time_update', {
|
||||||
id: 'time_update',
|
id: 'time_update',
|
||||||
selector: (item: ILibraryItem) => item.time_update,
|
header: 'Обновлена',
|
||||||
format: (item: ILibraryItem) => new Date(item.time_update).toLocaleString(intl.locale),
|
minSize: 200,
|
||||||
sortable: true,
|
size: 200,
|
||||||
reorder: true
|
maxSize: 200,
|
||||||
}
|
cell: props => new Date(props.cell.getValue()).toLocaleString(intl.locale),
|
||||||
|
enableSorting: true
|
||||||
|
})
|
||||||
], [intl]);
|
], [intl]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConceptDataTable
|
<div className='h-full overflow-auto text-sm border w-fit'>
|
||||||
className='h-full overflow-auto border'
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={items}
|
data={items}
|
||||||
defaultSortFieldId='time_update'
|
// defaultSortFieldId='time_update'
|
||||||
defaultSortAsc={false}
|
// defaultSortAsc={false}
|
||||||
|
|
||||||
noDataComponent={
|
noDataComponent={
|
||||||
<div className='h-[10rem]'>Отслеживаемые схемы отсутствуют</div>
|
<div className='h-[10rem]'>Отслеживаемые схемы отсутствуют</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
striped
|
|
||||||
dense
|
|
||||||
highlightOnHover
|
|
||||||
pointerOnHover
|
|
||||||
onRowClicked={openRSForm}
|
onRowClicked={openRSForm}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,67 +95,14 @@ export const darkT: IColorTheme = {
|
||||||
bgTeal: 'hsl(192, 080%, 030%)',
|
bgTeal: 'hsl(192, 080%, 030%)',
|
||||||
bgOrange: 'hsl(035, 100%, 035%)',
|
bgOrange: 'hsl(035, 100%, 035%)',
|
||||||
|
|
||||||
fgRed: 'hsl(000, 080%, 050%)',
|
fgRed: 'hsl(000, 080%, 045%)',
|
||||||
fgGreen: 'hsl(100, 080%, 040%)',
|
fgGreen: 'hsl(100, 080%, 035%)',
|
||||||
fgBlue: 'hsl(235, 100%, 080%)',
|
fgBlue: 'hsl(235, 100%, 080%)',
|
||||||
fgPurple: 'hsl(270, 100%, 080%)',
|
fgPurple: 'hsl(270, 100%, 080%)',
|
||||||
fgTeal: 'hsl(192, 100%, 030%)',
|
fgTeal: 'hsl(192, 100%, 030%)',
|
||||||
fgOrange: 'hsl(035, 100%, 050%)'
|
fgOrange: 'hsl(035, 100%, 050%)'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========= DATA TABLE THEMES ========
|
|
||||||
export const dataTableLightT = {
|
|
||||||
text: {
|
|
||||||
primary: lightT.fgDefault,
|
|
||||||
secondary: lightT.fgDefault,
|
|
||||||
disabled: lightT.fgDisabled
|
|
||||||
},
|
|
||||||
background: {
|
|
||||||
default: lightT.bgDefault
|
|
||||||
},
|
|
||||||
highlightOnHover: {
|
|
||||||
default: lightT.bgHover,
|
|
||||||
text: lightT.fgDefault
|
|
||||||
},
|
|
||||||
divider: {
|
|
||||||
default: lightT.border
|
|
||||||
},
|
|
||||||
striped: {
|
|
||||||
default: lightT.bgControls,
|
|
||||||
text: lightT.fgDefault
|
|
||||||
},
|
|
||||||
selected: {
|
|
||||||
default: lightT.bgSelected,
|
|
||||||
text: lightT.fgDefault
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const dataTableDarkT = {
|
|
||||||
text: {
|
|
||||||
primary: darkT.fgDefault,
|
|
||||||
secondary: darkT.fgDefault,
|
|
||||||
disabled: darkT.fgDisabled
|
|
||||||
},
|
|
||||||
background: {
|
|
||||||
default: darkT.bgDefault
|
|
||||||
},
|
|
||||||
highlightOnHover: {
|
|
||||||
default: darkT.bgHover,
|
|
||||||
text: darkT.fgDefault
|
|
||||||
},
|
|
||||||
divider: {
|
|
||||||
default: darkT.border
|
|
||||||
},
|
|
||||||
striped: {
|
|
||||||
default: darkT.bgControls,
|
|
||||||
text: darkT.fgDefault
|
|
||||||
},
|
|
||||||
selected: {
|
|
||||||
default: darkT.bgSelected,
|
|
||||||
text: darkT.fgDefault
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============ SELECT THEMES ==========
|
// ============ SELECT THEMES ==========
|
||||||
export const selectLightT = {
|
export const selectLightT = {
|
||||||
primary: lightT.bgPrimary,
|
primary: lightT.bgPrimary,
|
||||||
|
|
|
@ -242,11 +242,13 @@ export function getCstTypeShortcut(type: CstType) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CstTypeSelector = (Object.values(CstType)).map(
|
export const CstTypeSelector = (
|
||||||
(typeStr) => {
|
Object.values(CstType)).map(
|
||||||
const type = typeStr as CstType;
|
typeStr => ({
|
||||||
return { value: type, label: getCstTypeLabel(type) };
|
value: typeStr as CstType,
|
||||||
});
|
label: getCstTypeLabel(typeStr as CstType)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
export function getCstCompareLabel(mode: CstMatchMode): string {
|
export function getCstCompareLabel(mode: CstMatchMode): string {
|
||||||
switch(mode) {
|
switch(mode) {
|
||||||
|
@ -313,7 +315,18 @@ export const GraphColoringSelector: {value: ColoringScheme, label: string}[] = [
|
||||||
{ value: 'type', label: 'Цвет: класс'},
|
{ value: 'type', label: 'Цвет: класс'},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function getCstStatusColor(status: ExpressionStatus, colors: IColorTheme): string {
|
export function getCstStatusBgColor(status: ExpressionStatus, colors: IColorTheme): string {
|
||||||
|
switch (status) {
|
||||||
|
case ExpressionStatus.VERIFIED: return colors.bgGreen;
|
||||||
|
case ExpressionStatus.INCORRECT: return colors.bgRed;
|
||||||
|
case ExpressionStatus.INCALCULABLE: return colors.bgOrange;
|
||||||
|
case ExpressionStatus.PROPERTY: return colors.bgTeal;
|
||||||
|
case ExpressionStatus.UNKNOWN: return colors.bgBlue;
|
||||||
|
case ExpressionStatus.UNDEFINED: return colors.bgBlue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCstStatusFgColor(status: ExpressionStatus, colors: IColorTheme): string {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case ExpressionStatus.VERIFIED: return colors.fgGreen;
|
case ExpressionStatus.VERIFIED: return colors.fgGreen;
|
||||||
case ExpressionStatus.INCORRECT: return colors.fgRed;
|
case ExpressionStatus.INCORRECT: return colors.fgRed;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user