mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Refactoring: wrapper index.tsx for components
This commit is contained in:
parent
fbd97b2653
commit
71dd8f4be1
|
@ -2,7 +2,7 @@ import { createBrowserRouter, Outlet, RouterProvider } from 'react-router-dom';
|
||||||
|
|
||||||
import ConceptToaster from './components/ConceptToaster';
|
import ConceptToaster from './components/ConceptToaster';
|
||||||
import Footer from './components/Footer';
|
import Footer from './components/Footer';
|
||||||
import Navigation from './components/Navigation/Navigation';
|
import Navigation from './components/Navigation';
|
||||||
import { NavigationState } from './context/NagivationContext';
|
import { NavigationState } from './context/NagivationContext';
|
||||||
import { useConceptTheme } from './context/ThemeContext';
|
import { useConceptTheme } from './context/ThemeContext';
|
||||||
import CreateRSFormPage from './pages/CreateRSFormPage';
|
import CreateRSFormPage from './pages/CreateRSFormPage';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import axios, { type AxiosError,AxiosHeaderValue } from 'axios';
|
import axios, { type AxiosError,AxiosHeaderValue } from 'axios';
|
||||||
|
|
||||||
import PrettyJson from './Common/PrettyJSON';
|
import PrettyJson from './common/PrettyJSON';
|
||||||
|
|
||||||
export type ErrorInfo = string | Error | AxiosError | undefined;
|
export type ErrorInfo = string | Error | AxiosError | undefined;
|
||||||
|
|
||||||
|
|
216
rsconcept/frontend/src/components/DataTable/DataTable.tsx
Normal file
216
rsconcept/frontend/src/components/DataTable/DataTable.tsx
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
import {
|
||||||
|
Cell, ColumnSort,
|
||||||
|
createColumnHelper, flexRender, getCoreRowModel,
|
||||||
|
getPaginationRowModel, getSortedRowModel, Header, HeaderGroup,
|
||||||
|
PaginationState, Row, RowData, type RowSelectionState,
|
||||||
|
SortingState, TableOptions, useReactTable, type VisibilityState
|
||||||
|
} from '@tanstack/react-table';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import DefaultNoData from './DefaultNoData';
|
||||||
|
import PaginationTools from './PaginationTools';
|
||||||
|
import SelectAll from './SelectAll';
|
||||||
|
import SelectRow from './SelectRow';
|
||||||
|
import SortingIcon from './SortingIcon';
|
||||||
|
|
||||||
|
export { createColumnHelper, type ColumnSort, type RowSelectionState, type VisibilityState };
|
||||||
|
|
||||||
|
export interface IConditionalStyle<TData> {
|
||||||
|
when: (rowData: TData) => boolean
|
||||||
|
style: React.CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DataTableProps<TData extends RowData>
|
||||||
|
extends Pick<TableOptions<TData>,
|
||||||
|
'data' | 'columns' |
|
||||||
|
'onRowSelectionChange' | 'onColumnVisibilityChange'
|
||||||
|
> {
|
||||||
|
dense?: boolean
|
||||||
|
headPosition?: string
|
||||||
|
noHeader?: boolean
|
||||||
|
noFooter?: boolean
|
||||||
|
conditionalRowStyles?: IConditionalStyle<TData>[]
|
||||||
|
onRowClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void
|
||||||
|
onRowDoubleClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void
|
||||||
|
noDataComponent?: React.ReactNode
|
||||||
|
|
||||||
|
enableRowSelection?: boolean
|
||||||
|
rowSelection?: RowSelectionState
|
||||||
|
|
||||||
|
enableHiding?: boolean
|
||||||
|
columnVisibility?: VisibilityState
|
||||||
|
|
||||||
|
enablePagination?: boolean
|
||||||
|
paginationPerPage?: number
|
||||||
|
paginationOptions?: number[]
|
||||||
|
onChangePaginationOption?: (newValue: number) => void
|
||||||
|
|
||||||
|
enableSorting?: boolean
|
||||||
|
initialSorting?: ColumnSort
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI element: data representation as a table.
|
||||||
|
*
|
||||||
|
* @param headPosition - Top position of sticky header (0 if no other sticky elements are present).
|
||||||
|
* No sticky header if omitted
|
||||||
|
*/
|
||||||
|
export default function DataTable<TData extends RowData>({
|
||||||
|
dense, headPosition, conditionalRowStyles, noFooter, noHeader,
|
||||||
|
onRowClicked, onRowDoubleClicked, noDataComponent,
|
||||||
|
|
||||||
|
enableRowSelection,
|
||||||
|
rowSelection,
|
||||||
|
|
||||||
|
enableHiding,
|
||||||
|
columnVisibility,
|
||||||
|
|
||||||
|
enableSorting,
|
||||||
|
initialSorting,
|
||||||
|
|
||||||
|
enablePagination,
|
||||||
|
paginationPerPage=10,
|
||||||
|
paginationOptions=[10, 20, 30, 40, 50],
|
||||||
|
onChangePaginationOption,
|
||||||
|
|
||||||
|
...options
|
||||||
|
}: DataTableProps<TData>) {
|
||||||
|
const [sorting, setSorting] = useState<SortingState>(initialSorting ? [initialSorting] : []);
|
||||||
|
|
||||||
|
const [pagination, setPagination] = useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: paginationPerPage,
|
||||||
|
});
|
||||||
|
|
||||||
|
const tableImpl = useReactTable({
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getSortedRowModel: enableSorting ? getSortedRowModel() : undefined,
|
||||||
|
getPaginationRowModel: enablePagination ? getPaginationRowModel() : undefined,
|
||||||
|
|
||||||
|
state: {
|
||||||
|
pagination: pagination,
|
||||||
|
sorting: sorting,
|
||||||
|
rowSelection: rowSelection ?? {},
|
||||||
|
columnVisibility: columnVisibility ?? {}
|
||||||
|
},
|
||||||
|
enableHiding: enableHiding,
|
||||||
|
onPaginationChange: enablePagination ? setPagination : undefined,
|
||||||
|
onSortingChange: enableSorting ? setSorting : undefined,
|
||||||
|
enableMultiRowSelection: enableRowSelection,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
|
||||||
|
const isEmpty = tableImpl.getRowModel().rows.length === 0;
|
||||||
|
|
||||||
|
function getRowStyles(row: Row<TData>) {
|
||||||
|
return {...conditionalRowStyles!
|
||||||
|
.filter(item => item.when(row.original))
|
||||||
|
.reduce((prev, item) => ({...prev, ...item.style}), {})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='w-full'>
|
||||||
|
<div className='flex flex-col items-stretch'>
|
||||||
|
<table>
|
||||||
|
{ !noHeader &&
|
||||||
|
<thead
|
||||||
|
className={`clr-app shadow-border`}
|
||||||
|
style={{
|
||||||
|
top: headPosition,
|
||||||
|
position: 'sticky'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tableImpl.getHeaderGroups().map(
|
||||||
|
(headerGroup: HeaderGroup<TData>) => (
|
||||||
|
<tr key={headerGroup.id}>
|
||||||
|
{enableRowSelection &&
|
||||||
|
<th className='pl-3 pr-1'>
|
||||||
|
<SelectAll table={tableImpl} />
|
||||||
|
</th>}
|
||||||
|
{headerGroup.headers.map(
|
||||||
|
(header: Header<TData, unknown>) => (
|
||||||
|
<th key={header.id}
|
||||||
|
colSpan={header.colSpan}
|
||||||
|
className='px-2 py-2 text-xs font-semibold select-none whitespace-nowrap'
|
||||||
|
style={{
|
||||||
|
textAlign: header.getSize() > 100 ? 'left': 'center',
|
||||||
|
width: header.getSize(),
|
||||||
|
cursor: enableSorting && header.column.getCanSort() ? 'pointer': 'auto',
|
||||||
|
}}
|
||||||
|
onClick={enableSorting ? header.column.getToggleSortingHandler() : undefined}
|
||||||
|
>
|
||||||
|
{header.isPlaceholder ? null : (
|
||||||
|
<div className='flex gap-1'>
|
||||||
|
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
|
{enableSorting && header.column.getCanSort() && <SortingIcon column={header.column} />}
|
||||||
|
</div>)}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</thead>}
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{tableImpl.getRowModel().rows.map(
|
||||||
|
(row: Row<TData>, index) => (
|
||||||
|
<tr
|
||||||
|
key={row.id}
|
||||||
|
className={
|
||||||
|
row.getIsSelected() ? 'clr-selected clr-hover' :
|
||||||
|
index % 2 === 0 ? 'clr-controls clr-hover' : 'clr-app clr-hover'
|
||||||
|
}
|
||||||
|
style={conditionalRowStyles && getRowStyles(row)}
|
||||||
|
>
|
||||||
|
{enableRowSelection &&
|
||||||
|
<td key={`select-${row.id}`} className='pl-3 pr-1 border-y'>
|
||||||
|
<SelectRow row={row} />
|
||||||
|
</td>}
|
||||||
|
{row.getVisibleCells().map(
|
||||||
|
(cell: Cell<TData, unknown>) => (
|
||||||
|
<td
|
||||||
|
key={cell.id}
|
||||||
|
className='px-2 border-y'
|
||||||
|
style={{
|
||||||
|
cursor: onRowClicked || onRowDoubleClicked ? 'pointer': 'auto',
|
||||||
|
paddingBottom: dense ? '0.25rem': '0.5rem',
|
||||||
|
paddingTop: dense ? '0.25rem': '0.5rem'
|
||||||
|
}}
|
||||||
|
onClick={event => onRowClicked && onRowClicked(row.original, event)}
|
||||||
|
onDoubleClick={event => onRowDoubleClicked && onRowDoubleClicked(row.original, event)}
|
||||||
|
>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
{!noFooter &&
|
||||||
|
<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>
|
||||||
|
|
||||||
|
{enablePagination && !isEmpty &&
|
||||||
|
<PaginationTools
|
||||||
|
table={tableImpl}
|
||||||
|
paginationOptions={paginationOptions}
|
||||||
|
onChangePaginationOption={onChangePaginationOption}
|
||||||
|
/>}
|
||||||
|
</div>
|
||||||
|
{isEmpty && (noDataComponent ?? <DefaultNoData />)}
|
||||||
|
</div>);
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import { Table } from '@tanstack/react-table';
|
import { Table } from '@tanstack/react-table';
|
||||||
|
|
||||||
import Tristate from '../Common/Tristate';
|
import Tristate from '../common/Tristate';
|
||||||
|
|
||||||
interface SelectAllProps<TData> {
|
interface SelectAllProps<TData> {
|
||||||
table: Table<TData>
|
table: Table<TData>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Row } from '@tanstack/react-table';
|
import { Row } from '@tanstack/react-table';
|
||||||
|
|
||||||
import Checkbox from '../Common/Checkbox';
|
import Checkbox from '../common/Checkbox';
|
||||||
|
|
||||||
interface SelectRowProps<TData> {
|
interface SelectRowProps<TData> {
|
||||||
row: Row<TData>
|
row: Row<TData>
|
||||||
|
|
|
@ -1,216 +1,5 @@
|
||||||
import {
|
export {
|
||||||
Cell, ColumnSort,
|
default,
|
||||||
createColumnHelper, flexRender, getCoreRowModel,
|
createColumnHelper, type IConditionalStyle,
|
||||||
getPaginationRowModel, getSortedRowModel, Header, HeaderGroup,
|
type RowSelectionState, type VisibilityState
|
||||||
PaginationState, Row, RowData, type RowSelectionState,
|
} from './DataTable';
|
||||||
SortingState, TableOptions, useReactTable, type VisibilityState
|
|
||||||
} from '@tanstack/react-table';
|
|
||||||
import { useState } from 'react';
|
|
||||||
|
|
||||||
import DefaultNoData from './DefaultNoData';
|
|
||||||
import PaginationTools from './PaginationTools';
|
|
||||||
import SelectAll from './SelectAll';
|
|
||||||
import SelectRow from './SelectRow';
|
|
||||||
import SortingIcon from './SortingIcon';
|
|
||||||
|
|
||||||
export { createColumnHelper, type ColumnSort, type RowSelectionState, type VisibilityState };
|
|
||||||
|
|
||||||
export interface IConditionalStyle<TData> {
|
|
||||||
when: (rowData: TData) => boolean
|
|
||||||
style: React.CSSProperties
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DataTableProps<TData extends RowData>
|
|
||||||
extends Pick<TableOptions<TData>,
|
|
||||||
'data' | 'columns' |
|
|
||||||
'onRowSelectionChange' | 'onColumnVisibilityChange'
|
|
||||||
> {
|
|
||||||
dense?: boolean
|
|
||||||
headPosition?: string
|
|
||||||
noHeader?: boolean
|
|
||||||
noFooter?: boolean
|
|
||||||
conditionalRowStyles?: IConditionalStyle<TData>[]
|
|
||||||
onRowClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void
|
|
||||||
onRowDoubleClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void
|
|
||||||
noDataComponent?: React.ReactNode
|
|
||||||
|
|
||||||
enableRowSelection?: boolean
|
|
||||||
rowSelection?: RowSelectionState
|
|
||||||
|
|
||||||
enableHiding?: boolean
|
|
||||||
columnVisibility?: VisibilityState
|
|
||||||
|
|
||||||
enablePagination?: boolean
|
|
||||||
paginationPerPage?: number
|
|
||||||
paginationOptions?: number[]
|
|
||||||
onChangePaginationOption?: (newValue: number) => void
|
|
||||||
|
|
||||||
enableSorting?: boolean
|
|
||||||
initialSorting?: ColumnSort
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* UI element: data representation as a table.
|
|
||||||
*
|
|
||||||
* @param headPosition - Top position of sticky header (0 if no other sticky elements are present).
|
|
||||||
* No sticky header if omitted
|
|
||||||
*/
|
|
||||||
export default function DataTable<TData extends RowData>({
|
|
||||||
dense, headPosition, conditionalRowStyles, noFooter, noHeader,
|
|
||||||
onRowClicked, onRowDoubleClicked, noDataComponent,
|
|
||||||
|
|
||||||
enableRowSelection,
|
|
||||||
rowSelection,
|
|
||||||
|
|
||||||
enableHiding,
|
|
||||||
columnVisibility,
|
|
||||||
|
|
||||||
enableSorting,
|
|
||||||
initialSorting,
|
|
||||||
|
|
||||||
enablePagination,
|
|
||||||
paginationPerPage=10,
|
|
||||||
paginationOptions=[10, 20, 30, 40, 50],
|
|
||||||
onChangePaginationOption,
|
|
||||||
|
|
||||||
...options
|
|
||||||
}: DataTableProps<TData>) {
|
|
||||||
const [sorting, setSorting] = useState<SortingState>(initialSorting ? [initialSorting] : []);
|
|
||||||
|
|
||||||
const [pagination, setPagination] = useState<PaginationState>({
|
|
||||||
pageIndex: 0,
|
|
||||||
pageSize: paginationPerPage,
|
|
||||||
});
|
|
||||||
|
|
||||||
const tableImpl = useReactTable({
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
getSortedRowModel: enableSorting ? getSortedRowModel() : undefined,
|
|
||||||
getPaginationRowModel: enablePagination ? getPaginationRowModel() : undefined,
|
|
||||||
|
|
||||||
state: {
|
|
||||||
pagination: pagination,
|
|
||||||
sorting: sorting,
|
|
||||||
rowSelection: rowSelection ?? {},
|
|
||||||
columnVisibility: columnVisibility ?? {}
|
|
||||||
},
|
|
||||||
enableHiding: enableHiding,
|
|
||||||
onPaginationChange: enablePagination ? setPagination : undefined,
|
|
||||||
onSortingChange: enableSorting ? setSorting : undefined,
|
|
||||||
enableMultiRowSelection: enableRowSelection,
|
|
||||||
...options
|
|
||||||
});
|
|
||||||
|
|
||||||
const isEmpty = tableImpl.getRowModel().rows.length === 0;
|
|
||||||
|
|
||||||
function getRowStyles(row: Row<TData>) {
|
|
||||||
return {...conditionalRowStyles!
|
|
||||||
.filter(item => item.when(row.original))
|
|
||||||
.reduce((prev, item) => ({...prev, ...item.style}), {})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='w-full'>
|
|
||||||
<div className='flex flex-col items-stretch'>
|
|
||||||
<table>
|
|
||||||
{ !noHeader &&
|
|
||||||
<thead
|
|
||||||
className={`clr-app shadow-border`}
|
|
||||||
style={{
|
|
||||||
top: headPosition,
|
|
||||||
position: 'sticky'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{tableImpl.getHeaderGroups().map(
|
|
||||||
(headerGroup: HeaderGroup<TData>) => (
|
|
||||||
<tr key={headerGroup.id}>
|
|
||||||
{enableRowSelection &&
|
|
||||||
<th className='pl-3 pr-1'>
|
|
||||||
<SelectAll table={tableImpl} />
|
|
||||||
</th>}
|
|
||||||
{headerGroup.headers.map(
|
|
||||||
(header: Header<TData, unknown>) => (
|
|
||||||
<th key={header.id}
|
|
||||||
colSpan={header.colSpan}
|
|
||||||
className='px-2 py-2 text-xs font-semibold select-none whitespace-nowrap'
|
|
||||||
style={{
|
|
||||||
textAlign: header.getSize() > 100 ? 'left': 'center',
|
|
||||||
width: header.getSize(),
|
|
||||||
cursor: enableSorting && header.column.getCanSort() ? 'pointer': 'auto',
|
|
||||||
}}
|
|
||||||
onClick={enableSorting ? header.column.getToggleSortingHandler() : undefined}
|
|
||||||
>
|
|
||||||
{header.isPlaceholder ? null : (
|
|
||||||
<div className='flex gap-1'>
|
|
||||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
||||||
{enableSorting && header.column.getCanSort() && <SortingIcon column={header.column} />}
|
|
||||||
</div>)}
|
|
||||||
</th>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</thead>}
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
{tableImpl.getRowModel().rows.map(
|
|
||||||
(row: Row<TData>, index) => (
|
|
||||||
<tr
|
|
||||||
key={row.id}
|
|
||||||
className={
|
|
||||||
row.getIsSelected() ? 'clr-selected clr-hover' :
|
|
||||||
index % 2 === 0 ? 'clr-controls clr-hover' : 'clr-app clr-hover'
|
|
||||||
}
|
|
||||||
style={conditionalRowStyles && getRowStyles(row)}
|
|
||||||
>
|
|
||||||
{enableRowSelection &&
|
|
||||||
<td key={`select-${row.id}`} className='pl-3 pr-1 border-y'>
|
|
||||||
<SelectRow row={row} />
|
|
||||||
</td>}
|
|
||||||
{row.getVisibleCells().map(
|
|
||||||
(cell: Cell<TData, unknown>) => (
|
|
||||||
<td
|
|
||||||
key={cell.id}
|
|
||||||
className='px-2 border-y'
|
|
||||||
style={{
|
|
||||||
cursor: onRowClicked || onRowDoubleClicked ? 'pointer': 'auto',
|
|
||||||
paddingBottom: dense ? '0.25rem': '0.5rem',
|
|
||||||
paddingTop: dense ? '0.25rem': '0.5rem'
|
|
||||||
}}
|
|
||||||
onClick={event => onRowClicked && onRowClicked(row.original, event)}
|
|
||||||
onDoubleClick={event => onRowDoubleClicked && onRowDoubleClicked(row.original, event)}
|
|
||||||
>
|
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
||||||
</td>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
|
|
||||||
{!noFooter &&
|
|
||||||
<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>
|
|
||||||
|
|
||||||
{enablePagination && !isEmpty &&
|
|
||||||
<PaginationTools
|
|
||||||
table={tableImpl}
|
|
||||||
paginationOptions={paginationOptions}
|
|
||||||
onChangePaginationOption={onChangePaginationOption}
|
|
||||||
/>}
|
|
||||||
</div>
|
|
||||||
{isEmpty && (noDataComponent ?? <DefaultNoData />)}
|
|
||||||
</div>);
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { type FallbackProps } from 'react-error-boundary';
|
import { type FallbackProps } from 'react-error-boundary';
|
||||||
|
|
||||||
import Button from './Common/Button';
|
import Button from './common/Button';
|
||||||
|
|
||||||
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
|
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
|
||||||
reportError(error);
|
reportError(error);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { IConstituenta } from '../../models/rsform';
|
import { IConstituenta } from '../../models/rsform';
|
||||||
import ConceptTooltip from '../Common/ConceptTooltip';
|
import ConceptTooltip from '../common/ConceptTooltip';
|
||||||
import InfoConstituenta from './InfoConstituenta';
|
import InfoConstituenta from './InfoConstituenta';
|
||||||
|
|
||||||
interface ConstituentaTooltipProps {
|
interface ConstituentaTooltipProps {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { urls } from '../../utils/constants';
|
import { urls } from '../../utils/constants';
|
||||||
import TextURL from '../Common/TextURL';
|
import TextURL from '../common/TextURL';
|
||||||
|
|
||||||
function HelpAPI() {
|
function HelpAPI() {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Divider from '../Common/Divider';
|
import Divider from '../common/Divider';
|
||||||
import InfoCstStatus from './InfoCstStatus';
|
import InfoCstStatus from './InfoCstStatus';
|
||||||
|
|
||||||
function HelpConstituenta() {
|
function HelpConstituenta() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { urls } from '../../utils/constants';
|
import { urls } from '../../utils/constants';
|
||||||
import TextURL from '../Common/TextURL';
|
import TextURL from '../common/TextURL';
|
||||||
|
|
||||||
function HelpExteor() {
|
function HelpExteor() {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { urls } from '../../utils/constants';
|
import { urls } from '../../utils/constants';
|
||||||
import TextURL from '../Common/TextURL';
|
import TextURL from '../common/TextURL';
|
||||||
|
|
||||||
function HelpMain() {
|
function HelpMain() {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Divider from '../Common/Divider';
|
import Divider from '../common/Divider';
|
||||||
import InfoCstStatus from './InfoCstStatus';
|
import InfoCstStatus from './InfoCstStatus';
|
||||||
|
|
||||||
function HelpRSFormItems() {
|
function HelpRSFormItems() {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { useMemo } from 'react';
|
||||||
|
|
||||||
import useWindowSize from '../../hooks/useWindowSize';
|
import useWindowSize from '../../hooks/useWindowSize';
|
||||||
import { urls, youtube } from '../../utils/constants';
|
import { urls, youtube } from '../../utils/constants';
|
||||||
import EmbedYoutube from '../Common/EmbedYoutube';
|
import EmbedYoutube from '../common/EmbedYoutube';
|
||||||
|
|
||||||
const OPT_VIDEO_H = 1080;
|
const OPT_VIDEO_H = 1080;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Divider from '../Common/Divider';
|
import Divider from '../common/Divider';
|
||||||
import InfoCstClass from './InfoCstClass';
|
import InfoCstClass from './InfoCstClass';
|
||||||
import InfoCstStatus from './InfoCstStatus';
|
import InfoCstStatus from './InfoCstStatus';
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { useAuth } from '../../context/AuthContext';
|
import { useAuth } from '../../context/AuthContext';
|
||||||
import { useConceptNavigation } from '../../context/NagivationContext';
|
import { useConceptNavigation } from '../../context/NagivationContext';
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
import Dropdown from '../Common/Dropdown';
|
import Dropdown from '../common/Dropdown';
|
||||||
import DropdownButton from '../Common/DropdownButton';
|
import DropdownButton from '../common/DropdownButton';
|
||||||
|
|
||||||
interface UserDropdownProps {
|
interface UserDropdownProps {
|
||||||
hideDropdown: () => void
|
hideDropdown: () => void
|
||||||
|
|
1
rsconcept/frontend/src/components/Navigation/index.tsx
Normal file
1
rsconcept/frontend/src/components/Navigation/index.tsx
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './Navigation';
|
176
rsconcept/frontend/src/components/RSInput/RSInput.tsx
Normal file
176
rsconcept/frontend/src/components/RSInput/RSInput.tsx
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
|
||||||
|
import { Extension } from '@codemirror/state';
|
||||||
|
import { tags } from '@lezer/highlight';
|
||||||
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
|
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||||
|
import { EditorView } from 'codemirror';
|
||||||
|
import { RefObject, useCallback, useMemo, useRef } from 'react';
|
||||||
|
|
||||||
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
|
import { TokenID } from '../../models/rslang';
|
||||||
|
import Label from '../common/Label';
|
||||||
|
import { ccBracketMatching } from './bracketMatching';
|
||||||
|
import { RSLanguage } from './rslang';
|
||||||
|
import { getSymbolSubstitute,RSTextWrapper } from './textEditing';
|
||||||
|
import { rsHoverTooltip } from './tooltip';
|
||||||
|
|
||||||
|
const editorSetup: BasicSetupOptions = {
|
||||||
|
highlightSpecialChars: false,
|
||||||
|
history: true,
|
||||||
|
drawSelection: false,
|
||||||
|
syntaxHighlighting: false,
|
||||||
|
defaultKeymap: true,
|
||||||
|
historyKeymap: true,
|
||||||
|
|
||||||
|
lineNumbers: false,
|
||||||
|
highlightActiveLineGutter: false,
|
||||||
|
foldGutter: false,
|
||||||
|
dropCursor: true,
|
||||||
|
allowMultipleSelections: false,
|
||||||
|
indentOnInput: false,
|
||||||
|
bracketMatching: false,
|
||||||
|
closeBrackets: false,
|
||||||
|
autocompletion: false,
|
||||||
|
rectangularSelection: false,
|
||||||
|
crosshairCursor: false,
|
||||||
|
highlightActiveLine: false,
|
||||||
|
highlightSelectionMatches: false,
|
||||||
|
closeBracketsKeymap: false,
|
||||||
|
searchKeymap: false,
|
||||||
|
foldKeymap: false,
|
||||||
|
completionKeymap: false,
|
||||||
|
lintKeymap: false
|
||||||
|
};
|
||||||
|
|
||||||
|
interface RSInputProps
|
||||||
|
extends Pick<ReactCodeMirrorProps,
|
||||||
|
'id' | 'height' | 'minHeight' | 'maxHeight' | 'value' | 'className' | 'onFocus' | 'onBlur' | 'placeholder'
|
||||||
|
> {
|
||||||
|
label?: string
|
||||||
|
dimensions?: string
|
||||||
|
disabled?: boolean
|
||||||
|
noTooltip?: boolean
|
||||||
|
innerref?: RefObject<ReactCodeMirrorRef> | undefined
|
||||||
|
onChange?: (newValue: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function RSInput({
|
||||||
|
id, label, innerref, onChange,
|
||||||
|
disabled, noTooltip,
|
||||||
|
dimensions = 'w-full',
|
||||||
|
...props
|
||||||
|
}: RSInputProps) {
|
||||||
|
const { darkMode, colors } = useConceptTheme();
|
||||||
|
const { schema } = useRSForm();
|
||||||
|
|
||||||
|
const internalRef = useRef<ReactCodeMirrorRef>(null);
|
||||||
|
const thisRef = useMemo(
|
||||||
|
() => {
|
||||||
|
return innerref ?? internalRef;
|
||||||
|
}, [internalRef, innerref]);
|
||||||
|
|
||||||
|
const cursor = useMemo(() => !disabled ? 'cursor-text': 'cursor-default', [disabled]);
|
||||||
|
const customTheme: Extension = useMemo(
|
||||||
|
() => createTheme({
|
||||||
|
theme: darkMode ? 'dark' : 'light',
|
||||||
|
settings: {
|
||||||
|
fontFamily: 'inherit',
|
||||||
|
background: !disabled ? colors.bgInput : colors.bgDefault,
|
||||||
|
foreground: colors.fgDefault,
|
||||||
|
selection: colors.bgHover
|
||||||
|
},
|
||||||
|
styles: [
|
||||||
|
{ tag: tags.name, color: colors.fgPurple, cursor: 'default' }, // GlobalID
|
||||||
|
{ tag: tags.variableName, color: colors.fgGreen }, // LocalID
|
||||||
|
{ tag: tags.propertyName, color: colors.fgTeal }, // Radical
|
||||||
|
{ tag: tags.keyword, color: colors.fgBlue }, // keywords
|
||||||
|
{ tag: tags.literal, color: colors.fgBlue }, // literals
|
||||||
|
{ tag: tags.controlKeyword, fontWeight: '500'}, // R | I | D
|
||||||
|
{ tag: tags.unit, fontSize: '0.75rem' }, // indicies
|
||||||
|
]
|
||||||
|
}), [disabled, colors, darkMode]);
|
||||||
|
|
||||||
|
const editorExtensions = useMemo(
|
||||||
|
() => [
|
||||||
|
EditorView.lineWrapping,
|
||||||
|
RSLanguage,
|
||||||
|
ccBracketMatching(darkMode),
|
||||||
|
... noTooltip ? [] : [rsHoverTooltip(schema?.items || [])],
|
||||||
|
], [darkMode, schema?.items, noTooltip]);
|
||||||
|
|
||||||
|
const handleInput = useCallback(
|
||||||
|
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
|
if (!thisRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const text = new RSTextWrapper(thisRef.current as Required<ReactCodeMirrorRef>);
|
||||||
|
if (event.shiftKey && !event.altKey) {
|
||||||
|
if (event.key === '*') {
|
||||||
|
text.insertToken(TokenID.DECART);
|
||||||
|
event.preventDefault();
|
||||||
|
} else if (event.code === 'KeyB') {
|
||||||
|
text.insertChar('ℬ');
|
||||||
|
event.preventDefault();
|
||||||
|
} else if (event.code === 'KeyZ') {
|
||||||
|
text.insertChar('Z');
|
||||||
|
event.preventDefault();
|
||||||
|
} else if (event.code === 'KeyR') {
|
||||||
|
text.insertChar('R');
|
||||||
|
event.preventDefault();
|
||||||
|
} else if (event.code === 'KeyF') {
|
||||||
|
text.insertChar('F');
|
||||||
|
event.preventDefault();
|
||||||
|
} else if (event.code === 'KeyP') {
|
||||||
|
text.insertChar('P');
|
||||||
|
event.preventDefault();
|
||||||
|
} else if (event.code === 'KeyX') {
|
||||||
|
text.insertChar('X');
|
||||||
|
event.preventDefault();
|
||||||
|
} else if (event.code === 'KeyS') {
|
||||||
|
text.insertChar('S');
|
||||||
|
event.preventDefault();
|
||||||
|
} else if (event.code === 'KeyD') {
|
||||||
|
text.insertChar('D');
|
||||||
|
event.preventDefault();
|
||||||
|
} else if (event.code === 'KeyC') {
|
||||||
|
text.insertChar('C');
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
} else if (event.altKey) {
|
||||||
|
if (text.processAltKey(event.code, event.shiftKey)) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
} else if (!event.ctrlKey) {
|
||||||
|
const newSymbol = getSymbolSubstitute(event.code, event.shiftKey);
|
||||||
|
if (newSymbol) {
|
||||||
|
text.replaceWith(newSymbol);
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [thisRef]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`flex flex-col ${dimensions} ${cursor}`}>
|
||||||
|
{label &&
|
||||||
|
<Label
|
||||||
|
text={label}
|
||||||
|
htmlFor={id}
|
||||||
|
className='mb-2'
|
||||||
|
/>}
|
||||||
|
<CodeMirror id={id}
|
||||||
|
ref={thisRef}
|
||||||
|
basicSetup={editorSetup}
|
||||||
|
theme={customTheme}
|
||||||
|
extensions={editorExtensions}
|
||||||
|
indentWithTab={false}
|
||||||
|
onChange={onChange}
|
||||||
|
editable={!disabled}
|
||||||
|
onKeyDown={handleInput}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RSInput;
|
|
@ -1,176 +1 @@
|
||||||
|
export { default } from './RSInput';
|
||||||
import { Extension } from '@codemirror/state';
|
|
||||||
import { tags } from '@lezer/highlight';
|
|
||||||
import { createTheme } from '@uiw/codemirror-themes';
|
|
||||||
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
|
||||||
import { EditorView } from 'codemirror';
|
|
||||||
import { RefObject, useCallback, useMemo, useRef } from 'react';
|
|
||||||
|
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
|
||||||
import { TokenID } from '../../models/rslang';
|
|
||||||
import Label from '../Common/Label';
|
|
||||||
import { ccBracketMatching } from './bracketMatching';
|
|
||||||
import { RSLanguage } from './rslang';
|
|
||||||
import { getSymbolSubstitute,RSTextWrapper } from './textEditing';
|
|
||||||
import { rsHoverTooltip } from './tooltip';
|
|
||||||
|
|
||||||
const editorSetup: BasicSetupOptions = {
|
|
||||||
highlightSpecialChars: false,
|
|
||||||
history: true,
|
|
||||||
drawSelection: false,
|
|
||||||
syntaxHighlighting: false,
|
|
||||||
defaultKeymap: true,
|
|
||||||
historyKeymap: true,
|
|
||||||
|
|
||||||
lineNumbers: false,
|
|
||||||
highlightActiveLineGutter: false,
|
|
||||||
foldGutter: false,
|
|
||||||
dropCursor: true,
|
|
||||||
allowMultipleSelections: false,
|
|
||||||
indentOnInput: false,
|
|
||||||
bracketMatching: false,
|
|
||||||
closeBrackets: false,
|
|
||||||
autocompletion: false,
|
|
||||||
rectangularSelection: false,
|
|
||||||
crosshairCursor: false,
|
|
||||||
highlightActiveLine: false,
|
|
||||||
highlightSelectionMatches: false,
|
|
||||||
closeBracketsKeymap: false,
|
|
||||||
searchKeymap: false,
|
|
||||||
foldKeymap: false,
|
|
||||||
completionKeymap: false,
|
|
||||||
lintKeymap: false
|
|
||||||
};
|
|
||||||
|
|
||||||
interface RSInputProps
|
|
||||||
extends Pick<ReactCodeMirrorProps,
|
|
||||||
'id' | 'height' | 'minHeight' | 'maxHeight' | 'value' | 'className' | 'onFocus' | 'onBlur' | 'placeholder'
|
|
||||||
> {
|
|
||||||
label?: string
|
|
||||||
dimensions?: string
|
|
||||||
disabled?: boolean
|
|
||||||
noTooltip?: boolean
|
|
||||||
innerref?: RefObject<ReactCodeMirrorRef> | undefined
|
|
||||||
onChange?: (newValue: string) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
function RSInput({
|
|
||||||
id, label, innerref, onChange,
|
|
||||||
disabled, noTooltip,
|
|
||||||
dimensions = 'w-full',
|
|
||||||
...props
|
|
||||||
}: RSInputProps) {
|
|
||||||
const { darkMode, colors } = useConceptTheme();
|
|
||||||
const { schema } = useRSForm();
|
|
||||||
|
|
||||||
const internalRef = useRef<ReactCodeMirrorRef>(null);
|
|
||||||
const thisRef = useMemo(
|
|
||||||
() => {
|
|
||||||
return innerref ?? internalRef;
|
|
||||||
}, [internalRef, innerref]);
|
|
||||||
|
|
||||||
const cursor = useMemo(() => !disabled ? 'cursor-text': 'cursor-default', [disabled]);
|
|
||||||
const customTheme: Extension = useMemo(
|
|
||||||
() => createTheme({
|
|
||||||
theme: darkMode ? 'dark' : 'light',
|
|
||||||
settings: {
|
|
||||||
fontFamily: 'inherit',
|
|
||||||
background: !disabled ? colors.bgInput : colors.bgDefault,
|
|
||||||
foreground: colors.fgDefault,
|
|
||||||
selection: colors.bgHover
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: tags.name, color: colors.fgPurple, cursor: 'default' }, // GlobalID
|
|
||||||
{ tag: tags.variableName, color: colors.fgGreen }, // LocalID
|
|
||||||
{ tag: tags.propertyName, color: colors.fgTeal }, // Radical
|
|
||||||
{ tag: tags.keyword, color: colors.fgBlue }, // keywords
|
|
||||||
{ tag: tags.literal, color: colors.fgBlue }, // literals
|
|
||||||
{ tag: tags.controlKeyword, fontWeight: '500'}, // R | I | D
|
|
||||||
{ tag: tags.unit, fontSize: '0.75rem' }, // indicies
|
|
||||||
]
|
|
||||||
}), [disabled, colors, darkMode]);
|
|
||||||
|
|
||||||
const editorExtensions = useMemo(
|
|
||||||
() => [
|
|
||||||
EditorView.lineWrapping,
|
|
||||||
RSLanguage,
|
|
||||||
ccBracketMatching(darkMode),
|
|
||||||
... noTooltip ? [] : [rsHoverTooltip(schema?.items || [])],
|
|
||||||
], [darkMode, schema?.items, noTooltip]);
|
|
||||||
|
|
||||||
const handleInput = useCallback(
|
|
||||||
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
|
||||||
if (!thisRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const text = new RSTextWrapper(thisRef.current as Required<ReactCodeMirrorRef>);
|
|
||||||
if (event.shiftKey && !event.altKey) {
|
|
||||||
if (event.key === '*') {
|
|
||||||
text.insertToken(TokenID.DECART);
|
|
||||||
event.preventDefault();
|
|
||||||
} else if (event.code === 'KeyB') {
|
|
||||||
text.insertChar('ℬ');
|
|
||||||
event.preventDefault();
|
|
||||||
} else if (event.code === 'KeyZ') {
|
|
||||||
text.insertChar('Z');
|
|
||||||
event.preventDefault();
|
|
||||||
} else if (event.code === 'KeyR') {
|
|
||||||
text.insertChar('R');
|
|
||||||
event.preventDefault();
|
|
||||||
} else if (event.code === 'KeyF') {
|
|
||||||
text.insertChar('F');
|
|
||||||
event.preventDefault();
|
|
||||||
} else if (event.code === 'KeyP') {
|
|
||||||
text.insertChar('P');
|
|
||||||
event.preventDefault();
|
|
||||||
} else if (event.code === 'KeyX') {
|
|
||||||
text.insertChar('X');
|
|
||||||
event.preventDefault();
|
|
||||||
} else if (event.code === 'KeyS') {
|
|
||||||
text.insertChar('S');
|
|
||||||
event.preventDefault();
|
|
||||||
} else if (event.code === 'KeyD') {
|
|
||||||
text.insertChar('D');
|
|
||||||
event.preventDefault();
|
|
||||||
} else if (event.code === 'KeyC') {
|
|
||||||
text.insertChar('C');
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
} else if (event.altKey) {
|
|
||||||
if (text.processAltKey(event.code, event.shiftKey)) {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
} else if (!event.ctrlKey) {
|
|
||||||
const newSymbol = getSymbolSubstitute(event.code, event.shiftKey);
|
|
||||||
if (newSymbol) {
|
|
||||||
text.replaceWith(newSymbol);
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [thisRef]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`flex flex-col ${dimensions} ${cursor}`}>
|
|
||||||
{label &&
|
|
||||||
<Label
|
|
||||||
text={label}
|
|
||||||
htmlFor={id}
|
|
||||||
className='mb-2'
|
|
||||||
/>}
|
|
||||||
<CodeMirror id={id}
|
|
||||||
ref={thisRef}
|
|
||||||
basicSetup={editorSetup}
|
|
||||||
theme={customTheme}
|
|
||||||
extensions={editorExtensions}
|
|
||||||
indentWithTab={false}
|
|
||||||
onChange={onChange}
|
|
||||||
editable={!disabled}
|
|
||||||
onKeyDown={handleInput}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default RSInput;
|
|
232
rsconcept/frontend/src/components/RefsInput/RefsInput.tsx
Normal file
232
rsconcept/frontend/src/components/RefsInput/RefsInput.tsx
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
|
||||||
|
import { Extension } from '@codemirror/state';
|
||||||
|
import { tags } from '@lezer/highlight';
|
||||||
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
|
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||||
|
import { EditorView } from 'codemirror';
|
||||||
|
import { RefObject, useCallback, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
|
import DlgEditReference from '../../dialogs/DlgEditReference';
|
||||||
|
import useResolveText from '../../hooks/useResolveText';
|
||||||
|
import { ReferenceType } from '../../models/language';
|
||||||
|
import { IConstituenta } from '../../models/rsform';
|
||||||
|
import { CodeMirrorWrapper } from '../../utils/codemirror';
|
||||||
|
import Label from '../common/Label';
|
||||||
|
import Modal from '../common/Modal';
|
||||||
|
import PrettyJson from '../common/PrettyJSON';
|
||||||
|
import { NaturalLanguage, ReferenceTokens } from './parse';
|
||||||
|
import { RefEntity } from './parse/parser.terms';
|
||||||
|
import { refsHoverTooltip } from './tooltip';
|
||||||
|
|
||||||
|
const editorSetup: BasicSetupOptions = {
|
||||||
|
highlightSpecialChars: false,
|
||||||
|
history: true,
|
||||||
|
drawSelection: false,
|
||||||
|
syntaxHighlighting: false,
|
||||||
|
defaultKeymap: true,
|
||||||
|
historyKeymap: true,
|
||||||
|
|
||||||
|
lineNumbers: false,
|
||||||
|
highlightActiveLineGutter: false,
|
||||||
|
foldGutter: false,
|
||||||
|
dropCursor: true,
|
||||||
|
allowMultipleSelections: false,
|
||||||
|
indentOnInput: false,
|
||||||
|
bracketMatching: false,
|
||||||
|
closeBrackets: false,
|
||||||
|
autocompletion: false,
|
||||||
|
rectangularSelection: false,
|
||||||
|
crosshairCursor: false,
|
||||||
|
highlightActiveLine: false,
|
||||||
|
highlightSelectionMatches: false,
|
||||||
|
closeBracketsKeymap: false,
|
||||||
|
searchKeymap: false,
|
||||||
|
foldKeymap: false,
|
||||||
|
completionKeymap: false,
|
||||||
|
lintKeymap: false
|
||||||
|
};
|
||||||
|
|
||||||
|
interface RefsInputInputProps
|
||||||
|
extends Pick<ReactCodeMirrorProps,
|
||||||
|
'id'| 'height' | 'value' | 'className' | 'onFocus' | 'onBlur' | 'placeholder'
|
||||||
|
> {
|
||||||
|
label?: string
|
||||||
|
innerref?: RefObject<ReactCodeMirrorRef> | undefined
|
||||||
|
onChange?: (newValue: string) => void
|
||||||
|
items?: IConstituenta[]
|
||||||
|
disabled?: boolean
|
||||||
|
|
||||||
|
initialValue?: string
|
||||||
|
value?: string
|
||||||
|
resolved?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function RefsInput({
|
||||||
|
id, label, innerref, disabled, items,
|
||||||
|
initialValue, value, resolved,
|
||||||
|
onFocus, onBlur, onChange,
|
||||||
|
...props
|
||||||
|
}: RefsInputInputProps) {
|
||||||
|
const { darkMode, colors } = useConceptTheme();
|
||||||
|
const { schema } = useRSForm();
|
||||||
|
|
||||||
|
const { resolveText, refsData } = useResolveText({schema: schema});
|
||||||
|
|
||||||
|
const [showResolve, setShowResolve] = useState(false);
|
||||||
|
const [isFocused, setIsFocused] = useState(false);
|
||||||
|
|
||||||
|
const [showEditor, setShowEditor] = useState(false);
|
||||||
|
const [currentType, setCurrentType] = useState<ReferenceType>(ReferenceType.ENTITY);
|
||||||
|
const [refText, setRefText] = useState('');
|
||||||
|
const [hintText, setHintText] = useState('');
|
||||||
|
const [basePosition, setBasePosition] = useState(0);
|
||||||
|
const [mainRefs, setMainRefs] = useState<string[]>([]);
|
||||||
|
|
||||||
|
const internalRef = useRef<ReactCodeMirrorRef>(null);
|
||||||
|
const thisRef = useMemo(
|
||||||
|
() => {
|
||||||
|
return innerref ?? internalRef;
|
||||||
|
}, [internalRef, innerref]);
|
||||||
|
|
||||||
|
const cursor = useMemo(() => !disabled ? 'cursor-text': 'cursor-default', [disabled]);
|
||||||
|
const customTheme: Extension = useMemo(
|
||||||
|
() => createTheme({
|
||||||
|
theme: darkMode ? 'dark' : 'light',
|
||||||
|
settings: {
|
||||||
|
fontFamily: 'inherit',
|
||||||
|
background: !disabled ? colors.bgInput : colors.bgDefault,
|
||||||
|
foreground: colors.fgDefault,
|
||||||
|
selection: colors.bgHover
|
||||||
|
},
|
||||||
|
styles: [
|
||||||
|
{ tag: tags.name, color: colors.fgPurple, cursor: 'default' }, // EntityReference
|
||||||
|
{ tag: tags.literal, color: colors.fgTeal, cursor: 'default' }, // SyntacticReference
|
||||||
|
{ tag: tags.comment, color: colors.fgRed }, // Error
|
||||||
|
]
|
||||||
|
}), [disabled, colors, darkMode]);
|
||||||
|
|
||||||
|
const editorExtensions = useMemo(
|
||||||
|
() => [
|
||||||
|
EditorView.lineWrapping,
|
||||||
|
NaturalLanguage,
|
||||||
|
refsHoverTooltip(schema?.items || [], colors)
|
||||||
|
], [schema?.items, colors]);
|
||||||
|
|
||||||
|
function handleChange(newValue: string) {
|
||||||
|
if (onChange) onChange(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFocusIn(event: React.FocusEvent<HTMLDivElement>) {
|
||||||
|
setIsFocused(true);
|
||||||
|
if (onFocus) onFocus(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFocusOut(event: React.FocusEvent<HTMLDivElement>) {
|
||||||
|
setIsFocused(false);
|
||||||
|
if (onBlur) onBlur(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleInput = useCallback(
|
||||||
|
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
|
if (!thisRef.current?.view) {
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.altKey) {
|
||||||
|
if (event.key === 'r' && value) {
|
||||||
|
event.preventDefault();
|
||||||
|
resolveText(value, () => {
|
||||||
|
setShowResolve(true);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (event.ctrlKey && event.code === 'Space') {
|
||||||
|
const wrap = new CodeMirrorWrapper(thisRef.current as Required<ReactCodeMirrorRef>);
|
||||||
|
wrap.fixSelection(ReferenceTokens);
|
||||||
|
const nodes = wrap.getEnvelopingNodes(ReferenceTokens);
|
||||||
|
if (nodes.length !== 1) {
|
||||||
|
setCurrentType(ReferenceType.ENTITY);
|
||||||
|
setRefText('');
|
||||||
|
setHintText(wrap.getSelectionText());
|
||||||
|
} else {
|
||||||
|
setCurrentType(nodes[0].type.id === RefEntity ? ReferenceType.ENTITY : ReferenceType.SYNTACTIC);
|
||||||
|
setRefText(wrap.getSelectionText());
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection = wrap.getSelection();
|
||||||
|
const mainNodes = wrap.getAllNodes([RefEntity]).filter(node => node.from >= selection.to || node.to <= selection.from);
|
||||||
|
setMainRefs(mainNodes.map(node => wrap.getText(node.from, node.to)));
|
||||||
|
setBasePosition(mainNodes.filter(node => node.to <= selection.from).length);
|
||||||
|
|
||||||
|
setShowEditor(true);
|
||||||
|
}
|
||||||
|
}, [thisRef, resolveText, value]);
|
||||||
|
|
||||||
|
const handleInputReference = useCallback(
|
||||||
|
(referenceText: string) => {
|
||||||
|
if (!thisRef.current?.view) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
thisRef.current.view.focus();
|
||||||
|
const wrap = new CodeMirrorWrapper(thisRef.current as Required<ReactCodeMirrorRef>);
|
||||||
|
wrap.replaceWith(referenceText);
|
||||||
|
}, [thisRef]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{ showEditor &&
|
||||||
|
<DlgEditReference
|
||||||
|
hideWindow={() => setShowEditor(false)}
|
||||||
|
items={items ?? []}
|
||||||
|
initial={{
|
||||||
|
type: currentType,
|
||||||
|
refRaw: refText,
|
||||||
|
text: hintText,
|
||||||
|
basePosition: basePosition,
|
||||||
|
mainRefs: mainRefs
|
||||||
|
}}
|
||||||
|
onSave={handleInputReference}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{ showResolve &&
|
||||||
|
<Modal
|
||||||
|
readonly
|
||||||
|
hideWindow={() => setShowResolve(false)}
|
||||||
|
>
|
||||||
|
<div className='max-h-[60vh] max-w-[80vw] overflow-auto'>
|
||||||
|
<PrettyJson data={refsData} />
|
||||||
|
</div>
|
||||||
|
</Modal>}
|
||||||
|
<div className={`flex flex-col w-full ${cursor}`}>
|
||||||
|
{label &&
|
||||||
|
<Label
|
||||||
|
text={label}
|
||||||
|
htmlFor={id}
|
||||||
|
className='mb-2'
|
||||||
|
/>}
|
||||||
|
<CodeMirror id={id}
|
||||||
|
ref={thisRef}
|
||||||
|
basicSetup={editorSetup}
|
||||||
|
theme={customTheme}
|
||||||
|
extensions={editorExtensions}
|
||||||
|
|
||||||
|
value={isFocused ? value : (value !== initialValue || showEditor ? value : resolved)}
|
||||||
|
|
||||||
|
indentWithTab={false}
|
||||||
|
onChange={handleChange}
|
||||||
|
editable={!disabled}
|
||||||
|
onKeyDown={handleInput}
|
||||||
|
onFocus={handleFocusIn}
|
||||||
|
onBlur={handleFocusOut}
|
||||||
|
spellCheck
|
||||||
|
// spellCheck= // TODO: figure out while automatic spellcheck doesnt work or implement with extension
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RefsInput;
|
|
@ -1,232 +1 @@
|
||||||
|
export { default } from './RefsInput';
|
||||||
import { Extension } from '@codemirror/state';
|
|
||||||
import { tags } from '@lezer/highlight';
|
|
||||||
import { createTheme } from '@uiw/codemirror-themes';
|
|
||||||
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
|
||||||
import { EditorView } from 'codemirror';
|
|
||||||
import { RefObject, useCallback, useMemo, useRef, useState } from 'react';
|
|
||||||
|
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
|
||||||
import DlgEditReference from '../../dialogs/DlgEditReference';
|
|
||||||
import useResolveText from '../../hooks/useResolveText';
|
|
||||||
import { ReferenceType } from '../../models/language';
|
|
||||||
import { IConstituenta } from '../../models/rsform';
|
|
||||||
import { CodeMirrorWrapper } from '../../utils/codemirror';
|
|
||||||
import Label from '../Common/Label';
|
|
||||||
import Modal from '../Common/Modal';
|
|
||||||
import PrettyJson from '../Common/PrettyJSON';
|
|
||||||
import { NaturalLanguage, ReferenceTokens } from './parse';
|
|
||||||
import { RefEntity } from './parse/parser.terms';
|
|
||||||
import { refsHoverTooltip } from './tooltip';
|
|
||||||
|
|
||||||
const editorSetup: BasicSetupOptions = {
|
|
||||||
highlightSpecialChars: false,
|
|
||||||
history: true,
|
|
||||||
drawSelection: false,
|
|
||||||
syntaxHighlighting: false,
|
|
||||||
defaultKeymap: true,
|
|
||||||
historyKeymap: true,
|
|
||||||
|
|
||||||
lineNumbers: false,
|
|
||||||
highlightActiveLineGutter: false,
|
|
||||||
foldGutter: false,
|
|
||||||
dropCursor: true,
|
|
||||||
allowMultipleSelections: false,
|
|
||||||
indentOnInput: false,
|
|
||||||
bracketMatching: false,
|
|
||||||
closeBrackets: false,
|
|
||||||
autocompletion: false,
|
|
||||||
rectangularSelection: false,
|
|
||||||
crosshairCursor: false,
|
|
||||||
highlightActiveLine: false,
|
|
||||||
highlightSelectionMatches: false,
|
|
||||||
closeBracketsKeymap: false,
|
|
||||||
searchKeymap: false,
|
|
||||||
foldKeymap: false,
|
|
||||||
completionKeymap: false,
|
|
||||||
lintKeymap: false
|
|
||||||
};
|
|
||||||
|
|
||||||
interface RefsInputInputProps
|
|
||||||
extends Pick<ReactCodeMirrorProps,
|
|
||||||
'id'| 'height' | 'value' | 'className' | 'onFocus' | 'onBlur' | 'placeholder'
|
|
||||||
> {
|
|
||||||
label?: string
|
|
||||||
innerref?: RefObject<ReactCodeMirrorRef> | undefined
|
|
||||||
onChange?: (newValue: string) => void
|
|
||||||
items?: IConstituenta[]
|
|
||||||
disabled?: boolean
|
|
||||||
|
|
||||||
initialValue?: string
|
|
||||||
value?: string
|
|
||||||
resolved?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
function RefsInput({
|
|
||||||
id, label, innerref, disabled, items,
|
|
||||||
initialValue, value, resolved,
|
|
||||||
onFocus, onBlur, onChange,
|
|
||||||
...props
|
|
||||||
}: RefsInputInputProps) {
|
|
||||||
const { darkMode, colors } = useConceptTheme();
|
|
||||||
const { schema } = useRSForm();
|
|
||||||
|
|
||||||
const { resolveText, refsData } = useResolveText({schema: schema});
|
|
||||||
|
|
||||||
const [showResolve, setShowResolve] = useState(false);
|
|
||||||
const [isFocused, setIsFocused] = useState(false);
|
|
||||||
|
|
||||||
const [showEditor, setShowEditor] = useState(false);
|
|
||||||
const [currentType, setCurrentType] = useState<ReferenceType>(ReferenceType.ENTITY);
|
|
||||||
const [refText, setRefText] = useState('');
|
|
||||||
const [hintText, setHintText] = useState('');
|
|
||||||
const [basePosition, setBasePosition] = useState(0);
|
|
||||||
const [mainRefs, setMainRefs] = useState<string[]>([]);
|
|
||||||
|
|
||||||
const internalRef = useRef<ReactCodeMirrorRef>(null);
|
|
||||||
const thisRef = useMemo(
|
|
||||||
() => {
|
|
||||||
return innerref ?? internalRef;
|
|
||||||
}, [internalRef, innerref]);
|
|
||||||
|
|
||||||
const cursor = useMemo(() => !disabled ? 'cursor-text': 'cursor-default', [disabled]);
|
|
||||||
const customTheme: Extension = useMemo(
|
|
||||||
() => createTheme({
|
|
||||||
theme: darkMode ? 'dark' : 'light',
|
|
||||||
settings: {
|
|
||||||
fontFamily: 'inherit',
|
|
||||||
background: !disabled ? colors.bgInput : colors.bgDefault,
|
|
||||||
foreground: colors.fgDefault,
|
|
||||||
selection: colors.bgHover
|
|
||||||
},
|
|
||||||
styles: [
|
|
||||||
{ tag: tags.name, color: colors.fgPurple, cursor: 'default' }, // EntityReference
|
|
||||||
{ tag: tags.literal, color: colors.fgTeal, cursor: 'default' }, // SyntacticReference
|
|
||||||
{ tag: tags.comment, color: colors.fgRed }, // Error
|
|
||||||
]
|
|
||||||
}), [disabled, colors, darkMode]);
|
|
||||||
|
|
||||||
const editorExtensions = useMemo(
|
|
||||||
() => [
|
|
||||||
EditorView.lineWrapping,
|
|
||||||
NaturalLanguage,
|
|
||||||
refsHoverTooltip(schema?.items || [], colors)
|
|
||||||
], [schema?.items, colors]);
|
|
||||||
|
|
||||||
function handleChange(newValue: string) {
|
|
||||||
if (onChange) onChange(newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFocusIn(event: React.FocusEvent<HTMLDivElement>) {
|
|
||||||
setIsFocused(true);
|
|
||||||
if (onFocus) onFocus(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFocusOut(event: React.FocusEvent<HTMLDivElement>) {
|
|
||||||
setIsFocused(false);
|
|
||||||
if (onBlur) onBlur(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleInput = useCallback(
|
|
||||||
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
|
||||||
if (!thisRef.current?.view) {
|
|
||||||
event.preventDefault();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.altKey) {
|
|
||||||
if (event.key === 'r' && value) {
|
|
||||||
event.preventDefault();
|
|
||||||
resolveText(value, () => {
|
|
||||||
setShowResolve(true);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (event.ctrlKey && event.code === 'Space') {
|
|
||||||
const wrap = new CodeMirrorWrapper(thisRef.current as Required<ReactCodeMirrorRef>);
|
|
||||||
wrap.fixSelection(ReferenceTokens);
|
|
||||||
const nodes = wrap.getEnvelopingNodes(ReferenceTokens);
|
|
||||||
if (nodes.length !== 1) {
|
|
||||||
setCurrentType(ReferenceType.ENTITY);
|
|
||||||
setRefText('');
|
|
||||||
setHintText(wrap.getSelectionText());
|
|
||||||
} else {
|
|
||||||
setCurrentType(nodes[0].type.id === RefEntity ? ReferenceType.ENTITY : ReferenceType.SYNTACTIC);
|
|
||||||
setRefText(wrap.getSelectionText());
|
|
||||||
}
|
|
||||||
|
|
||||||
const selection = wrap.getSelection();
|
|
||||||
const mainNodes = wrap.getAllNodes([RefEntity]).filter(node => node.from >= selection.to || node.to <= selection.from);
|
|
||||||
setMainRefs(mainNodes.map(node => wrap.getText(node.from, node.to)));
|
|
||||||
setBasePosition(mainNodes.filter(node => node.to <= selection.from).length);
|
|
||||||
|
|
||||||
setShowEditor(true);
|
|
||||||
}
|
|
||||||
}, [thisRef, resolveText, value]);
|
|
||||||
|
|
||||||
const handleInputReference = useCallback(
|
|
||||||
(referenceText: string) => {
|
|
||||||
if (!thisRef.current?.view) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
thisRef.current.view.focus();
|
|
||||||
const wrap = new CodeMirrorWrapper(thisRef.current as Required<ReactCodeMirrorRef>);
|
|
||||||
wrap.replaceWith(referenceText);
|
|
||||||
}, [thisRef]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{ showEditor &&
|
|
||||||
<DlgEditReference
|
|
||||||
hideWindow={() => setShowEditor(false)}
|
|
||||||
items={items ?? []}
|
|
||||||
initial={{
|
|
||||||
type: currentType,
|
|
||||||
refRaw: refText,
|
|
||||||
text: hintText,
|
|
||||||
basePosition: basePosition,
|
|
||||||
mainRefs: mainRefs
|
|
||||||
}}
|
|
||||||
onSave={handleInputReference}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
{ showResolve &&
|
|
||||||
<Modal
|
|
||||||
readonly
|
|
||||||
hideWindow={() => setShowResolve(false)}
|
|
||||||
>
|
|
||||||
<div className='max-h-[60vh] max-w-[80vw] overflow-auto'>
|
|
||||||
<PrettyJson data={refsData} />
|
|
||||||
</div>
|
|
||||||
</Modal>}
|
|
||||||
<div className={`flex flex-col w-full ${cursor}`}>
|
|
||||||
{label &&
|
|
||||||
<Label
|
|
||||||
text={label}
|
|
||||||
htmlFor={id}
|
|
||||||
className='mb-2'
|
|
||||||
/>}
|
|
||||||
<CodeMirror id={id}
|
|
||||||
ref={thisRef}
|
|
||||||
basicSetup={editorSetup}
|
|
||||||
theme={customTheme}
|
|
||||||
extensions={editorExtensions}
|
|
||||||
|
|
||||||
value={isFocused ? value : (value !== initialValue || showEditor ? value : resolved)}
|
|
||||||
|
|
||||||
indentWithTab={false}
|
|
||||||
onChange={handleChange}
|
|
||||||
editable={!disabled}
|
|
||||||
onKeyDown={handleInput}
|
|
||||||
onFocus={handleFocusIn}
|
|
||||||
onBlur={handleFocusOut}
|
|
||||||
spellCheck
|
|
||||||
// spellCheck= // TODO: figure out while automatic spellcheck doesnt work or implement with extension
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default RefsInput;
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { useAuth } from '../context/AuthContext';
|
import { useAuth } from '../context/AuthContext';
|
||||||
import TextURL from './Common/TextURL';
|
import TextURL from './common/TextURL';
|
||||||
|
|
||||||
interface RequireAuthProps {
|
interface RequireAuthProps {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { IConstituenta } from '../../models/rsform';
|
||||||
import { isMockCst } from '../../models/rsformAPI';
|
import { isMockCst } from '../../models/rsformAPI';
|
||||||
import { colorfgCstStatus,IColorTheme } from '../../utils/color';
|
import { colorfgCstStatus,IColorTheme } from '../../utils/color';
|
||||||
import { describeExpressionStatus } from '../../utils/labels';
|
import { describeExpressionStatus } from '../../utils/labels';
|
||||||
import ConceptTooltip from '../Common/ConceptTooltip';
|
import ConceptTooltip from '../common/ConceptTooltip';
|
||||||
import ConstituentaTooltip from '../Help/ConstituentaTooltip';
|
import ConstituentaTooltip from '../Help/ConstituentaTooltip';
|
||||||
|
|
||||||
interface ConstituentaBadgeProps {
|
interface ConstituentaBadgeProps {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { IConstituenta } from '../../models/rsform';
|
||||||
import { matchConstituenta } from '../../models/rsformAPI';
|
import { matchConstituenta } from '../../models/rsformAPI';
|
||||||
import { prefixes } from '../../utils/constants';
|
import { prefixes } from '../../utils/constants';
|
||||||
import { describeConstituenta } from '../../utils/labels';
|
import { describeConstituenta } from '../../utils/labels';
|
||||||
import ConceptSearch from '../Common/ConceptSearch';
|
import ConceptSearch from '../common/ConceptSearch';
|
||||||
import DataTable, { createColumnHelper, IConditionalStyle } from '../DataTable';
|
import DataTable, { createColumnHelper, IConditionalStyle } from '../DataTable';
|
||||||
import ConstituentaBadge from './ConstituentaBadge';
|
import ConstituentaBadge from './ConstituentaBadge';
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import Checkbox from '../components/Common/Checkbox';
|
import Checkbox from '../components/common/Checkbox';
|
||||||
import Modal, { ModalProps } from '../components/Common/Modal';
|
import Modal, { ModalProps } from '../components/common/Modal';
|
||||||
import TextArea from '../components/Common/TextArea';
|
import TextArea from '../components/common/TextArea';
|
||||||
import TextInput from '../components/Common/TextInput';
|
import TextInput from '../components/common/TextInput';
|
||||||
import { useLibrary } from '../context/LibraryContext';
|
import { useLibrary } from '../context/LibraryContext';
|
||||||
import { useConceptNavigation } from '../context/NagivationContext';
|
import { useConceptNavigation } from '../context/NagivationContext';
|
||||||
import { useRSForm } from '../context/RSFormContext';
|
import { useRSForm } from '../context/RSFormContext';
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { createColumnHelper } from '@tanstack/react-table';
|
import { createColumnHelper } from '@tanstack/react-table';
|
||||||
import { Dispatch, useCallback, useEffect, useMemo, useState } from 'react';
|
import { Dispatch, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import MiniButton from '../../components/Common/MiniButton';
|
import MiniButton from '../../components/common/MiniButton';
|
||||||
import DataTable, { IConditionalStyle } from '../../components/DataTable';
|
import DataTable, { IConditionalStyle } from '../../components/DataTable';
|
||||||
import { CheckIcon, CrossIcon } from '../../components/Icons';
|
import { CheckIcon, CrossIcon } from '../../components/Icons';
|
||||||
import RSInput from '../../components/RSInput';
|
import RSInput from '../../components/RSInput';
|
||||||
import ConstituentaPicker from '../../components/Shared/ConstituentaPicker';
|
import ConstituentaPicker from '../../components/shared/ConstituentaPicker';
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
import { IConstituenta, IRSForm } from '../../models/rsform';
|
import { IConstituenta, IRSForm } from '../../models/rsform';
|
||||||
import { IArgumentValue } from '../../models/rslang';
|
import { IArgumentValue } from '../../models/rslang';
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Dispatch } from 'react';
|
import { Dispatch } from 'react';
|
||||||
|
|
||||||
import SelectSingle from '../../components/Common/SelectSingle';
|
import SelectSingle from '../../components/common/SelectSingle';
|
||||||
import TextArea from '../../components/Common/TextArea';
|
import TextArea from '../../components/common/TextArea';
|
||||||
import TextInput from '../../components/Common/TextInput';
|
import TextInput from '../../components/common/TextInput';
|
||||||
import RSInput from '../../components/RSInput';
|
import RSInput from '../../components/RSInput';
|
||||||
import { CstType, ICstCreateData } from '../../models/rsform';
|
import { CstType, ICstCreateData } from '../../models/rsform';
|
||||||
import { labelCstType } from '../../utils/labels';
|
import { labelCstType } from '../../utils/labels';
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
import { useLayoutEffect, useState } from 'react';
|
||||||
|
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
|
|
||||||
|
import ConceptTab from '../../components/common/ConceptTab';
|
||||||
|
import ConceptTooltip from '../../components/common/ConceptTooltip';
|
||||||
|
import Modal, { ModalProps } from '../../components/common/Modal';
|
||||||
|
import HelpRSTemplates from '../../components/Help/HelpRSTemplates';
|
||||||
|
import { HelpIcon } from '../../components/Icons';
|
||||||
|
import usePartialUpdate from '../../hooks/usePartialUpdate';
|
||||||
|
import { CstType, ICstCreateData, IRSForm } from '../../models/rsform';
|
||||||
|
import { inferTemplatedType, substituteTemplateArgs } from '../../models/rslangAPI';
|
||||||
|
import { createAliasFor, validateCstAlias } from '../../utils/misc';
|
||||||
|
import ArgumentsTab, { IArgumentsState } from './ArgumentsTab';
|
||||||
|
import ConstituentaTab from './ConstituentaTab';
|
||||||
|
import TemplateTab, { ITemplateState } from './TemplateTab';
|
||||||
|
|
||||||
|
interface DlgConstituentaTemplateProps
|
||||||
|
extends Pick<ModalProps, 'hideWindow'> {
|
||||||
|
schema: IRSForm
|
||||||
|
onCreate: (data: ICstCreateData) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TabID {
|
||||||
|
TEMPLATE = 0,
|
||||||
|
ARGUMENTS = 1,
|
||||||
|
CONSTITUENTA = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
function DlgConstituentaTemplate({ hideWindow, schema, onCreate }: DlgConstituentaTemplateProps) {
|
||||||
|
const [ validated, setValidated ] = useState(false);
|
||||||
|
const [ template, updateTemplate ] = usePartialUpdate<ITemplateState>({});
|
||||||
|
const [ substitutes, updateSubstitutes ] = usePartialUpdate<IArgumentsState>({
|
||||||
|
definition: '',
|
||||||
|
arguments: []
|
||||||
|
});
|
||||||
|
const [constituenta, updateConstituenta] = usePartialUpdate<ICstCreateData>({
|
||||||
|
cst_type: CstType.TERM,
|
||||||
|
insert_after: null,
|
||||||
|
alias: '',
|
||||||
|
convention: '',
|
||||||
|
definition_formal: '',
|
||||||
|
definition_raw: '',
|
||||||
|
term_raw: '',
|
||||||
|
term_forms: []
|
||||||
|
});
|
||||||
|
|
||||||
|
const [ activeTab, setActiveTab ] = useState(TabID.TEMPLATE);
|
||||||
|
|
||||||
|
const handleSubmit = () => onCreate(constituenta);
|
||||||
|
|
||||||
|
useLayoutEffect(
|
||||||
|
() => {
|
||||||
|
updateConstituenta({ alias: createAliasFor(constituenta.cst_type, schema) });
|
||||||
|
}, [constituenta.cst_type, updateConstituenta, schema]);
|
||||||
|
|
||||||
|
useLayoutEffect(
|
||||||
|
() => {
|
||||||
|
setValidated(validateCstAlias(constituenta.alias, constituenta.cst_type, schema));
|
||||||
|
}, [constituenta.alias, constituenta.cst_type, schema]);
|
||||||
|
|
||||||
|
useLayoutEffect(
|
||||||
|
() => {
|
||||||
|
if (!template.prototype) {
|
||||||
|
updateConstituenta({
|
||||||
|
definition_raw: '',
|
||||||
|
definition_formal: '',
|
||||||
|
term_raw: ''
|
||||||
|
});
|
||||||
|
updateSubstitutes({
|
||||||
|
definition: '',
|
||||||
|
arguments: []
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
updateConstituenta({
|
||||||
|
cst_type: template.prototype.cst_type,
|
||||||
|
definition_raw: template.prototype.definition_raw,
|
||||||
|
definition_formal: template.prototype.definition_formal,
|
||||||
|
term_raw: template.prototype.term_raw
|
||||||
|
});
|
||||||
|
updateSubstitutes({
|
||||||
|
definition: template.prototype.definition_formal,
|
||||||
|
arguments: template.prototype.parse.args.map(
|
||||||
|
arg => ({
|
||||||
|
alias: arg.alias,
|
||||||
|
typification: arg.typification,
|
||||||
|
value: ''
|
||||||
|
})
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [template.prototype, updateConstituenta, updateSubstitutes]);
|
||||||
|
|
||||||
|
useLayoutEffect(
|
||||||
|
() => {
|
||||||
|
if (substitutes.arguments.length === 0 || !template.prototype) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const definition = substituteTemplateArgs(template.prototype.definition_formal, substitutes.arguments);
|
||||||
|
const type = inferTemplatedType(template.prototype.cst_type, substitutes.arguments);
|
||||||
|
updateConstituenta({
|
||||||
|
cst_type: type,
|
||||||
|
definition_formal: definition,
|
||||||
|
});
|
||||||
|
updateSubstitutes({
|
||||||
|
definition: definition,
|
||||||
|
});
|
||||||
|
}, [substitutes.arguments, template.prototype, updateConstituenta, updateSubstitutes]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title='Создание конституенты из шаблона'
|
||||||
|
hideWindow={hideWindow}
|
||||||
|
canSubmit={validated}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
submitText='Создать'
|
||||||
|
>
|
||||||
|
<div className='max-w-[40rem] min-w-[40rem] min-h-[35rem] px-2 mb-1'>
|
||||||
|
<Tabs
|
||||||
|
selectedIndex={activeTab}
|
||||||
|
onSelect={setActiveTab}
|
||||||
|
defaultFocus
|
||||||
|
forceRenderTabPanel
|
||||||
|
|
||||||
|
selectedTabClassName='clr-selected'
|
||||||
|
className='flex flex-col items-center'
|
||||||
|
>
|
||||||
|
<div className='flex gap-1 pl-6 mb-3'>
|
||||||
|
<TabList className='flex items-start font-semibold text-center border select-none clr-controls small-caps'>
|
||||||
|
<ConceptTab tooltip='Выбор шаблона выражения' className='border-r w-[8rem]'>
|
||||||
|
Шаблон
|
||||||
|
</ConceptTab>
|
||||||
|
<ConceptTab tooltip='Подстановка аргументов шаблона' className='border-r w-[8rem]'>
|
||||||
|
Аргументы
|
||||||
|
</ConceptTab>
|
||||||
|
<ConceptTab tooltip='Редактирование атрибутов конституенты' className='w-[8rem]'>
|
||||||
|
Конституента
|
||||||
|
</ConceptTab>
|
||||||
|
</TabList>
|
||||||
|
|
||||||
|
<div id='templates-help' className='px-1 py-1'>
|
||||||
|
<HelpIcon color='text-primary' size={5} />
|
||||||
|
</div>
|
||||||
|
<ConceptTooltip
|
||||||
|
anchorSelect='#templates-help'
|
||||||
|
className='max-w-[30rem] z-modal-tooltip'
|
||||||
|
offset={4}
|
||||||
|
>
|
||||||
|
<HelpRSTemplates />
|
||||||
|
</ConceptTooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='w-full'>
|
||||||
|
<TabPanel style={{ display: activeTab === TabID.TEMPLATE ? '': 'none' }}>
|
||||||
|
<TemplateTab
|
||||||
|
state={template}
|
||||||
|
partialUpdate={updateTemplate}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
|
||||||
|
<TabPanel style={{ display: activeTab === TabID.ARGUMENTS ? '': 'none' }}>
|
||||||
|
<ArgumentsTab
|
||||||
|
schema={schema}
|
||||||
|
state={substitutes}
|
||||||
|
partialUpdate={updateSubstitutes}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
|
||||||
|
<TabPanel style={{ display: activeTab === TabID.CONSTITUENTA ? '': 'none' }}>
|
||||||
|
<ConstituentaTab
|
||||||
|
state={constituenta}
|
||||||
|
partialUpdate={updateConstituenta}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
</div>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
</Modal>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DlgConstituentaTemplate;
|
|
@ -1,9 +1,9 @@
|
||||||
import { Dispatch, useEffect, useMemo, useState } from 'react';
|
import { Dispatch, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import SelectSingle from '../../components/Common/SelectSingle';
|
import SelectSingle from '../../components/common/SelectSingle';
|
||||||
import TextArea from '../../components/Common/TextArea';
|
import TextArea from '../../components/common/TextArea';
|
||||||
import RSInput from '../../components/RSInput';
|
import RSInput from '../../components/RSInput';
|
||||||
import ConstituentaPicker from '../../components/Shared/ConstituentaPicker';
|
import ConstituentaPicker from '../../components/shared/ConstituentaPicker';
|
||||||
import { useLibrary } from '../../context/LibraryContext';
|
import { useLibrary } from '../../context/LibraryContext';
|
||||||
import { CATEGORY_CST_TYPE, IConstituenta, IRSForm } from '../../models/rsform';
|
import { CATEGORY_CST_TYPE, IConstituenta, IRSForm } from '../../models/rsform';
|
||||||
import { applyFilterCategory } from '../../models/rsformAPI';
|
import { applyFilterCategory } from '../../models/rsformAPI';
|
||||||
|
|
|
@ -1,180 +1 @@
|
||||||
import { useLayoutEffect, useState } from 'react';
|
export { default } from './DlgConstituentaTemplate';
|
||||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
|
||||||
|
|
||||||
import ConceptTab from '../../components/Common/ConceptTab';
|
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
|
||||||
import Modal, { ModalProps } from '../../components/Common/Modal';
|
|
||||||
import HelpRSTemplates from '../../components/Help/HelpRSTemplates';
|
|
||||||
import { HelpIcon } from '../../components/Icons';
|
|
||||||
import usePartialUpdate from '../../hooks/usePartialUpdate';
|
|
||||||
import { CstType, ICstCreateData, IRSForm } from '../../models/rsform';
|
|
||||||
import { inferTemplatedType, substituteTemplateArgs } from '../../models/rslangAPI';
|
|
||||||
import { createAliasFor, validateCstAlias } from '../../utils/misc';
|
|
||||||
import ArgumentsTab, { IArgumentsState } from './ArgumentsTab';
|
|
||||||
import ConstituentaTab from './ConstituentaTab';
|
|
||||||
import TemplateTab, { ITemplateState } from './TemplateTab';
|
|
||||||
|
|
||||||
interface DlgConstituentaTemplateProps
|
|
||||||
extends Pick<ModalProps, 'hideWindow'> {
|
|
||||||
schema: IRSForm
|
|
||||||
onCreate: (data: ICstCreateData) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum TabID {
|
|
||||||
TEMPLATE = 0,
|
|
||||||
ARGUMENTS = 1,
|
|
||||||
CONSTITUENTA = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
function DlgConstituentaTemplate({ hideWindow, schema, onCreate }: DlgConstituentaTemplateProps) {
|
|
||||||
const [ validated, setValidated ] = useState(false);
|
|
||||||
const [ template, updateTemplate ] = usePartialUpdate<ITemplateState>({});
|
|
||||||
const [ substitutes, updateSubstitutes ] = usePartialUpdate<IArgumentsState>({
|
|
||||||
definition: '',
|
|
||||||
arguments: []
|
|
||||||
});
|
|
||||||
const [constituenta, updateConstituenta] = usePartialUpdate<ICstCreateData>({
|
|
||||||
cst_type: CstType.TERM,
|
|
||||||
insert_after: null,
|
|
||||||
alias: '',
|
|
||||||
convention: '',
|
|
||||||
definition_formal: '',
|
|
||||||
definition_raw: '',
|
|
||||||
term_raw: '',
|
|
||||||
term_forms: []
|
|
||||||
});
|
|
||||||
|
|
||||||
const [ activeTab, setActiveTab ] = useState(TabID.TEMPLATE);
|
|
||||||
|
|
||||||
const handleSubmit = () => onCreate(constituenta);
|
|
||||||
|
|
||||||
useLayoutEffect(
|
|
||||||
() => {
|
|
||||||
updateConstituenta({ alias: createAliasFor(constituenta.cst_type, schema) });
|
|
||||||
}, [constituenta.cst_type, updateConstituenta, schema]);
|
|
||||||
|
|
||||||
useLayoutEffect(
|
|
||||||
() => {
|
|
||||||
setValidated(validateCstAlias(constituenta.alias, constituenta.cst_type, schema));
|
|
||||||
}, [constituenta.alias, constituenta.cst_type, schema]);
|
|
||||||
|
|
||||||
useLayoutEffect(
|
|
||||||
() => {
|
|
||||||
if (!template.prototype) {
|
|
||||||
updateConstituenta({
|
|
||||||
definition_raw: '',
|
|
||||||
definition_formal: '',
|
|
||||||
term_raw: ''
|
|
||||||
});
|
|
||||||
updateSubstitutes({
|
|
||||||
definition: '',
|
|
||||||
arguments: []
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
updateConstituenta({
|
|
||||||
cst_type: template.prototype.cst_type,
|
|
||||||
definition_raw: template.prototype.definition_raw,
|
|
||||||
definition_formal: template.prototype.definition_formal,
|
|
||||||
term_raw: template.prototype.term_raw
|
|
||||||
});
|
|
||||||
updateSubstitutes({
|
|
||||||
definition: template.prototype.definition_formal,
|
|
||||||
arguments: template.prototype.parse.args.map(
|
|
||||||
arg => ({
|
|
||||||
alias: arg.alias,
|
|
||||||
typification: arg.typification,
|
|
||||||
value: ''
|
|
||||||
})
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [template.prototype, updateConstituenta, updateSubstitutes]);
|
|
||||||
|
|
||||||
useLayoutEffect(
|
|
||||||
() => {
|
|
||||||
if (substitutes.arguments.length === 0 || !template.prototype) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const definition = substituteTemplateArgs(template.prototype.definition_formal, substitutes.arguments);
|
|
||||||
const type = inferTemplatedType(template.prototype.cst_type, substitutes.arguments);
|
|
||||||
updateConstituenta({
|
|
||||||
cst_type: type,
|
|
||||||
definition_formal: definition,
|
|
||||||
});
|
|
||||||
updateSubstitutes({
|
|
||||||
definition: definition,
|
|
||||||
});
|
|
||||||
}, [substitutes.arguments, template.prototype, updateConstituenta, updateSubstitutes]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title='Создание конституенты из шаблона'
|
|
||||||
hideWindow={hideWindow}
|
|
||||||
canSubmit={validated}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
submitText='Создать'
|
|
||||||
>
|
|
||||||
<div className='max-w-[40rem] min-w-[40rem] min-h-[35rem] px-2 mb-1'>
|
|
||||||
<Tabs
|
|
||||||
selectedIndex={activeTab}
|
|
||||||
onSelect={setActiveTab}
|
|
||||||
defaultFocus
|
|
||||||
forceRenderTabPanel
|
|
||||||
|
|
||||||
selectedTabClassName='clr-selected'
|
|
||||||
className='flex flex-col items-center'
|
|
||||||
>
|
|
||||||
<div className='flex gap-1 pl-6 mb-3'>
|
|
||||||
<TabList className='flex items-start font-semibold text-center border select-none clr-controls small-caps'>
|
|
||||||
<ConceptTab tooltip='Выбор шаблона выражения' className='border-r w-[8rem]'>
|
|
||||||
Шаблон
|
|
||||||
</ConceptTab>
|
|
||||||
<ConceptTab tooltip='Подстановка аргументов шаблона' className='border-r w-[8rem]'>
|
|
||||||
Аргументы
|
|
||||||
</ConceptTab>
|
|
||||||
<ConceptTab tooltip='Редактирование атрибутов конституенты' className='w-[8rem]'>
|
|
||||||
Конституента
|
|
||||||
</ConceptTab>
|
|
||||||
</TabList>
|
|
||||||
|
|
||||||
<div id='templates-help' className='px-1 py-1'>
|
|
||||||
<HelpIcon color='text-primary' size={5} />
|
|
||||||
</div>
|
|
||||||
<ConceptTooltip
|
|
||||||
anchorSelect='#templates-help'
|
|
||||||
className='max-w-[30rem] z-modal-tooltip'
|
|
||||||
offset={4}
|
|
||||||
>
|
|
||||||
<HelpRSTemplates />
|
|
||||||
</ConceptTooltip>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='w-full'>
|
|
||||||
<TabPanel style={{ display: activeTab === TabID.TEMPLATE ? '': 'none' }}>
|
|
||||||
<TemplateTab
|
|
||||||
state={template}
|
|
||||||
partialUpdate={updateTemplate}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
|
||||||
|
|
||||||
<TabPanel style={{ display: activeTab === TabID.ARGUMENTS ? '': 'none' }}>
|
|
||||||
<ArgumentsTab
|
|
||||||
schema={schema}
|
|
||||||
state={substitutes}
|
|
||||||
partialUpdate={updateSubstitutes}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
|
||||||
|
|
||||||
<TabPanel style={{ display: activeTab === TabID.CONSTITUENTA ? '': 'none' }}>
|
|
||||||
<ConstituentaTab
|
|
||||||
state={constituenta}
|
|
||||||
partialUpdate={updateConstituenta}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
|
||||||
</div>
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
|
||||||
</Modal>);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DlgConstituentaTemplate;
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { useEffect, useLayoutEffect, useState } from 'react';
|
import { useEffect, useLayoutEffect, useState } from 'react';
|
||||||
|
|
||||||
import Modal, { ModalProps } from '../components/Common/Modal';
|
import Modal, { ModalProps } from '../components/common/Modal';
|
||||||
import SelectSingle from '../components/Common/SelectSingle';
|
import SelectSingle from '../components/common/SelectSingle';
|
||||||
import TextArea from '../components/Common/TextArea';
|
import TextArea from '../components/common/TextArea';
|
||||||
import TextInput from '../components/Common/TextInput';
|
import TextInput from '../components/common/TextInput';
|
||||||
import RSInput from '../components/RSInput';
|
import RSInput from '../components/RSInput';
|
||||||
import usePartialUpdate from '../hooks/usePartialUpdate';
|
import usePartialUpdate from '../hooks/usePartialUpdate';
|
||||||
import { CstType,ICstCreateData, IRSForm } from '../models/rsform';
|
import { CstType,ICstCreateData, IRSForm } from '../models/rsform';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
|
||||||
import Checkbox from '../components/Common/Checkbox';
|
import Checkbox from '../components/common/Checkbox';
|
||||||
import Modal, { ModalProps } from '../components/Common/Modal';
|
import Modal, { ModalProps } from '../components/common/Modal';
|
||||||
import { useRSForm } from '../context/RSFormContext';
|
import { useRSForm } from '../context/RSFormContext';
|
||||||
import { prefixes } from '../utils/constants';
|
import { prefixes } from '../utils/constants';
|
||||||
import { labelConstituenta } from '../utils/labels';
|
import { labelConstituenta } from '../utils/labels';
|
||||||
|
|
|
@ -0,0 +1,272 @@
|
||||||
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import ConceptTooltip from '../../components/common/ConceptTooltip';
|
||||||
|
import Label from '../../components/common/Label';
|
||||||
|
import Modal from '../../components/common/Modal';
|
||||||
|
import SelectMulti from '../../components/common/SelectMulti';
|
||||||
|
import TextInput from '../../components/common/TextInput';
|
||||||
|
import HelpTerminologyControl from '../../components/Help/HelpTerminologyControl';
|
||||||
|
import { HelpIcon } from '../../components/Icons';
|
||||||
|
import ConstituentaPicker from '../../components/shared/ConstituentaPicker';
|
||||||
|
import { Grammeme, ReferenceType } from '../../models/language';
|
||||||
|
import { getCompatibleGrams, parseEntityReference, parseGrammemes, parseSyntacticReference } from '../../models/languageAPI';
|
||||||
|
import { CstMatchMode } from '../../models/miscelanious';
|
||||||
|
import { IConstituenta } from '../../models/rsform';
|
||||||
|
import { matchConstituenta } from '../../models/rsformAPI';
|
||||||
|
import { prefixes } from '../../utils/constants';
|
||||||
|
import { compareGrammemeOptions, IGrammemeOption, PremadeWordForms, SelectorGrammems } from '../../utils/selectors';
|
||||||
|
import ReferenceTypeButton from './ReferenceTypeButton';
|
||||||
|
import WordformButton from './WordformButton';
|
||||||
|
|
||||||
|
export interface IReferenceInputState {
|
||||||
|
type: ReferenceType
|
||||||
|
refRaw?: string
|
||||||
|
text?: string
|
||||||
|
mainRefs: string[]
|
||||||
|
basePosition: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DlgEditReferenceProps {
|
||||||
|
hideWindow: () => void
|
||||||
|
items: IConstituenta[]
|
||||||
|
initial: IReferenceInputState
|
||||||
|
onSave: (newRef: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferenceProps) {
|
||||||
|
const [type, setType] = useState<ReferenceType>(ReferenceType.ENTITY);
|
||||||
|
|
||||||
|
const [nominal, setNominal] = useState('');
|
||||||
|
const [offset, setOffset] = useState(1);
|
||||||
|
|
||||||
|
const [selectedCst, setSelectedCst] = useState<IConstituenta | undefined>(undefined);
|
||||||
|
const [alias, setAlias] = useState('');
|
||||||
|
const [term, setTerm] = useState('');
|
||||||
|
|
||||||
|
const [selectedGrams, setSelectedGrams] = useState<IGrammemeOption[]>([]);
|
||||||
|
const [gramOptions, setGramOptions] = useState<IGrammemeOption[]>([]);
|
||||||
|
|
||||||
|
const mainLink = useMemo(
|
||||||
|
() => {
|
||||||
|
const position = offset > 0 ? initial.basePosition + (offset - 1) : initial.basePosition + offset;
|
||||||
|
if (offset === 0 || position < 0 || position >= initial.mainRefs.length) {
|
||||||
|
return 'Некорректное значение смещения';
|
||||||
|
} else {
|
||||||
|
return initial.mainRefs[position];
|
||||||
|
}
|
||||||
|
}, [initial, offset]);
|
||||||
|
|
||||||
|
const isValid = useMemo(
|
||||||
|
() => {
|
||||||
|
if (type === ReferenceType.ENTITY) {
|
||||||
|
return alias !== '' && selectedGrams.length > 0;
|
||||||
|
} else if (type === ReferenceType.SYNTACTIC) {
|
||||||
|
return nominal !== '' && offset !== 0;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, [type, alias, selectedGrams, nominal, offset]);
|
||||||
|
|
||||||
|
function produceReference(): string {
|
||||||
|
if (type === ReferenceType.ENTITY) {
|
||||||
|
return `@{${alias}|${selectedGrams.map(gram => gram.value).join(',')}}`;
|
||||||
|
} else if (type === ReferenceType.SYNTACTIC) {
|
||||||
|
return `@{${offset}|${nominal}}`;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialization
|
||||||
|
useLayoutEffect(
|
||||||
|
() => {
|
||||||
|
setType(initial.type);
|
||||||
|
if (initial.refRaw) {
|
||||||
|
if (initial.type === ReferenceType.ENTITY) {
|
||||||
|
const ref = parseEntityReference(initial.refRaw);
|
||||||
|
setAlias(ref.entity);
|
||||||
|
const grams = parseGrammemes(ref.form);
|
||||||
|
setSelectedGrams(SelectorGrammems.filter(data => grams.includes(data.value)));
|
||||||
|
} else if (initial.type === ReferenceType.SYNTACTIC) {
|
||||||
|
const ref = parseSyntacticReference(initial.refRaw);
|
||||||
|
setOffset(ref.offset);
|
||||||
|
setNominal(ref.nominal);
|
||||||
|
}
|
||||||
|
} else if (initial.text) {
|
||||||
|
setNominal(initial.text ?? '');
|
||||||
|
}
|
||||||
|
}, [initial, items]);
|
||||||
|
|
||||||
|
// Filter grammemes when input changes
|
||||||
|
useEffect(
|
||||||
|
() => {
|
||||||
|
const compatible = getCompatibleGrams(
|
||||||
|
selectedGrams
|
||||||
|
.filter(data => Object.values(Grammeme).includes(data.value as Grammeme))
|
||||||
|
.map(data => data.value as Grammeme)
|
||||||
|
);
|
||||||
|
setGramOptions(SelectorGrammems.filter(({value}) => compatible.includes(value as Grammeme)));
|
||||||
|
}, [selectedGrams]);
|
||||||
|
|
||||||
|
// Update term when alias changes
|
||||||
|
useEffect(
|
||||||
|
() => {
|
||||||
|
const cst = items.find(item => item.alias === alias)
|
||||||
|
setTerm(cst?.term_resolved ?? '')
|
||||||
|
}, [alias, term, items]);
|
||||||
|
|
||||||
|
const handleSubmit = () => onSave(produceReference());
|
||||||
|
|
||||||
|
function handleSelectConstituenta(cst: IConstituenta) {
|
||||||
|
setAlias(cst.alias);
|
||||||
|
setSelectedCst(cst);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSelectGrams = useCallback(
|
||||||
|
(grams: Grammeme[]) => {
|
||||||
|
setSelectedGrams(SelectorGrammems.filter(({value}) => grams.includes(value as Grammeme)));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const FormButtons = useMemo(() => {
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col items-center w-full text-sm'>
|
||||||
|
<div className='flex flex-start'>
|
||||||
|
{PremadeWordForms.slice(0, 6).map(
|
||||||
|
(data, index) =>
|
||||||
|
<WordformButton id={`${prefixes.wordform_list}${index}`}
|
||||||
|
text={data.text} example={data.example} grams={data.grams}
|
||||||
|
isSelected={data.grams.every(gram => selectedGrams.find(item => item.value as Grammeme === gram))}
|
||||||
|
onSelectGrams={handleSelectGrams}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex flex-start'>
|
||||||
|
{PremadeWordForms.slice(6, 12).map(
|
||||||
|
(data, index) =>
|
||||||
|
<WordformButton id={`${prefixes.wordform_list}${index}`}
|
||||||
|
text={data.text} example={data.example} grams={data.grams}
|
||||||
|
isSelected={data.grams.every(gram => selectedGrams.find(item => item.value as Grammeme === gram))}
|
||||||
|
onSelectGrams={handleSelectGrams}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>);
|
||||||
|
}, [handleSelectGrams, selectedGrams]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title='Редактирование ссылки'
|
||||||
|
hideWindow={hideWindow}
|
||||||
|
submitText='Сохранить ссылку'
|
||||||
|
canSubmit={isValid}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
<div className='min-w-[40rem] max-w-[40rem] flex flex-col gap-4 mb-4 min-h-[34rem]'>
|
||||||
|
<div className='flex items-center self-center flex-start'>
|
||||||
|
<ReferenceTypeButton
|
||||||
|
type={ReferenceType.ENTITY}
|
||||||
|
onSelect={setType}
|
||||||
|
isSelected={type === ReferenceType.ENTITY}
|
||||||
|
/>
|
||||||
|
<ReferenceTypeButton
|
||||||
|
type={ReferenceType.SYNTACTIC}
|
||||||
|
onSelect={setType}
|
||||||
|
isSelected={type === ReferenceType.SYNTACTIC}
|
||||||
|
/>
|
||||||
|
<div id='terminology-help' className='px-1 py-1'>
|
||||||
|
<HelpIcon color='text-primary' size={5} />
|
||||||
|
</div>
|
||||||
|
<ConceptTooltip
|
||||||
|
anchorSelect='#terminology-help'
|
||||||
|
className='max-w-[30rem] z-modal-tooltip'
|
||||||
|
offset={4}
|
||||||
|
>
|
||||||
|
<HelpTerminologyControl />
|
||||||
|
</ConceptTooltip>
|
||||||
|
</div>
|
||||||
|
{type === ReferenceType.SYNTACTIC &&
|
||||||
|
<div className='flex flex-col gap-2'>
|
||||||
|
<div className='flex flex-start'>
|
||||||
|
<TextInput id='offset' type='number'
|
||||||
|
label='Смещение'
|
||||||
|
dimensions='max-w-[10rem]'
|
||||||
|
dense
|
||||||
|
value={offset}
|
||||||
|
onChange={event => setOffset(event.target.valueAsNumber)}
|
||||||
|
/>
|
||||||
|
<div className='self-center ml-2 text-sm font-semibold whitespace-nowrap'>
|
||||||
|
Основная ссылка:
|
||||||
|
</div>
|
||||||
|
<TextInput
|
||||||
|
dense
|
||||||
|
disabled
|
||||||
|
noBorder
|
||||||
|
value={mainLink}
|
||||||
|
dimensions='w-full text-sm'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<TextInput id='nominal' type='text'
|
||||||
|
dimensions='w-full'
|
||||||
|
label='Начальная форма'
|
||||||
|
placeholder='зависимое слово в начальной форме'
|
||||||
|
spellCheck
|
||||||
|
value={nominal}
|
||||||
|
onChange={event => setNominal(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>}
|
||||||
|
{type === ReferenceType.ENTITY &&
|
||||||
|
<div className='flex flex-col gap-2'>
|
||||||
|
<ConstituentaPicker
|
||||||
|
value={selectedCst}
|
||||||
|
data={items}
|
||||||
|
onSelectValue={handleSelectConstituenta}
|
||||||
|
prefixID={prefixes.cst_modal_list}
|
||||||
|
describeFunc={cst => cst.term_resolved}
|
||||||
|
matchFunc={(cst, filter) => matchConstituenta(cst, filter, CstMatchMode.TERM)}
|
||||||
|
prefilterFunc={cst => cst.term_resolved !== ''}
|
||||||
|
rows={8}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className='flex gap-4 flex-start'>
|
||||||
|
<TextInput
|
||||||
|
label='Отсылаемая конституента'
|
||||||
|
dimensions='max-w-[16rem] min-w-[16rem] whitespace-nowrap'
|
||||||
|
placeholder='Имя'
|
||||||
|
dense
|
||||||
|
value={alias}
|
||||||
|
onChange={event => setAlias(event.target.value)}
|
||||||
|
/>
|
||||||
|
<div className='flex items-center w-full flex-start'>
|
||||||
|
<div className='self-center text-sm font-semibold'>
|
||||||
|
Термин:
|
||||||
|
</div>
|
||||||
|
<TextInput
|
||||||
|
dense
|
||||||
|
disabled
|
||||||
|
noBorder
|
||||||
|
value={term}
|
||||||
|
tooltip={term}
|
||||||
|
dimensions='w-full text-sm'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{FormButtons}
|
||||||
|
<div className='flex items-center gap-10 flex-start'>
|
||||||
|
<Label text='Отсылаемая словоформа'/>
|
||||||
|
<SelectMulti
|
||||||
|
className='flex-grow h-full z-modal-top'
|
||||||
|
options={gramOptions}
|
||||||
|
placeholder='Выберите граммемы'
|
||||||
|
|
||||||
|
value={selectedGrams}
|
||||||
|
onChange={newValue => setSelectedGrams([...newValue].sort(compareGrammemeOptions))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>}
|
||||||
|
</div>
|
||||||
|
</Modal>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DlgEditReference;
|
|
@ -1,4 +1,4 @@
|
||||||
import SwitchButton from '../../components/Common/SwitchButton';
|
import SwitchButton from '../../components/common/SwitchButton';
|
||||||
import { ReferenceType } from '../../models/language';
|
import { ReferenceType } from '../../models/language';
|
||||||
import { labelReferenceType } from '../../utils/labels';
|
import { labelReferenceType } from '../../utils/labels';
|
||||||
|
|
||||||
|
|
|
@ -1,272 +1 @@
|
||||||
import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
export { default } from './DlgEditReference';
|
||||||
|
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
|
||||||
import Label from '../../components/Common/Label';
|
|
||||||
import Modal from '../../components/Common/Modal';
|
|
||||||
import SelectMulti from '../../components/Common/SelectMulti';
|
|
||||||
import TextInput from '../../components/Common/TextInput';
|
|
||||||
import HelpTerminologyControl from '../../components/Help/HelpTerminologyControl';
|
|
||||||
import { HelpIcon } from '../../components/Icons';
|
|
||||||
import ConstituentaPicker from '../../components/Shared/ConstituentaPicker';
|
|
||||||
import { Grammeme, ReferenceType } from '../../models/language';
|
|
||||||
import { getCompatibleGrams, parseEntityReference, parseGrammemes, parseSyntacticReference } from '../../models/languageAPI';
|
|
||||||
import { CstMatchMode } from '../../models/miscelanious';
|
|
||||||
import { IConstituenta } from '../../models/rsform';
|
|
||||||
import { matchConstituenta } from '../../models/rsformAPI';
|
|
||||||
import { prefixes } from '../../utils/constants';
|
|
||||||
import { compareGrammemeOptions, IGrammemeOption, PremadeWordForms, SelectorGrammems } from '../../utils/selectors';
|
|
||||||
import ReferenceTypeButton from './ReferenceTypeButton';
|
|
||||||
import WordformButton from './WordformButton';
|
|
||||||
|
|
||||||
export interface IReferenceInputState {
|
|
||||||
type: ReferenceType
|
|
||||||
refRaw?: string
|
|
||||||
text?: string
|
|
||||||
mainRefs: string[]
|
|
||||||
basePosition: number
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DlgEditReferenceProps {
|
|
||||||
hideWindow: () => void
|
|
||||||
items: IConstituenta[]
|
|
||||||
initial: IReferenceInputState
|
|
||||||
onSave: (newRef: string) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferenceProps) {
|
|
||||||
const [type, setType] = useState<ReferenceType>(ReferenceType.ENTITY);
|
|
||||||
|
|
||||||
const [nominal, setNominal] = useState('');
|
|
||||||
const [offset, setOffset] = useState(1);
|
|
||||||
|
|
||||||
const [selectedCst, setSelectedCst] = useState<IConstituenta | undefined>(undefined);
|
|
||||||
const [alias, setAlias] = useState('');
|
|
||||||
const [term, setTerm] = useState('');
|
|
||||||
|
|
||||||
const [selectedGrams, setSelectedGrams] = useState<IGrammemeOption[]>([]);
|
|
||||||
const [gramOptions, setGramOptions] = useState<IGrammemeOption[]>([]);
|
|
||||||
|
|
||||||
const mainLink = useMemo(
|
|
||||||
() => {
|
|
||||||
const position = offset > 0 ? initial.basePosition + (offset - 1) : initial.basePosition + offset;
|
|
||||||
if (offset === 0 || position < 0 || position >= initial.mainRefs.length) {
|
|
||||||
return 'Некорректное значение смещения';
|
|
||||||
} else {
|
|
||||||
return initial.mainRefs[position];
|
|
||||||
}
|
|
||||||
}, [initial, offset]);
|
|
||||||
|
|
||||||
const isValid = useMemo(
|
|
||||||
() => {
|
|
||||||
if (type === ReferenceType.ENTITY) {
|
|
||||||
return alias !== '' && selectedGrams.length > 0;
|
|
||||||
} else if (type === ReferenceType.SYNTACTIC) {
|
|
||||||
return nominal !== '' && offset !== 0;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, [type, alias, selectedGrams, nominal, offset]);
|
|
||||||
|
|
||||||
function produceReference(): string {
|
|
||||||
if (type === ReferenceType.ENTITY) {
|
|
||||||
return `@{${alias}|${selectedGrams.map(gram => gram.value).join(',')}}`;
|
|
||||||
} else if (type === ReferenceType.SYNTACTIC) {
|
|
||||||
return `@{${offset}|${nominal}}`;
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialization
|
|
||||||
useLayoutEffect(
|
|
||||||
() => {
|
|
||||||
setType(initial.type);
|
|
||||||
if (initial.refRaw) {
|
|
||||||
if (initial.type === ReferenceType.ENTITY) {
|
|
||||||
const ref = parseEntityReference(initial.refRaw);
|
|
||||||
setAlias(ref.entity);
|
|
||||||
const grams = parseGrammemes(ref.form);
|
|
||||||
setSelectedGrams(SelectorGrammems.filter(data => grams.includes(data.value)));
|
|
||||||
} else if (initial.type === ReferenceType.SYNTACTIC) {
|
|
||||||
const ref = parseSyntacticReference(initial.refRaw);
|
|
||||||
setOffset(ref.offset);
|
|
||||||
setNominal(ref.nominal);
|
|
||||||
}
|
|
||||||
} else if (initial.text) {
|
|
||||||
setNominal(initial.text ?? '');
|
|
||||||
}
|
|
||||||
}, [initial, items]);
|
|
||||||
|
|
||||||
// Filter grammemes when input changes
|
|
||||||
useEffect(
|
|
||||||
() => {
|
|
||||||
const compatible = getCompatibleGrams(
|
|
||||||
selectedGrams
|
|
||||||
.filter(data => Object.values(Grammeme).includes(data.value as Grammeme))
|
|
||||||
.map(data => data.value as Grammeme)
|
|
||||||
);
|
|
||||||
setGramOptions(SelectorGrammems.filter(({value}) => compatible.includes(value as Grammeme)));
|
|
||||||
}, [selectedGrams]);
|
|
||||||
|
|
||||||
// Update term when alias changes
|
|
||||||
useEffect(
|
|
||||||
() => {
|
|
||||||
const cst = items.find(item => item.alias === alias)
|
|
||||||
setTerm(cst?.term_resolved ?? '')
|
|
||||||
}, [alias, term, items]);
|
|
||||||
|
|
||||||
const handleSubmit = () => onSave(produceReference());
|
|
||||||
|
|
||||||
function handleSelectConstituenta(cst: IConstituenta) {
|
|
||||||
setAlias(cst.alias);
|
|
||||||
setSelectedCst(cst);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSelectGrams = useCallback(
|
|
||||||
(grams: Grammeme[]) => {
|
|
||||||
setSelectedGrams(SelectorGrammems.filter(({value}) => grams.includes(value as Grammeme)));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const FormButtons = useMemo(() => {
|
|
||||||
return (
|
|
||||||
<div className='flex flex-col items-center w-full text-sm'>
|
|
||||||
<div className='flex flex-start'>
|
|
||||||
{PremadeWordForms.slice(0, 6).map(
|
|
||||||
(data, index) =>
|
|
||||||
<WordformButton id={`${prefixes.wordform_list}${index}`}
|
|
||||||
text={data.text} example={data.example} grams={data.grams}
|
|
||||||
isSelected={data.grams.every(gram => selectedGrams.find(item => item.value as Grammeme === gram))}
|
|
||||||
onSelectGrams={handleSelectGrams}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='flex flex-start'>
|
|
||||||
{PremadeWordForms.slice(6, 12).map(
|
|
||||||
(data, index) =>
|
|
||||||
<WordformButton id={`${prefixes.wordform_list}${index}`}
|
|
||||||
text={data.text} example={data.example} grams={data.grams}
|
|
||||||
isSelected={data.grams.every(gram => selectedGrams.find(item => item.value as Grammeme === gram))}
|
|
||||||
onSelectGrams={handleSelectGrams}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>);
|
|
||||||
}, [handleSelectGrams, selectedGrams]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title='Редактирование ссылки'
|
|
||||||
hideWindow={hideWindow}
|
|
||||||
submitText='Сохранить ссылку'
|
|
||||||
canSubmit={isValid}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
>
|
|
||||||
<div className='min-w-[40rem] max-w-[40rem] flex flex-col gap-4 mb-4 min-h-[34rem]'>
|
|
||||||
<div className='flex items-center self-center flex-start'>
|
|
||||||
<ReferenceTypeButton
|
|
||||||
type={ReferenceType.ENTITY}
|
|
||||||
onSelect={setType}
|
|
||||||
isSelected={type === ReferenceType.ENTITY}
|
|
||||||
/>
|
|
||||||
<ReferenceTypeButton
|
|
||||||
type={ReferenceType.SYNTACTIC}
|
|
||||||
onSelect={setType}
|
|
||||||
isSelected={type === ReferenceType.SYNTACTIC}
|
|
||||||
/>
|
|
||||||
<div id='terminology-help' className='px-1 py-1'>
|
|
||||||
<HelpIcon color='text-primary' size={5} />
|
|
||||||
</div>
|
|
||||||
<ConceptTooltip
|
|
||||||
anchorSelect='#terminology-help'
|
|
||||||
className='max-w-[30rem] z-modal-tooltip'
|
|
||||||
offset={4}
|
|
||||||
>
|
|
||||||
<HelpTerminologyControl />
|
|
||||||
</ConceptTooltip>
|
|
||||||
</div>
|
|
||||||
{type === ReferenceType.SYNTACTIC &&
|
|
||||||
<div className='flex flex-col gap-2'>
|
|
||||||
<div className='flex flex-start'>
|
|
||||||
<TextInput id='offset' type='number'
|
|
||||||
label='Смещение'
|
|
||||||
dimensions='max-w-[10rem]'
|
|
||||||
dense
|
|
||||||
value={offset}
|
|
||||||
onChange={event => setOffset(event.target.valueAsNumber)}
|
|
||||||
/>
|
|
||||||
<div className='self-center ml-2 text-sm font-semibold whitespace-nowrap'>
|
|
||||||
Основная ссылка:
|
|
||||||
</div>
|
|
||||||
<TextInput
|
|
||||||
dense
|
|
||||||
disabled
|
|
||||||
noBorder
|
|
||||||
value={mainLink}
|
|
||||||
dimensions='w-full text-sm'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<TextInput id='nominal' type='text'
|
|
||||||
dimensions='w-full'
|
|
||||||
label='Начальная форма'
|
|
||||||
placeholder='зависимое слово в начальной форме'
|
|
||||||
spellCheck
|
|
||||||
value={nominal}
|
|
||||||
onChange={event => setNominal(event.target.value)}
|
|
||||||
/>
|
|
||||||
</div>}
|
|
||||||
{type === ReferenceType.ENTITY &&
|
|
||||||
<div className='flex flex-col gap-2'>
|
|
||||||
<ConstituentaPicker
|
|
||||||
value={selectedCst}
|
|
||||||
data={items}
|
|
||||||
onSelectValue={handleSelectConstituenta}
|
|
||||||
prefixID={prefixes.cst_modal_list}
|
|
||||||
describeFunc={cst => cst.term_resolved}
|
|
||||||
matchFunc={(cst, filter) => matchConstituenta(cst, filter, CstMatchMode.TERM)}
|
|
||||||
prefilterFunc={cst => cst.term_resolved !== ''}
|
|
||||||
rows={8}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className='flex gap-4 flex-start'>
|
|
||||||
<TextInput
|
|
||||||
label='Отсылаемая конституента'
|
|
||||||
dimensions='max-w-[16rem] min-w-[16rem] whitespace-nowrap'
|
|
||||||
placeholder='Имя'
|
|
||||||
dense
|
|
||||||
value={alias}
|
|
||||||
onChange={event => setAlias(event.target.value)}
|
|
||||||
/>
|
|
||||||
<div className='flex items-center w-full flex-start'>
|
|
||||||
<div className='self-center text-sm font-semibold'>
|
|
||||||
Термин:
|
|
||||||
</div>
|
|
||||||
<TextInput
|
|
||||||
dense
|
|
||||||
disabled
|
|
||||||
noBorder
|
|
||||||
value={term}
|
|
||||||
tooltip={term}
|
|
||||||
dimensions='w-full text-sm'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{FormButtons}
|
|
||||||
<div className='flex items-center gap-10 flex-start'>
|
|
||||||
<Label text='Отсылаемая словоформа'/>
|
|
||||||
<SelectMulti
|
|
||||||
className='flex-grow h-full z-modal-top'
|
|
||||||
options={gramOptions}
|
|
||||||
placeholder='Выберите граммемы'
|
|
||||||
|
|
||||||
value={selectedGrams}
|
|
||||||
onChange={newValue => setSelectedGrams([...newValue].sort(compareGrammemeOptions))}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>}
|
|
||||||
</div>
|
|
||||||
</Modal>);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DlgEditReference;
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
import { useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import ConceptTooltip from '../components/Common/ConceptTooltip';
|
import ConceptTooltip from '../components/common/ConceptTooltip';
|
||||||
import MiniButton from '../components/Common/MiniButton';
|
import MiniButton from '../components/common/MiniButton';
|
||||||
import Modal from '../components/Common/Modal';
|
import Modal from '../components/common/Modal';
|
||||||
import SelectMulti from '../components/Common/SelectMulti';
|
import SelectMulti from '../components/common/SelectMulti';
|
||||||
import TextArea from '../components/Common/TextArea';
|
import TextArea from '../components/common/TextArea';
|
||||||
import DataTable, { createColumnHelper } from '../components/DataTable';
|
import DataTable, { createColumnHelper } from '../components/DataTable';
|
||||||
import HelpTerminologyControl from '../components/Help/HelpTerminologyControl';
|
import HelpTerminologyControl from '../components/Help/HelpTerminologyControl';
|
||||||
import { ArrowLeftIcon, ArrowRightIcon, CheckIcon, ChevronDoubleDownIcon, CrossIcon, HelpIcon } from '../components/Icons';
|
import { ArrowLeftIcon, ArrowRightIcon, CheckIcon, ChevronDoubleDownIcon, CrossIcon, HelpIcon } from '../components/Icons';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Checkbox from '../components/Common/Checkbox';
|
import Checkbox from '../components/common/Checkbox';
|
||||||
import Modal, { ModalProps } from '../components/Common/Modal';
|
import Modal, { ModalProps } from '../components/common/Modal';
|
||||||
import usePartialUpdate from '../hooks/usePartialUpdate';
|
import usePartialUpdate from '../hooks/usePartialUpdate';
|
||||||
import { GraphEditorParams } from '../models/miscelanious';
|
import { GraphEditorParams } from '../models/miscelanious';
|
||||||
import { CstType } from '../models/rsform';
|
import { CstType } from '../models/rsform';
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { useLayoutEffect, useState } from 'react';
|
import { useLayoutEffect, useState } from 'react';
|
||||||
|
|
||||||
import Modal, { ModalProps } from '../components/Common/Modal';
|
import Modal, { ModalProps } from '../components/common/Modal';
|
||||||
import SelectSingle from '../components/Common/SelectSingle';
|
import SelectSingle from '../components/common/SelectSingle';
|
||||||
import TextInput from '../components/Common/TextInput';
|
import TextInput from '../components/common/TextInput';
|
||||||
import { useRSForm } from '../context/RSFormContext';
|
import { useRSForm } from '../context/RSFormContext';
|
||||||
import usePartialUpdate from '../hooks/usePartialUpdate';
|
import usePartialUpdate from '../hooks/usePartialUpdate';
|
||||||
import { CstType, ICstRenameData } from '../models/rsform';
|
import { CstType, ICstRenameData } from '../models/rsform';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { GraphCanvas,GraphEdge, GraphNode } from 'reagraph';
|
import { GraphCanvas,GraphEdge, GraphNode } from 'reagraph';
|
||||||
|
|
||||||
import Modal, { ModalProps } from '../components/Common/Modal';
|
import Modal, { ModalProps } from '../components/common/Modal';
|
||||||
import { useConceptTheme } from '../context/ThemeContext';
|
import { useConceptTheme } from '../context/ThemeContext';
|
||||||
import { SyntaxTree } from '../models/rslang';
|
import { SyntaxTree } from '../models/rslang';
|
||||||
import { graphDarkT, graphLightT } from '../utils/color';
|
import { graphDarkT, graphLightT } from '../utils/color';
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import Checkbox from '../components/Common/Checkbox';
|
import Checkbox from '../components/common/Checkbox';
|
||||||
import FileInput from '../components/Common/FileInput';
|
import FileInput from '../components/common/FileInput';
|
||||||
import Modal from '../components/Common/Modal';
|
import Modal from '../components/common/Modal';
|
||||||
import { useRSForm } from '../context/RSFormContext';
|
import { useRSForm } from '../context/RSFormContext';
|
||||||
import { IRSFormUploadData } from '../models/rsform';
|
import { IRSFormUploadData } from '../models/rsform';
|
||||||
import { EXTEOR_TRS_FILE } from '../utils/constants';
|
import { EXTEOR_TRS_FILE } from '../utils/constants';
|
||||||
|
|
|
@ -3,14 +3,14 @@ import { useLocation } from 'react-router-dom';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import BackendError from '../components/BackendError';
|
import BackendError from '../components/BackendError';
|
||||||
import Button from '../components/Common/Button';
|
import Button from '../components/common/Button';
|
||||||
import Checkbox from '../components/Common/Checkbox';
|
import Checkbox from '../components/common/Checkbox';
|
||||||
import Form from '../components/Common/Form';
|
import Form from '../components/common/Form';
|
||||||
import Label from '../components/Common/Label';
|
import Label from '../components/common/Label';
|
||||||
import MiniButton from '../components/Common/MiniButton';
|
import MiniButton from '../components/common/MiniButton';
|
||||||
import SubmitButton from '../components/Common/SubmitButton';
|
import SubmitButton from '../components/common/SubmitButton';
|
||||||
import TextArea from '../components/Common/TextArea';
|
import TextArea from '../components/common/TextArea';
|
||||||
import TextInput from '../components/Common/TextInput';
|
import TextInput from '../components/common/TextInput';
|
||||||
import { DownloadIcon } from '../components/Icons';
|
import { DownloadIcon } from '../components/Icons';
|
||||||
import RequireAuth from '../components/RequireAuth';
|
import RequireAuth from '../components/RequireAuth';
|
||||||
import { useLibrary } from '../context/LibraryContext';
|
import { useLibrary } from '../context/LibraryContext';
|
||||||
|
|
65
rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx
Normal file
65
rsconcept/frontend/src/pages/LibraryPage/LibraryPage.tsx
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import { useCallback, useLayoutEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import BackendError from '../../components/BackendError'
|
||||||
|
import { ConceptLoader } from '../../components/common/ConceptLoader'
|
||||||
|
import { useLibrary } from '../../context/LibraryContext';
|
||||||
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
|
import useLocalStorage from '../../hooks/useLocalStorage';
|
||||||
|
import { ILibraryItem } from '../../models/library';
|
||||||
|
import { ILibraryFilter, LibraryFilterStrategy } from '../../models/miscelanious';
|
||||||
|
import SearchPanel from './SearchPanel';
|
||||||
|
import ViewLibrary from './ViewLibrary';
|
||||||
|
|
||||||
|
function LibraryPage() {
|
||||||
|
const library = useLibrary();
|
||||||
|
const { setShowScroll } = useConceptTheme();
|
||||||
|
|
||||||
|
const [ filter, setFilter ] = useState<ILibraryFilter>({});
|
||||||
|
const [ items, setItems ] = useState<ILibraryItem[]>([]);
|
||||||
|
|
||||||
|
const [query, setQuery] = useState('');
|
||||||
|
const [strategy, setStrategy] = useLocalStorage<LibraryFilterStrategy>('search_strategy', LibraryFilterStrategy.MANUAL);
|
||||||
|
|
||||||
|
useLayoutEffect(
|
||||||
|
() => {
|
||||||
|
setShowScroll(true);
|
||||||
|
return () => setShowScroll(false);
|
||||||
|
}, [setShowScroll]);
|
||||||
|
|
||||||
|
useLayoutEffect(
|
||||||
|
() => {
|
||||||
|
setItems(library.applyFilter(filter));
|
||||||
|
}, [library, filter, filter.query]);
|
||||||
|
|
||||||
|
const resetQuery = useCallback(
|
||||||
|
() => {
|
||||||
|
setQuery('');
|
||||||
|
setFilter({});
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='w-full'>
|
||||||
|
{ library.loading && <ConceptLoader /> }
|
||||||
|
{ library.error && <BackendError error={library.error} />}
|
||||||
|
{ !library.loading && library.items &&
|
||||||
|
<div className='flex flex-col w-full'>
|
||||||
|
<SearchPanel
|
||||||
|
query={query}
|
||||||
|
setQuery={setQuery}
|
||||||
|
strategy={strategy}
|
||||||
|
setStrategy={setStrategy}
|
||||||
|
total={library.items.length ?? 0}
|
||||||
|
filtered={items.length}
|
||||||
|
setFilter={setFilter}
|
||||||
|
/>
|
||||||
|
<ViewLibrary
|
||||||
|
resetQuery={resetQuery}
|
||||||
|
items={items}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LibraryPage;
|
|
@ -1,8 +1,8 @@
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import Dropdown from '../../components/Common/Dropdown';
|
import Dropdown from '../../components/common/Dropdown';
|
||||||
import DropdownCheckbox from '../../components/Common/DropdownCheckbox';
|
import DropdownCheckbox from '../../components/common/DropdownCheckbox';
|
||||||
import SelectorButton from '../../components/Common/SelectorButton';
|
import SelectorButton from '../../components/common/SelectorButton';
|
||||||
import { FilterIcon } from '../../components/Icons';
|
import { FilterIcon } from '../../components/Icons';
|
||||||
import { useAuth } from '../../context/AuthContext';
|
import { useAuth } from '../../context/AuthContext';
|
||||||
import useDropdown from '../../hooks/useDropdown';
|
import useDropdown from '../../hooks/useDropdown';
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
import ConceptTooltip from '../../components/common/ConceptTooltip';
|
||||||
import TextURL from '../../components/Common/TextURL';
|
import TextURL from '../../components/common/TextURL';
|
||||||
import DataTable, { createColumnHelper } from '../../components/DataTable';
|
import DataTable, { createColumnHelper } from '../../components/DataTable';
|
||||||
import HelpLibrary from '../../components/Help/HelpLibrary';
|
import HelpLibrary from '../../components/Help/HelpLibrary';
|
||||||
import { EducationIcon, GroupIcon, HelpIcon,SubscribedIcon } from '../../components/Icons';
|
import { EducationIcon, GroupIcon, HelpIcon,SubscribedIcon } from '../../components/Icons';
|
||||||
|
|
|
@ -1,65 +1 @@
|
||||||
import { useCallback, useLayoutEffect, useState } from 'react';
|
export { default } from './LibraryPage';
|
||||||
|
|
||||||
import BackendError from '../../components/BackendError'
|
|
||||||
import { ConceptLoader } from '../../components/Common/ConceptLoader'
|
|
||||||
import { useLibrary } from '../../context/LibraryContext';
|
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
|
||||||
import useLocalStorage from '../../hooks/useLocalStorage';
|
|
||||||
import { ILibraryItem } from '../../models/library';
|
|
||||||
import { ILibraryFilter, LibraryFilterStrategy } from '../../models/miscelanious';
|
|
||||||
import SearchPanel from './SearchPanel';
|
|
||||||
import ViewLibrary from './ViewLibrary';
|
|
||||||
|
|
||||||
function LibraryPage() {
|
|
||||||
const library = useLibrary();
|
|
||||||
const { setShowScroll } = useConceptTheme();
|
|
||||||
|
|
||||||
const [ filter, setFilter ] = useState<ILibraryFilter>({});
|
|
||||||
const [ items, setItems ] = useState<ILibraryItem[]>([]);
|
|
||||||
|
|
||||||
const [query, setQuery] = useState('');
|
|
||||||
const [strategy, setStrategy] = useLocalStorage<LibraryFilterStrategy>('search_strategy', LibraryFilterStrategy.MANUAL);
|
|
||||||
|
|
||||||
useLayoutEffect(
|
|
||||||
() => {
|
|
||||||
setShowScroll(true);
|
|
||||||
return () => setShowScroll(false);
|
|
||||||
}, [setShowScroll]);
|
|
||||||
|
|
||||||
useLayoutEffect(
|
|
||||||
() => {
|
|
||||||
setItems(library.applyFilter(filter));
|
|
||||||
}, [library, filter, filter.query]);
|
|
||||||
|
|
||||||
const resetQuery = useCallback(
|
|
||||||
() => {
|
|
||||||
setQuery('');
|
|
||||||
setFilter({});
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='w-full'>
|
|
||||||
{ library.loading && <ConceptLoader /> }
|
|
||||||
{ library.error && <BackendError error={library.error} />}
|
|
||||||
{ !library.loading && library.items &&
|
|
||||||
<div className='flex flex-col w-full'>
|
|
||||||
<SearchPanel
|
|
||||||
query={query}
|
|
||||||
setQuery={setQuery}
|
|
||||||
strategy={strategy}
|
|
||||||
setStrategy={setStrategy}
|
|
||||||
total={library.items.length ?? 0}
|
|
||||||
filtered={items.length}
|
|
||||||
setFilter={setFilter}
|
|
||||||
/>
|
|
||||||
<ViewLibrary
|
|
||||||
resetQuery={resetQuery}
|
|
||||||
items={items}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LibraryPage;
|
|
|
@ -3,10 +3,10 @@ import { useEffect, useState } from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
import BackendError, { ErrorInfo } from '../components/BackendError';
|
import BackendError, { ErrorInfo } from '../components/BackendError';
|
||||||
import Form from '../components/Common/Form';
|
import Form from '../components/common/Form';
|
||||||
import SubmitButton from '../components/Common/SubmitButton';
|
import SubmitButton from '../components/common/SubmitButton';
|
||||||
import TextInput from '../components/Common/TextInput';
|
import TextInput from '../components/common/TextInput';
|
||||||
import TextURL from '../components/Common/TextURL';
|
import TextURL from '../components/common/TextURL';
|
||||||
import { useAuth } from '../context/AuthContext';
|
import { useAuth } from '../context/AuthContext';
|
||||||
import { useConceptNavigation } from '../context/NagivationContext';
|
import { useConceptNavigation } from '../context/NagivationContext';
|
||||||
import { useConceptTheme } from '../context/ThemeContext';
|
import { useConceptTheme } from '../context/ThemeContext';
|
||||||
|
|
49
rsconcept/frontend/src/pages/ManualsPage/ManualsPage.tsx
Normal file
49
rsconcept/frontend/src/pages/ManualsPage/ManualsPage.tsx
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { useCallback, useLayoutEffect, useState } from 'react';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { useConceptNavigation } from '../../context/NagivationContext';
|
||||||
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
|
import { HelpTopic } from '../../models/miscelanious';
|
||||||
|
import TopicsList from './TopicsList';
|
||||||
|
import ViewTopic from './ViewTopic';
|
||||||
|
|
||||||
|
function ManualsPage() {
|
||||||
|
const { navigateTo } = useConceptNavigation();
|
||||||
|
const search = useLocation().search;
|
||||||
|
const { mainHeight } = useConceptTheme();
|
||||||
|
const [activeTopic, setActiveTopic] = useState<HelpTopic>(HelpTopic.MAIN);
|
||||||
|
|
||||||
|
const navigateTopic = useCallback(
|
||||||
|
(newTopic: HelpTopic) => {
|
||||||
|
navigateTo(`/manuals?topic=${newTopic}`);
|
||||||
|
}, [navigateTo]);
|
||||||
|
|
||||||
|
|
||||||
|
function onSelectTopic(newTopic: HelpTopic) {
|
||||||
|
navigateTopic(newTopic);
|
||||||
|
}
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const topic = new URLSearchParams(search).get('topic') as HelpTopic;
|
||||||
|
if (!Object.values(HelpTopic).includes(topic)) {
|
||||||
|
navigateTopic(HelpTopic.MAIN);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
setActiveTopic(topic);
|
||||||
|
}
|
||||||
|
}, [search, setActiveTopic, navigateTopic]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex w-full gap-2 justify-start items-start' style={{minHeight: mainHeight}}>
|
||||||
|
<TopicsList
|
||||||
|
activeTopic={activeTopic}
|
||||||
|
onChangeTopic={topic => onSelectTopic(topic)}
|
||||||
|
/>
|
||||||
|
<ViewTopic
|
||||||
|
topic={activeTopic}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ManualsPage;
|
|
@ -1,49 +1 @@
|
||||||
import { useCallback, useLayoutEffect, useState } from 'react';
|
export { default } from './ManualsPage';
|
||||||
import { useLocation } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { useConceptNavigation } from '../../context/NagivationContext';
|
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
|
||||||
import { HelpTopic } from '../../models/miscelanious';
|
|
||||||
import TopicsList from './TopicsList';
|
|
||||||
import ViewTopic from './ViewTopic';
|
|
||||||
|
|
||||||
function ManualsPage() {
|
|
||||||
const { navigateTo } = useConceptNavigation();
|
|
||||||
const search = useLocation().search;
|
|
||||||
const { mainHeight } = useConceptTheme();
|
|
||||||
const [activeTopic, setActiveTopic] = useState<HelpTopic>(HelpTopic.MAIN);
|
|
||||||
|
|
||||||
const navigateTopic = useCallback(
|
|
||||||
(newTopic: HelpTopic) => {
|
|
||||||
navigateTo(`/manuals?topic=${newTopic}`);
|
|
||||||
}, [navigateTo]);
|
|
||||||
|
|
||||||
|
|
||||||
function onSelectTopic(newTopic: HelpTopic) {
|
|
||||||
navigateTopic(newTopic);
|
|
||||||
}
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
const topic = new URLSearchParams(search).get('topic') as HelpTopic;
|
|
||||||
if (!Object.values(HelpTopic).includes(topic)) {
|
|
||||||
navigateTopic(HelpTopic.MAIN);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
setActiveTopic(topic);
|
|
||||||
}
|
|
||||||
}, [search, setActiveTopic, navigateTopic]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='flex w-full gap-2 justify-start items-start' style={{minHeight: mainHeight}}>
|
|
||||||
<TopicsList
|
|
||||||
activeTopic={activeTopic}
|
|
||||||
onChangeTopic={topic => onSelectTopic(topic)}
|
|
||||||
/>
|
|
||||||
<ViewTopic
|
|
||||||
topic={activeTopic}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ManualsPage;
|
|
|
@ -1,4 +1,4 @@
|
||||||
import TextURL from '../components/Common/TextURL';
|
import TextURL from '../components/common/TextURL';
|
||||||
|
|
||||||
export function NotFoundPage() {
|
export function NotFoundPage() {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Dispatch, SetStateAction, useLayoutEffect, useMemo, useState } from 'react';
|
import { Dispatch, SetStateAction, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
import ConceptTooltip from '../../components/common/ConceptTooltip';
|
||||||
import MiniButton from '../../components/Common/MiniButton';
|
import MiniButton from '../../components/common/MiniButton';
|
||||||
import SubmitButton from '../../components/Common/SubmitButton';
|
import SubmitButton from '../../components/common/SubmitButton';
|
||||||
import TextArea from '../../components/Common/TextArea';
|
import TextArea from '../../components/common/TextArea';
|
||||||
import HelpConstituenta from '../../components/Help/HelpConstituenta';
|
import HelpConstituenta from '../../components/Help/HelpConstituenta';
|
||||||
import { ArrowsRotateIcon, CloneIcon, DumpBinIcon, EditIcon, HelpIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons';
|
import { ArrowsRotateIcon, CloneIcon, DumpBinIcon, EditIcon, HelpIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons';
|
||||||
import RefsInput from '../../components/RefsInput';
|
import RefsInput from '../../components/RefsInput';
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import DataTable, { createColumnHelper, type RowSelectionState,VisibilityState } from '../../components/DataTable';
|
import DataTable, { createColumnHelper, type RowSelectionState,VisibilityState } from '../../components/DataTable';
|
||||||
import ConstituentaBadge from '../../components/Shared/ConstituentaBadge';
|
import ConstituentaBadge from '../../components/shared/ConstituentaBadge';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
import useWindowSize from '../../hooks/useWindowSize';
|
import useWindowSize from '../../hooks/useWindowSize';
|
||||||
|
|
|
@ -2,9 +2,9 @@ import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||||
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
import { useCallback, useLayoutEffect, useRef, 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 { ConceptLoader } from '../../components/Common/ConceptLoader';
|
import { ConceptLoader } from '../../components/common/ConceptLoader';
|
||||||
import MiniButton from '../../components/Common/MiniButton';
|
import MiniButton from '../../components/common/MiniButton';
|
||||||
import { ASTNetworkIcon } from '../../components/Icons';
|
import { ASTNetworkIcon } from '../../components/Icons';
|
||||||
import RSInput from '../../components/RSInput';
|
import RSInput from '../../components/RSInput';
|
||||||
import { RSTextWrapper } from '../../components/RSInput/textEditing';
|
import { RSTextWrapper } from '../../components/RSInput/textEditing';
|
||||||
|
|
|
@ -2,13 +2,13 @@ import { Dispatch, SetStateAction, useLayoutEffect, useState } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import Checkbox from '../../components/Common/Checkbox';
|
import Checkbox from '../../components/common/Checkbox';
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
import ConceptTooltip from '../../components/common/ConceptTooltip';
|
||||||
import Divider from '../../components/Common/Divider';
|
import Divider from '../../components/common/Divider';
|
||||||
import MiniButton from '../../components/Common/MiniButton';
|
import MiniButton from '../../components/common/MiniButton';
|
||||||
import SubmitButton from '../../components/Common/SubmitButton';
|
import SubmitButton from '../../components/common/SubmitButton';
|
||||||
import TextArea from '../../components/Common/TextArea';
|
import TextArea from '../../components/common/TextArea';
|
||||||
import TextInput from '../../components/Common/TextInput';
|
import TextInput from '../../components/common/TextInput';
|
||||||
import HelpRSFormMeta from '../../components/Help/HelpRSFormMeta';
|
import HelpRSFormMeta from '../../components/Help/HelpRSFormMeta';
|
||||||
import { DownloadIcon, DumpBinIcon, HelpIcon, OwnerIcon, SaveIcon, ShareIcon } from '../../components/Icons';
|
import { DownloadIcon, DumpBinIcon, HelpIcon, OwnerIcon, SaveIcon, ShareIcon } from '../../components/Icons';
|
||||||
import { useAuth } from '../../context/AuthContext';
|
import { useAuth } from '../../context/AuthContext';
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { GraphCanvas, GraphCanvasRef, GraphEdge,
|
||||||
GraphNode, LayoutTypes, Sphere, useSelection
|
GraphNode, LayoutTypes, Sphere, useSelection
|
||||||
} from 'reagraph';
|
} from 'reagraph';
|
||||||
|
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
import ConceptTooltip from '../../components/common/ConceptTooltip';
|
||||||
import MiniButton from '../../components/Common/MiniButton';
|
import MiniButton from '../../components/common/MiniButton';
|
||||||
import SelectSingle from '../../components/Common/SelectSingle';
|
import SelectSingle from '../../components/common/SelectSingle';
|
||||||
import ConstituentaTooltip from '../../components/Help/ConstituentaTooltip';
|
import ConstituentaTooltip from '../../components/Help/ConstituentaTooltip';
|
||||||
import HelpTermGraph from '../../components/Help/HelpTermGraph';
|
import HelpTermGraph from '../../components/Help/HelpTermGraph';
|
||||||
import InfoConstituenta from '../../components/Help/InfoConstituenta';
|
import InfoConstituenta from '../../components/Help/InfoConstituenta';
|
||||||
|
|
15
rsconcept/frontend/src/pages/RSFormPage/RSFormPage.tsx
Normal file
15
rsconcept/frontend/src/pages/RSFormPage/RSFormPage.tsx
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { RSFormState } from '../../context/RSFormContext';
|
||||||
|
import RSTabs from './RSTabs';
|
||||||
|
|
||||||
|
function RSFormPage() {
|
||||||
|
const { id } = useParams();
|
||||||
|
return (
|
||||||
|
<RSFormState schemaID={id ?? ''}>
|
||||||
|
<RSTabs />
|
||||||
|
</RSFormState>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RSFormPage;
|
|
@ -6,9 +6,9 @@ import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import BackendError, { ErrorInfo } from '../../components/BackendError';
|
import BackendError, { ErrorInfo } from '../../components/BackendError';
|
||||||
import { ConceptLoader } from '../../components/Common/ConceptLoader';
|
import { ConceptLoader } from '../../components/common/ConceptLoader';
|
||||||
import ConceptTab from '../../components/Common/ConceptTab';
|
import ConceptTab from '../../components/common/ConceptTab';
|
||||||
import TextURL from '../../components/Common/TextURL';
|
import TextURL from '../../components/common/TextURL';
|
||||||
import { useLibrary } from '../../context/LibraryContext';
|
import { useLibrary } from '../../context/LibraryContext';
|
||||||
import { useConceptNavigation } from '../../context/NagivationContext';
|
import { useConceptNavigation } from '../../context/NagivationContext';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Divider from '../../../components/Common/Divider';
|
import Divider from '../../../components/common/Divider';
|
||||||
import LabeledText from '../../../components/Common/LabeledText';
|
import LabeledText from '../../../components/common/LabeledText';
|
||||||
import { type IRSFormStats } from '../../../models/rsform';
|
import { type IRSFormStats } from '../../../models/rsform';
|
||||||
|
|
||||||
interface RSFormStatsProps {
|
interface RSFormStatsProps {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import ConceptTooltip from '../../../components/Common/ConceptTooltip';
|
import ConceptTooltip from '../../../components/common/ConceptTooltip';
|
||||||
import Dropdown from '../../../components/Common/Dropdown';
|
import Dropdown from '../../../components/common/Dropdown';
|
||||||
import DropdownButton from '../../../components/Common/DropdownButton';
|
import DropdownButton from '../../../components/common/DropdownButton';
|
||||||
import MiniButton from '../../../components/Common/MiniButton';
|
import MiniButton from '../../../components/common/MiniButton';
|
||||||
import HelpRSFormItems from '../../../components/Help/HelpRSFormItems';
|
import HelpRSFormItems from '../../../components/Help/HelpRSFormItems';
|
||||||
import { ArrowDownIcon, ArrowDropdownIcon, ArrowUpIcon, CloneIcon, DiamondIcon, DumpBinIcon, HelpIcon, SmallPlusIcon,UpdateIcon } from '../../../components/Icons';
|
import { ArrowDownIcon, ArrowDropdownIcon, ArrowUpIcon, CloneIcon, DiamondIcon, DumpBinIcon, HelpIcon, SmallPlusIcon,UpdateIcon } from '../../../components/Icons';
|
||||||
import { useRSForm } from '../../../context/RSFormContext';
|
import { useRSForm } from '../../../context/RSFormContext';
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import Button from '../../../components/Common/Button';
|
import Button from '../../../components/common/Button';
|
||||||
import Dropdown from '../../../components/Common/Dropdown';
|
import Dropdown from '../../../components/common/Dropdown';
|
||||||
import DropdownButton from '../../../components/Common/DropdownButton';
|
import DropdownButton from '../../../components/common/DropdownButton';
|
||||||
import DropdownCheckbox from '../../../components/Common/DropdownCheckbox';
|
import DropdownCheckbox from '../../../components/common/DropdownCheckbox';
|
||||||
import {
|
import {
|
||||||
CloneIcon, DownloadIcon, DumpBinIcon, EditIcon, MenuIcon, NotSubscribedIcon,
|
CloneIcon, DownloadIcon, DumpBinIcon, EditIcon, MenuIcon, NotSubscribedIcon,
|
||||||
OwnerIcon, ShareIcon, SmallPlusIcon, SubscribedIcon, UploadIcon
|
OwnerIcon, ShareIcon, SmallPlusIcon, SubscribedIcon, UploadIcon
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import Dropdown from '../../../components/Common/Dropdown';
|
import Dropdown from '../../../components/common/Dropdown';
|
||||||
import DropdownButton from '../../../components/Common/DropdownButton';
|
import DropdownButton from '../../../components/common/DropdownButton';
|
||||||
import SelectorButton from '../../../components/Common/SelectorButton';
|
import SelectorButton from '../../../components/common/SelectorButton';
|
||||||
import DataTable, { createColumnHelper, IConditionalStyle, VisibilityState } from '../../../components/DataTable';
|
import DataTable, { createColumnHelper, IConditionalStyle, VisibilityState } from '../../../components/DataTable';
|
||||||
import { CogIcon, FilterIcon, MagnifyingGlassIcon } from '../../../components/Icons';
|
import { CogIcon, FilterIcon, MagnifyingGlassIcon } from '../../../components/Icons';
|
||||||
import ConstituentaBadge from '../../../components/Shared/ConstituentaBadge';
|
import ConstituentaBadge from '../../../components/shared/ConstituentaBadge';
|
||||||
import { useRSForm } from '../../../context/RSFormContext';
|
import { useRSForm } from '../../../context/RSFormContext';
|
||||||
import { useConceptTheme } from '../../../context/ThemeContext';
|
import { useConceptTheme } from '../../../context/ThemeContext';
|
||||||
import useDropdown from '../../../hooks/useDropdown';
|
import useDropdown from '../../../hooks/useDropdown';
|
||||||
|
|
|
@ -1,15 +1 @@
|
||||||
import { useParams } from 'react-router-dom';
|
export { default } from './RSFormPage';
|
||||||
|
|
||||||
import { RSFormState } from '../../context/RSFormContext';
|
|
||||||
import RSTabs from './RSTabs';
|
|
||||||
|
|
||||||
function RSFormPage() {
|
|
||||||
const { id } = useParams();
|
|
||||||
return (
|
|
||||||
<RSFormState schemaID={id ?? ''}>
|
|
||||||
<RSTabs />
|
|
||||||
</RSFormState>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default RSFormPage;
|
|
|
@ -3,10 +3,10 @@ import { useLocation } from 'react-router-dom';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import BackendError from '../components/BackendError';
|
import BackendError from '../components/BackendError';
|
||||||
import Button from '../components/Common/Button';
|
import Button from '../components/common/Button';
|
||||||
import Form from '../components/Common/Form';
|
import Form from '../components/common/Form';
|
||||||
import SubmitButton from '../components/Common/SubmitButton';
|
import SubmitButton from '../components/common/SubmitButton';
|
||||||
import TextInput from '../components/Common/TextInput';
|
import TextInput from '../components/common/TextInput';
|
||||||
import { useAuth } from '../context/AuthContext';
|
import { useAuth } from '../context/AuthContext';
|
||||||
import { useConceptNavigation } from '../context/NagivationContext';
|
import { useConceptNavigation } from '../context/NagivationContext';
|
||||||
import { type IUserSignupData } from '../models/library';
|
import { type IUserSignupData } from '../models/library';
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { useEffect, useMemo, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import BackendError, { ErrorInfo } from '../../components/BackendError';
|
import BackendError, { ErrorInfo } from '../../components/BackendError';
|
||||||
import SubmitButton from '../../components/Common/SubmitButton';
|
import SubmitButton from '../../components/common/SubmitButton';
|
||||||
import TextInput from '../../components/Common/TextInput';
|
import TextInput from '../../components/common/TextInput';
|
||||||
import { useAuth } from '../../context/AuthContext';
|
import { useAuth } from '../../context/AuthContext';
|
||||||
import { useConceptNavigation } from '../../context/NagivationContext';
|
import { useConceptNavigation } from '../../context/NagivationContext';
|
||||||
import { IUserUpdatePassword } from '../../models/library';
|
import { IUserUpdatePassword } from '../../models/library';
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { useLayoutEffect, useState } from 'react';
|
import { useLayoutEffect, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import SubmitButton from '../../components/Common/SubmitButton';
|
import SubmitButton from '../../components/common/SubmitButton';
|
||||||
import TextInput from '../../components/Common/TextInput';
|
import TextInput from '../../components/common/TextInput';
|
||||||
import { useUserProfile } from '../../context/UserProfileContext';
|
import { useUserProfile } from '../../context/UserProfileContext';
|
||||||
import useModificationPrompt from '../../hooks/useModificationPrompt';
|
import useModificationPrompt from '../../hooks/useModificationPrompt';
|
||||||
import { IUserUpdateData } from '../../models/library';
|
import { IUserUpdateData } from '../../models/library';
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import RequireAuth from '../../components/RequireAuth';
|
||||||
|
import { UserProfileState } from '../../context/UserProfileContext';
|
||||||
|
import UserTabs from './UserTabs';
|
||||||
|
|
||||||
|
function UserProfilePage() {
|
||||||
|
return (
|
||||||
|
<RequireAuth>
|
||||||
|
<UserProfileState>
|
||||||
|
<UserTabs />
|
||||||
|
</UserProfileState>
|
||||||
|
</RequireAuth>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserProfilePage;
|
|
@ -1,8 +1,8 @@
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
|
||||||
import BackendError from '../../components/BackendError';
|
import BackendError from '../../components/BackendError';
|
||||||
import { ConceptLoader } from '../../components/Common/ConceptLoader';
|
import { ConceptLoader } from '../../components/common/ConceptLoader';
|
||||||
import MiniButton from '../../components/Common/MiniButton';
|
import MiniButton from '../../components/common/MiniButton';
|
||||||
import { NotSubscribedIcon,SubscribedIcon } from '../../components/Icons';
|
import { NotSubscribedIcon,SubscribedIcon } from '../../components/Icons';
|
||||||
import { useAuth } from '../../context/AuthContext';
|
import { useAuth } from '../../context/AuthContext';
|
||||||
import { useLibrary } from '../../context/LibraryContext';
|
import { useLibrary } from '../../context/LibraryContext';
|
||||||
|
|
|
@ -1,15 +1 @@
|
||||||
import RequireAuth from '../../components/RequireAuth';
|
export { default } from './UserProfilePage';
|
||||||
import { UserProfileState } from '../../context/UserProfileContext';
|
|
||||||
import UserTabs from './UserTabs';
|
|
||||||
|
|
||||||
function UserProfilePage() {
|
|
||||||
return (
|
|
||||||
<RequireAuth>
|
|
||||||
<UserProfileState>
|
|
||||||
<UserTabs />
|
|
||||||
</UserProfileState>
|
|
||||||
</RequireAuth>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default UserProfilePage;
|
|
Loading…
Reference in New Issue
Block a user