Compare commits
18 Commits
4113370c62
...
6e1d99122e
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6e1d99122e | ||
![]() |
ad5a97b844 | ||
![]() |
2569cc521c | ||
![]() |
012ee27db6 | ||
![]() |
25f2768445 | ||
![]() |
ea0c4565bf | ||
![]() |
39006a70bc | ||
![]() |
0694c45c08 | ||
![]() |
b6a888140a | ||
![]() |
6e5e932af6 | ||
![]() |
d1e4edc326 | ||
![]() |
44ea7f7944 | ||
![]() |
32b34ff0ed | ||
![]() |
12ddc007ac | ||
![]() |
45dbe16444 | ||
![]() |
4b19fc57a2 | ||
![]() |
3e4d67c9a0 | ||
![]() |
13dd7638e4 |
|
@ -40,6 +40,7 @@ This readme file is used mostly to document project dependencies and conventions
|
|||
- react-tooltip
|
||||
- react-zoom-pan-pinch
|
||||
- react-hook-form
|
||||
- react-scan
|
||||
- reactflow
|
||||
- js-file-download
|
||||
- use-debounce
|
||||
|
@ -61,8 +62,6 @@ This readme file is used mostly to document project dependencies and conventions
|
|||
<summary>npm install -D</summary>
|
||||
<pre>
|
||||
- tailwindcss
|
||||
- postcss
|
||||
- autoprefixer
|
||||
- eslint-plugin-import
|
||||
- eslint-plugin-react-compiler
|
||||
- eslint-plugin-simple-import-sort
|
||||
|
@ -72,6 +71,7 @@ This readme file is used mostly to document project dependencies and conventions
|
|||
- vite
|
||||
- jest
|
||||
- ts-jest
|
||||
- @vitejs/plugin-react
|
||||
- @types/jest
|
||||
- @lezer/generator
|
||||
- @playwright/test
|
||||
|
|
20
TODO.txt
20
TODO.txt
|
@ -2,16 +2,16 @@
|
|||
For more specific TODOs see comments in code
|
||||
|
||||
[Bugs - PENDING]
|
||||
- Tab index still selecting background elements when modal is active
|
||||
-
|
||||
|
||||
[Functionality - PENDING]
|
||||
- Landing page
|
||||
- Design first user experience
|
||||
- Demo sandbox for anonymous users
|
||||
|
||||
- User profile: Settings + settings server persistency
|
||||
User profile:
|
||||
- Settings + settings server persistency
|
||||
- Profile pictures
|
||||
- Integrate socials and feedback
|
||||
- Custom LibraryItem lists
|
||||
- Custom user filters and sharing filters
|
||||
|
||||
|
@ -31,6 +31,7 @@ For more specific TODOs see comments in code
|
|||
|
||||
[Functionality - CANCELED]
|
||||
- User notifications on edit - consider spam prevention and change aggregation
|
||||
- Integrate socials and feedback
|
||||
- Content based search in Library
|
||||
- Private projects. Consider cooperative editing
|
||||
- OSS: synthesis table: auto substitution for diamond synthesis
|
||||
|
@ -38,10 +39,7 @@ For more specific TODOs see comments in code
|
|||
|
||||
[Tech]
|
||||
- duplicate syntax parsing and type info calculations to client. Consider moving backend to Nodejs or embedding c++ lib
|
||||
- add debounce to some search fields. Consider pagination and dynamic loading
|
||||
- move autopep8 and isort settings from vscode settings to pyproject.toml
|
||||
|
||||
- Testing: frontend react components, testplane / playwright?
|
||||
- Testing E2E playwright
|
||||
|
||||
|
||||
[Deployment]
|
||||
|
@ -70,11 +68,3 @@ https://drf-standardized-errors.readthedocs.io/en/latest/error_response.html
|
|||
https://stackoverflow.com/questions/28838170/multilevel-json-diff-in-python
|
||||
|
||||
- Documentation platform. Consider diplodoc
|
||||
|
||||
- radix-ui
|
||||
- shadcn-ui
|
||||
|
||||
- Zod
|
||||
|
||||
- react-query
|
||||
- react-hook-form
|
|
@ -11,15 +11,7 @@ export default [
|
|||
...typescriptPlugin.configs.recommendedTypeChecked,
|
||||
...typescriptPlugin.configs.stylisticTypeChecked,
|
||||
{
|
||||
ignores: [
|
||||
'**/parser.ts',
|
||||
'**/node_modules/**',
|
||||
'**/public/**',
|
||||
'**/dist/**',
|
||||
'eslint.config.js',
|
||||
'tailwind.config.js',
|
||||
'postcss.config.js'
|
||||
]
|
||||
ignores: ['**/parser.ts', '**/node_modules/**', '**/public/**', '**/dist/**', 'eslint.config.js']
|
||||
},
|
||||
{
|
||||
languageOptions: {
|
||||
|
@ -43,6 +35,12 @@ export default [
|
|||
settings: { react: { version: 'detect' } },
|
||||
rules: {
|
||||
'react-compiler/react-compiler': 'error',
|
||||
'@typescript-eslint/consistent-type-imports': [
|
||||
'warn',
|
||||
{
|
||||
fixStyle: 'inline-type-imports'
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-empty-object-type': ['error', { allowInterfaces: 'with-single-extends' }],
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||
'@typescript-eslint/no-inferrable-types': 'off',
|
||||
|
|
2123
rsconcept/frontend/package-lock.json
generated
2123
rsconcept/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -16,13 +16,14 @@
|
|||
"@dagrejs/dagre": "^1.1.4",
|
||||
"@hookform/resolvers": "^4.1.0",
|
||||
"@lezer/lr": "^1.4.2",
|
||||
"@tanstack/react-query": "^5.66.7",
|
||||
"@tanstack/react-query-devtools": "^5.66.7",
|
||||
"@tanstack/react-query": "^5.66.8",
|
||||
"@tanstack/react-query-devtools": "^5.66.8",
|
||||
"@tanstack/react-table": "^8.21.2",
|
||||
"@uiw/codemirror-themes": "^4.23.8",
|
||||
"@uiw/react-codemirror": "^4.23.8",
|
||||
"axios": "^1.7.9",
|
||||
"clsx": "^2.1.1",
|
||||
"global": "^4.4.0",
|
||||
"html-to-image": "^1.11.13",
|
||||
"js-file-download": "^0.4.12",
|
||||
"qrcode.react": "^4.2.0",
|
||||
|
@ -30,9 +31,10 @@
|
|||
"react-dom": "^19.0.0",
|
||||
"react-error-boundary": "^5.0.0",
|
||||
"react-hook-form": "^7.54.2",
|
||||
"react-icons": "^5.4.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-intl": "^7.1.6",
|
||||
"react-router": "^7.2.0",
|
||||
"react-scan": "^0.1.3",
|
||||
"react-select": "^5.10.0",
|
||||
"react-tabs": "^6.1.0",
|
||||
"react-toastify": "^11.0.3",
|
||||
|
@ -46,6 +48,7 @@
|
|||
"devDependencies": {
|
||||
"@lezer/generator": "^1.7.2",
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@tailwindcss/vite": "^4.0.7",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.13.4",
|
||||
"@types/react": "^19.0.10",
|
||||
|
@ -53,22 +56,20 @@
|
|||
"@typescript-eslint/eslint-plugin": "^8.0.1",
|
||||
"@typescript-eslint/parser": "^8.0.1",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"babel-plugin-react-compiler": "^19.0.0-beta-30d8a17-20250209",
|
||||
"babel-plugin-react-compiler": "^19.0.0-beta-21e868a-20250216",
|
||||
"eslint": "^9.20.1",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"eslint-plugin-react-compiler": "^19.0.0-beta-30d8a17-20250209",
|
||||
"eslint-plugin-react-compiler": "^19.0.0-beta-21e868a-20250216",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"globals": "^15.15.0",
|
||||
"globals": "^16.0.0",
|
||||
"jest": "^29.7.0",
|
||||
"postcss": "^8.5.2",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"tailwindcss": "^4.0.7",
|
||||
"ts-jest": "^29.2.5",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vite": "^6.1.0"
|
||||
"vite": "^6.1.1"
|
||||
},
|
||||
"overrides": {
|
||||
"react": "^19.0.0"
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
export default {
|
||||
plugins: {
|
||||
'postcss-import': {},
|
||||
'tailwindcss/nesting': {},
|
||||
'tailwindcss': {},
|
||||
'autoprefixer': {}
|
||||
}
|
||||
};
|
|
@ -3,7 +3,7 @@ import { Outlet } from 'react-router';
|
|||
|
||||
import { ModalLoader } from '@/components/Modal';
|
||||
import { useAppLayoutStore, useMainHeight, useViewportHeight } from '@/stores/appLayout';
|
||||
import { globals } from '@/utils/constants';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
|
||||
import { NavigationState } from './Navigation/NavigationContext';
|
||||
import { Footer } from './Footer';
|
||||
|
@ -14,13 +14,14 @@ import { GlobalTooltips } from './GlobalTooltips';
|
|||
import { MutationErrors } from './MutationErrors';
|
||||
import { Navigation } from './Navigation';
|
||||
|
||||
function ApplicationLayout() {
|
||||
export function ApplicationLayout() {
|
||||
const mainHeight = useMainHeight();
|
||||
const viewportHeight = useViewportHeight();
|
||||
const showScroll = useAppLayoutStore(state => !state.noScroll);
|
||||
const noNavigationAnimation = useAppLayoutStore(state => state.noNavigationAnimation);
|
||||
const noNavigation = useAppLayoutStore(state => state.noNavigation);
|
||||
const noFooter = useAppLayoutStore(state => state.noFooter);
|
||||
const activeDialog = useDialogsStore(state => state.active);
|
||||
|
||||
return (
|
||||
<NavigationState>
|
||||
|
@ -41,11 +42,9 @@ function ApplicationLayout() {
|
|||
<Navigation />
|
||||
|
||||
<div
|
||||
id={globals.main_scroll}
|
||||
className='overflow-x-auto max-w-[100vw]'
|
||||
style={{
|
||||
maxHeight: viewportHeight
|
||||
}}
|
||||
style={{ maxHeight: viewportHeight }}
|
||||
inert={activeDialog !== null}
|
||||
>
|
||||
<main className='cc-scroll-y' style={{ overflowY: showScroll ? 'scroll' : 'auto', minHeight: mainHeight }}>
|
||||
<GlobalLoader />
|
||||
|
@ -58,5 +57,3 @@ function ApplicationLayout() {
|
|||
</NavigationState>
|
||||
);
|
||||
}
|
||||
|
||||
export default ApplicationLayout;
|
||||
|
|
|
@ -8,7 +8,7 @@ export function ErrorFallback() {
|
|||
const router = useNavigate();
|
||||
|
||||
function resetErrorBoundary() {
|
||||
Promise.resolve(router('/')).catch(console.log);
|
||||
Promise.resolve(router('/')).catch(console.error);
|
||||
}
|
||||
return (
|
||||
<div className='flex flex-col gap-3 my-3 items-center antialiased' role='alert'>
|
||||
|
|
|
@ -4,34 +4,120 @@ import React from 'react';
|
|||
|
||||
import { DialogType, useDialogsStore } from '@/stores/dialogs';
|
||||
|
||||
const DlgChangeInputSchema = React.lazy(() => import('@/features/oss/dialogs/DlgChangeInputSchema'));
|
||||
const DlgChangeLocation = React.lazy(() => import('@/features/library/dialogs/DlgChangeLocation'));
|
||||
const DlgCloneLibraryItem = React.lazy(() => import('@/features/library/dialogs/DlgCloneLibraryItem'));
|
||||
const DlgCreateCst = React.lazy(() => import('@/features/rsform/dialogs/DlgCreateCst'));
|
||||
const DlgCreateOperation = React.lazy(() => import('@/features/oss/dialogs/DlgCreateOperation'));
|
||||
const DlgCreateVersion = React.lazy(() => import('@/features/library/dialogs/DlgCreateVersion'));
|
||||
const DlgCstTemplate = React.lazy(() => import('@/features/rsform/dialogs/DlgCstTemplate'));
|
||||
const DlgDeleteCst = React.lazy(() => import('@/features/rsform/dialogs/DlgDeleteCst'));
|
||||
const DlgDeleteOperation = React.lazy(() => import('@/features/oss/dialogs/DlgDeleteOperation'));
|
||||
const DlgEditEditors = React.lazy(() => import('@/features/library/dialogs/DlgEditEditors'));
|
||||
const DlgEditOperation = React.lazy(() => import('@/features/oss/dialogs/DlgEditOperation'));
|
||||
const DlgEditReference = React.lazy(() => import('@/features/rsform/dialogs/DlgEditReference'));
|
||||
const DlgEditVersions = React.lazy(() => import('@/features/library/dialogs/DlgEditVersions'));
|
||||
const DlgEditWordForms = React.lazy(() => import('@/features/rsform/dialogs/DlgEditWordForms'));
|
||||
const DlgGraphParams = React.lazy(() => import('@/features/rsform/dialogs/DlgGraphParams'));
|
||||
const DlgInlineSynthesis = React.lazy(() => import('@/features/rsform/dialogs/DlgInlineSynthesis'));
|
||||
const DlgRelocateConstituents = React.lazy(() => import('@/features/oss/dialogs/DlgRelocateConstituents'));
|
||||
const DlgRenameCst = React.lazy(() => import('@/features/rsform/dialogs/DlgRenameCst'));
|
||||
const DlgShowAST = React.lazy(() => import('@/features/rsform/dialogs/DlgShowAST'));
|
||||
const DlgShowQR = React.lazy(() => import('@/features/rsform/dialogs/DlgShowQR'));
|
||||
const DlgShowTypeGraph = React.lazy(() => import('@/features/rsform/dialogs/DlgShowTypeGraph'));
|
||||
const DlgSubstituteCst = React.lazy(() => import('@/features/rsform/dialogs/DlgSubstituteCst'));
|
||||
const DlgUploadRSForm = React.lazy(() => import('@/features/rsform/dialogs/DlgUploadRSForm'));
|
||||
const DlgChangeInputSchema = React.lazy(() =>
|
||||
import('@/features/oss/dialogs/DlgChangeInputSchema').then(module => ({ default: module.DlgChangeInputSchema }))
|
||||
);
|
||||
const DlgChangeLocation = React.lazy(() =>
|
||||
import('@/features/library/dialogs/DlgChangeLocation').then(module => ({
|
||||
default: module.DlgChangeLocation
|
||||
}))
|
||||
);
|
||||
const DlgCloneLibraryItem = React.lazy(() =>
|
||||
import('@/features/library/dialogs/DlgCloneLibraryItem').then(module => ({
|
||||
default: module.DlgCloneLibraryItem
|
||||
}))
|
||||
);
|
||||
const DlgCreateCst = React.lazy(() =>
|
||||
import('@/features/rsform/dialogs/DlgCreateCst').then(module => ({ default: module.DlgCreateCst }))
|
||||
);
|
||||
const DlgCreateOperation = React.lazy(() =>
|
||||
import('@/features/oss/dialogs/DlgCreateOperation').then(module => ({
|
||||
default: module.DlgCreateOperation
|
||||
}))
|
||||
);
|
||||
const DlgCreateVersion = React.lazy(() =>
|
||||
import('@/features/library/dialogs/DlgCreateVersion').then(module => ({
|
||||
default: module.DlgCreateVersion
|
||||
}))
|
||||
);
|
||||
const DlgCstTemplate = React.lazy(() =>
|
||||
import('@/features/rsform/dialogs/DlgCstTemplate').then(module => ({
|
||||
default: module.DlgCstTemplate
|
||||
}))
|
||||
);
|
||||
const DlgDeleteCst = React.lazy(() =>
|
||||
import('@/features/rsform/dialogs/DlgDeleteCst').then(module => ({
|
||||
default: module.DlgDeleteCst
|
||||
}))
|
||||
);
|
||||
const DlgDeleteOperation = React.lazy(() =>
|
||||
import('@/features/oss/dialogs/DlgDeleteOperation').then(module => ({
|
||||
default: module.DlgDeleteOperation
|
||||
}))
|
||||
);
|
||||
const DlgEditEditors = React.lazy(() =>
|
||||
import('@/features/library/dialogs/DlgEditEditors').then(module => ({
|
||||
default: module.DlgEditEditors
|
||||
}))
|
||||
);
|
||||
const DlgEditOperation = React.lazy(() =>
|
||||
import('@/features/oss/dialogs/DlgEditOperation').then(module => ({
|
||||
default: module.DlgEditOperation
|
||||
}))
|
||||
);
|
||||
const DlgEditReference = React.lazy(() =>
|
||||
import('@/features/rsform/dialogs/DlgEditReference').then(module => ({
|
||||
default: module.DlgEditReference
|
||||
}))
|
||||
);
|
||||
const DlgEditVersions = React.lazy(() =>
|
||||
import('@/features/library/dialogs/DlgEditVersions').then(module => ({
|
||||
default: module.DlgEditVersions
|
||||
}))
|
||||
);
|
||||
const DlgEditWordForms = React.lazy(() =>
|
||||
import('@/features/rsform/dialogs/DlgEditWordForms').then(module => ({
|
||||
default: module.DlgEditWordForms
|
||||
}))
|
||||
);
|
||||
const DlgInlineSynthesis = React.lazy(() =>
|
||||
import('@/features/rsform/dialogs/DlgInlineSynthesis').then(module => ({
|
||||
default: module.DlgInlineSynthesis
|
||||
}))
|
||||
);
|
||||
const DlgRelocateConstituents = React.lazy(() =>
|
||||
import('@/features/oss/dialogs/DlgRelocateConstituents').then(module => ({
|
||||
default: module.DlgRelocateConstituents
|
||||
}))
|
||||
);
|
||||
const DlgRenameCst = React.lazy(() =>
|
||||
import('@/features/rsform/dialogs/DlgRenameCst').then(module => ({
|
||||
default: module.DlgRenameCst
|
||||
}))
|
||||
);
|
||||
const DlgShowAST = React.lazy(() =>
|
||||
import('@/features/rsform/dialogs/DlgShowAST').then(module => ({
|
||||
default: module.DlgShowAST
|
||||
}))
|
||||
);
|
||||
const DlgShowQR = React.lazy(() =>
|
||||
import('@/features/rsform/dialogs/DlgShowQR').then(module => ({
|
||||
default: module.DlgShowQR
|
||||
}))
|
||||
);
|
||||
const DlgShowTypeGraph = React.lazy(() =>
|
||||
import('@/features/rsform/dialogs/DlgShowTypeGraph').then(module => ({
|
||||
default: module.DlgShowTypeGraph
|
||||
}))
|
||||
);
|
||||
const DlgSubstituteCst = React.lazy(() =>
|
||||
import('@/features/rsform/dialogs/DlgSubstituteCst').then(module => ({
|
||||
default: module.DlgSubstituteCst
|
||||
}))
|
||||
);
|
||||
const DlgUploadRSForm = React.lazy(() =>
|
||||
import('@/features/rsform/dialogs/DlgUploadRSForm').then(module => ({
|
||||
default: module.DlgUploadRSForm
|
||||
}))
|
||||
);
|
||||
const DlgGraphParams = React.lazy(() =>
|
||||
import('@/features/rsform/dialogs/DlgGraphParams').then(module => ({ default: module.DlgGraphParams }))
|
||||
);
|
||||
|
||||
export const GlobalDialogs = () => {
|
||||
const active = useDialogsStore(state => state.active);
|
||||
|
||||
if (active === undefined) {
|
||||
if (active === null) {
|
||||
return null;
|
||||
}
|
||||
switch (active) {
|
||||
|
|
|
@ -7,7 +7,7 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
|||
import { queryClient } from '@/backend/queryClient';
|
||||
|
||||
// prettier-ignore
|
||||
function GlobalProviders({ children }: React.PropsWithChildren) {
|
||||
export function GlobalProviders({ children }: React.PropsWithChildren) {
|
||||
return (
|
||||
<IntlProvider locale='ru' defaultLocale='ru'>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
|
@ -18,5 +18,3 @@ function GlobalProviders({ children }: React.PropsWithChildren) {
|
|||
</QueryClientProvider>
|
||||
</IntlProvider>);
|
||||
}
|
||||
|
||||
export default GlobalProviders;
|
||||
|
|
|
@ -1,32 +1,59 @@
|
|||
'use client';
|
||||
|
||||
import InfoConstituenta from '@/features/rsform/components/InfoConstituenta';
|
||||
import React, { Suspense } from 'react';
|
||||
|
||||
import { Tooltip } from '@/components/Container';
|
||||
import { Loader } from '@/components/Loader';
|
||||
import { useTooltipsStore } from '@/stores/tooltips';
|
||||
import { globals } from '@/utils/constants';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
|
||||
const InfoConstituenta = React.lazy(() =>
|
||||
import('@/features/rsform/components/InfoConstituenta').then(module => ({ default: module.InfoConstituenta }))
|
||||
);
|
||||
|
||||
const InfoOperation = React.lazy(() =>
|
||||
import('@/features/oss/components/InfoOperation').then(module => ({ default: module.InfoOperation }))
|
||||
);
|
||||
|
||||
export const GlobalTooltips = () => {
|
||||
const hoverCst = useTooltipsStore(state => state.activeCst);
|
||||
const hoverOperation = useTooltipsStore(state => state.activeOperation);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip
|
||||
float
|
||||
id={globals.tooltip}
|
||||
id={globalIDs.tooltip}
|
||||
layer='z-topmost'
|
||||
place='right-start'
|
||||
className='mt-8 max-w-[20rem] break-words'
|
||||
/>
|
||||
<Tooltip
|
||||
float
|
||||
id={globals.value_tooltip}
|
||||
id={globalIDs.value_tooltip}
|
||||
layer='z-topmost'
|
||||
className='max-w-[calc(min(40rem,100dvw-2rem))] text-justify'
|
||||
/>
|
||||
<Tooltip clickable id={globals.constituenta_tooltip} layer='z-modalTooltip' className='max-w-[30rem]'>
|
||||
{hoverCst ? <InfoConstituenta data={hoverCst} onClick={event => event.stopPropagation()} /> : <Loader />}
|
||||
<Tooltip
|
||||
clickable
|
||||
id={globalIDs.constituenta_tooltip}
|
||||
layer='z-modalTooltip'
|
||||
className='max-w-[30rem]'
|
||||
hidden={!hoverCst}
|
||||
>
|
||||
<Suspense fallback={<Loader />}>
|
||||
{hoverCst ? <InfoConstituenta data={hoverCst} onClick={event => event.stopPropagation()} /> : null}
|
||||
</Suspense>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
id={globalIDs.operation_tooltip}
|
||||
layer='z-modalTooltip'
|
||||
className='max-w-[35rem] max-h-[40rem] dense'
|
||||
hidden={!hoverOperation}
|
||||
>
|
||||
<Suspense fallback={<Loader />}>
|
||||
{hoverOperation ? <InfoOperation operation={hoverOperation} /> : null}
|
||||
</Suspense>
|
||||
</Tooltip>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -3,7 +3,7 @@ import clsx from 'clsx';
|
|||
import { useMutationErrors } from '@/backend/useMutationErrors';
|
||||
import { Button } from '@/components/Control';
|
||||
import { DescribeError } from '@/components/InfoError';
|
||||
import useEscapeKey from '@/hooks/useEscapeKey';
|
||||
import { useEscapeKey } from '@/hooks/useEscapeKey';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
|
||||
export function MutationErrors() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import useWindowSize from '@/hooks/useWindowSize';
|
||||
import { useWindowSize } from '@/hooks/useWindowSize';
|
||||
import { usePreferencesStore } from '@/stores/preferences';
|
||||
|
||||
function Logo() {
|
||||
export function Logo() {
|
||||
const darkMode = usePreferencesStore(state => state.darkMode);
|
||||
const size = useWindowSize();
|
||||
|
||||
|
@ -13,4 +13,3 @@ function Logo() {
|
|||
/>
|
||||
);
|
||||
}
|
||||
export default Logo;
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { IconLibrary2, IconManuals, IconNewItem2 } from '@/components/Icons';
|
||||
import { CProps } from '@/components/props';
|
||||
import useWindowSize from '@/hooks/useWindowSize';
|
||||
import { useWindowSize } from '@/hooks/useWindowSize';
|
||||
import { useAppLayoutStore } from '@/stores/appLayout';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
import { urls } from '../urls';
|
||||
|
||||
import Logo from './Logo';
|
||||
import NavigationButton from './NavigationButton';
|
||||
import { Logo } from './Logo';
|
||||
import { NavigationButton } from './NavigationButton';
|
||||
import { useConceptNavigation } from './NavigationContext';
|
||||
import ToggleNavigation from './ToggleNavigation';
|
||||
import UserMenu from './UserMenu';
|
||||
import { ToggleNavigation } from './ToggleNavigation';
|
||||
import { UserMenu } from './UserMenu';
|
||||
|
||||
export function Navigation() {
|
||||
const router = useConceptNavigation();
|
||||
const size = useWindowSize();
|
||||
const noNavigationAnimation = useAppLayoutStore(state => state.noNavigationAnimation);
|
||||
|
||||
const navigateHome = (event: CProps.EventMouse) => router.push(urls.home, event.ctrlKey || event.metaKey);
|
||||
const navigateLibrary = (event: CProps.EventMouse) => router.push(urls.library, event.ctrlKey || event.metaKey);
|
||||
const navigateHelp = (event: CProps.EventMouse) => router.push(urls.manuals, event.ctrlKey || event.metaKey);
|
||||
const navigateCreateNew = (event: CProps.EventMouse) =>
|
||||
const navigateHome = (event: React.MouseEvent<Element>) => router.push(urls.home, event.ctrlKey || event.metaKey);
|
||||
const navigateLibrary = (event: React.MouseEvent<Element>) =>
|
||||
router.push(urls.library, event.ctrlKey || event.metaKey);
|
||||
const navigateHelp = (event: React.MouseEvent<Element>) => router.push(urls.manuals, event.ctrlKey || event.metaKey);
|
||||
const navigateCreateNew = (event: React.MouseEvent<Element>) =>
|
||||
router.push(urls.create_schema, event.ctrlKey || event.metaKey);
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { CProps } from '@/components/props';
|
||||
import { globals } from '@/utils/constants';
|
||||
import { type Styling, type Titled } from '@/components/props';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
|
||||
interface NavigationButtonProps extends CProps.Titled, CProps.Styling {
|
||||
interface NavigationButtonProps extends Titled, Styling {
|
||||
text?: string;
|
||||
icon: React.ReactNode;
|
||||
onClick?: (event: CProps.EventMouse) => void;
|
||||
onClick?: (event: React.MouseEvent<Element>) => void;
|
||||
}
|
||||
|
||||
function NavigationButton({
|
||||
export function NavigationButton({
|
||||
icon,
|
||||
title,
|
||||
className,
|
||||
|
@ -23,14 +23,15 @@ function NavigationButton({
|
|||
<button
|
||||
type='button'
|
||||
tabIndex={-1}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globals.tooltip : undefined}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
|
||||
data-tooltip-html={titleHtml}
|
||||
data-tooltip-content={title}
|
||||
data-tooltip-hidden={hideTitle}
|
||||
onClick={onClick}
|
||||
className={clsx(
|
||||
'mr-1 h-full', //
|
||||
'mr-1 h-full',
|
||||
'flex items-center gap-1',
|
||||
'cursor-pointer',
|
||||
'clr-btn-nav cc-animate-color duration-500',
|
||||
'rounded-xl',
|
||||
'font-controls whitespace-nowrap',
|
||||
|
@ -47,5 +48,3 @@ function NavigationButton({
|
|||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default NavigationButton;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
'use client';
|
||||
|
||||
import { createContext, useCallback, useContext, useEffect, useState } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router';
|
||||
import { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
|
||||
import { globals } from '@/utils/constants';
|
||||
import { contextOutsideScope } from '@/utils/labels';
|
||||
|
||||
interface INavigationContext {
|
||||
|
@ -29,68 +28,48 @@ export const useConceptNavigation = () => {
|
|||
|
||||
export const NavigationState = ({ children }: React.PropsWithChildren) => {
|
||||
const router = useNavigate();
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const [isBlocked, setIsBlocked] = useState(false);
|
||||
const validate = useCallback(() => {
|
||||
|
||||
function validate() {
|
||||
return !isBlocked || confirm('Изменения не сохранены. Вы уверены что хотите совершить переход?');
|
||||
}, [isBlocked]);
|
||||
|
||||
const canBack = useCallback(() => !!window.history && window.history?.length !== 0, []);
|
||||
|
||||
const scrollTop = useCallback(() => {
|
||||
window.scrollTo(0, 0);
|
||||
const mainScroll = document.getElementById(globals.main_scroll);
|
||||
if (mainScroll) {
|
||||
mainScroll.scroll(0, 0);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const push = useCallback(
|
||||
(path: string, newTab?: boolean) => {
|
||||
function canBack() {
|
||||
return !!window.history && window.history?.length !== 0;
|
||||
}
|
||||
|
||||
function push(path: string, newTab?: boolean) {
|
||||
if (newTab) {
|
||||
window.open(`${path}`, '_blank');
|
||||
return;
|
||||
}
|
||||
if (validate()) {
|
||||
scrollTop();
|
||||
Promise.resolve(router(path, { viewTransition: true })).catch(console.log);
|
||||
Promise.resolve(router(path, { viewTransition: true })).catch(console.error);
|
||||
setIsBlocked(false);
|
||||
}
|
||||
},
|
||||
[router, validate, scrollTop]
|
||||
);
|
||||
}
|
||||
|
||||
const replace = useCallback(
|
||||
(path: string) => {
|
||||
function replace(path: string) {
|
||||
if (validate()) {
|
||||
scrollTop();
|
||||
Promise.resolve(router(path, { replace: true, viewTransition: true })).catch(console.log);
|
||||
Promise.resolve(router(path, { replace: true, viewTransition: true })).catch(console.error);
|
||||
setIsBlocked(false);
|
||||
}
|
||||
},
|
||||
[router, validate, scrollTop]
|
||||
);
|
||||
}
|
||||
|
||||
const back = useCallback(() => {
|
||||
function back() {
|
||||
if (validate()) {
|
||||
scrollTop();
|
||||
Promise.resolve(router(-1)).catch(console.log);
|
||||
Promise.resolve(router(-1)).catch(console.error);
|
||||
setIsBlocked(false);
|
||||
}
|
||||
}, [router, validate, scrollTop]);
|
||||
}
|
||||
|
||||
const forward = useCallback(() => {
|
||||
function forward() {
|
||||
if (validate()) {
|
||||
scrollTop();
|
||||
Promise.resolve(router(1)).catch(console.log);
|
||||
Promise.resolve(router(1)).catch(console.error);
|
||||
setIsBlocked(false);
|
||||
}
|
||||
}, [router, validate, scrollTop]);
|
||||
|
||||
useEffect(() => {
|
||||
scrollTop();
|
||||
}, [pathname, scrollTop]);
|
||||
}
|
||||
|
||||
return (
|
||||
<NavigationContext
|
||||
|
|
|
@ -3,9 +3,9 @@ import clsx from 'clsx';
|
|||
import { IconDarkTheme, IconLightTheme, IconPin, IconUnpin } from '@/components/Icons';
|
||||
import { useAppLayoutStore } from '@/stores/appLayout';
|
||||
import { usePreferencesStore } from '@/stores/preferences';
|
||||
import { globals, PARAMETER } from '@/utils/constants';
|
||||
import { globalIDs, PARAMETER } from '@/utils/constants';
|
||||
|
||||
function ToggleNavigation() {
|
||||
export function ToggleNavigation() {
|
||||
const darkMode = usePreferencesStore(state => state.darkMode);
|
||||
const toggleDarkMode = usePreferencesStore(state => state.toggleDarkMode);
|
||||
const noNavigation = useAppLayoutStore(state => state.noNavigation);
|
||||
|
@ -34,7 +34,7 @@ function ToggleNavigation() {
|
|||
type='button'
|
||||
className='p-1'
|
||||
onClick={toggleDarkMode}
|
||||
data-tooltip-id={globals.tooltip}
|
||||
data-tooltip-id={globalIDs.tooltip}
|
||||
data-tooltip-content={darkMode ? 'Тема: Темная' : 'Тема: Светлая'}
|
||||
>
|
||||
{darkMode ? <IconDarkTheme size='0.75rem' /> : null}
|
||||
|
@ -46,7 +46,7 @@ function ToggleNavigation() {
|
|||
type='button'
|
||||
className='p-1'
|
||||
onClick={toggleNoNavigation}
|
||||
data-tooltip-id={globals.tooltip}
|
||||
data-tooltip-id={globalIDs.tooltip}
|
||||
data-tooltip-content={noNavigationAnimation ? 'Показать навигацию' : 'Скрыть навигацию'}
|
||||
>
|
||||
{!noNavigationAnimation ? <IconPin size={iconSize} /> : null}
|
||||
|
@ -55,5 +55,3 @@ function ToggleNavigation() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ToggleNavigation;
|
||||
|
|
|
@ -3,14 +3,14 @@ import { useAuthSuspense } from '@/features/auth';
|
|||
import { IconLogin, IconUser2 } from '@/components/Icons';
|
||||
import { usePreferencesStore } from '@/stores/preferences';
|
||||
|
||||
import NavigationButton from './NavigationButton';
|
||||
import { NavigationButton } from './NavigationButton';
|
||||
|
||||
interface UserButtonProps {
|
||||
onLogin: () => void;
|
||||
onClickUser: () => void;
|
||||
}
|
||||
|
||||
function UserButton({ onLogin, onClickUser }: UserButtonProps) {
|
||||
export function UserButton({ onLogin, onClickUser }: UserButtonProps) {
|
||||
const { user, isAnonymous } = useAuthSuspense();
|
||||
const adminMode = usePreferencesStore(state => state.adminMode);
|
||||
if (isAnonymous) {
|
||||
|
@ -32,5 +32,3 @@ function UserButton({ onLogin, onClickUser }: UserButtonProps) {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default UserButton;
|
||||
|
|
|
@ -15,7 +15,6 @@ import {
|
|||
IconRESTapi,
|
||||
IconUser
|
||||
} from '@/components/Icons';
|
||||
import { CProps } from '@/components/props';
|
||||
import { usePreferencesStore } from '@/stores/preferences';
|
||||
|
||||
import { urls } from '../urls';
|
||||
|
@ -27,7 +26,7 @@ interface UserDropdownProps {
|
|||
hideDropdown: () => void;
|
||||
}
|
||||
|
||||
function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
||||
export function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
||||
const router = useConceptNavigation();
|
||||
const { user } = useAuthSuspense();
|
||||
const { logout } = useLogout();
|
||||
|
@ -39,7 +38,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
|||
const adminMode = usePreferencesStore(state => state.adminMode);
|
||||
const toggleAdminMode = usePreferencesStore(state => state.toggleAdminMode);
|
||||
|
||||
function navigateProfile(event: CProps.EventMouse) {
|
||||
function navigateProfile(event: React.MouseEvent<Element>) {
|
||||
hideDropdown();
|
||||
router.push(urls.profile, event.ctrlKey || event.metaKey);
|
||||
}
|
||||
|
@ -54,7 +53,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
|||
void logout().then(() => router.push(urls.admin, true));
|
||||
}
|
||||
|
||||
function gotoIcons(event: CProps.EventMouse) {
|
||||
function gotoIcons(event: React.MouseEvent<Element>) {
|
||||
hideDropdown();
|
||||
router.push(urls.icons, event.ctrlKey || event.metaKey);
|
||||
}
|
||||
|
@ -64,7 +63,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
|||
router.push(urls.rest_api, true);
|
||||
}
|
||||
|
||||
function gotoDatabaseSchema(event: CProps.EventMouse) {
|
||||
function gotoDatabaseSchema(event: React.MouseEvent<Element>) {
|
||||
hideDropdown();
|
||||
router.push(urls.database_schema, event.ctrlKey || event.metaKey);
|
||||
}
|
||||
|
@ -141,5 +140,3 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
|||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
export default UserDropdown;
|
||||
|
|
|
@ -6,10 +6,10 @@ import { Loader } from '@/components/Loader';
|
|||
import { urls } from '../urls';
|
||||
|
||||
import { useConceptNavigation } from './NavigationContext';
|
||||
import UserButton from './UserButton';
|
||||
import UserDropdown from './UserDropdown';
|
||||
import { UserButton } from './UserButton';
|
||||
import { UserDropdown } from './UserDropdown';
|
||||
|
||||
function UserMenu() {
|
||||
export function UserMenu() {
|
||||
const router = useConceptNavigation();
|
||||
const menu = useDropdown();
|
||||
return (
|
||||
|
@ -21,5 +21,3 @@ function UserMenu() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default UserMenu;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { createBrowserRouter } from 'react-router';
|
||||
|
||||
import { prefetchAuth } from '@/features/auth/backend/useAuth';
|
||||
import LoginPage from '@/features/auth/pages/LoginPage';
|
||||
import HomePage from '@/features/home/HomePage';
|
||||
import NotFoundPage from '@/features/home/NotFoundPage';
|
||||
import { LoginPage } from '@/features/auth/pages/LoginPage';
|
||||
import { HomePage } from '@/features/home/HomePage';
|
||||
import { NotFoundPage } from '@/features/home/NotFoundPage';
|
||||
import { prefetchLibrary } from '@/features/library/backend/useLibrary';
|
||||
import CreateItemPage from '@/features/library/pages/CreateItemPage';
|
||||
import { CreateItemPage } from '@/features/library/pages/CreateItemPage';
|
||||
import { prefetchOSS } from '@/features/oss/backend/useOSS';
|
||||
import { prefetchRSForm } from '@/features/rsform/backend/useRSForm';
|
||||
import { prefetchProfile } from '@/features/users/backend/useProfile';
|
||||
|
@ -13,7 +13,7 @@ import { prefetchUsers } from '@/features/users/backend/useUsers';
|
|||
|
||||
import { Loader } from '@/components/Loader';
|
||||
|
||||
import ApplicationLayout from './ApplicationLayout';
|
||||
import { ApplicationLayout } from './ApplicationLayout';
|
||||
import { ErrorFallback } from './ErrorFallback';
|
||||
import { routes } from './urls';
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@ import { RouterProvider } from 'react-router';
|
|||
|
||||
import { Router } from './Router';
|
||||
|
||||
function App() {
|
||||
export function App() {
|
||||
return <RouterProvider router={Router} />;
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
* Module: generic API for backend REST communications using axios library.
|
||||
*/
|
||||
import { toast } from 'react-toastify';
|
||||
import axios, { AxiosError, AxiosRequestConfig } from 'axios';
|
||||
import { z, ZodError } from 'zod';
|
||||
import axios, { type AxiosError, type AxiosRequestConfig } from 'axios';
|
||||
import { type z, ZodError } from 'zod';
|
||||
|
||||
import { buildConstants } from '@/utils/buildConstants';
|
||||
import { errorMsg } from '@/utils/labels';
|
||||
|
@ -32,8 +32,6 @@ axiosInstance.interceptors.request.use(config => {
|
|||
});
|
||||
|
||||
// ================ Data transfer types ================
|
||||
export type DataCallback<ResponseData = undefined> = (data: ResponseData) => void;
|
||||
|
||||
export interface IFrontRequest<RequestData, ResponseData> {
|
||||
data?: RequestData;
|
||||
successMessage?: string | ((data: ResponseData) => string);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { QueryClient } from '@tanstack/react-query';
|
||||
import { ZodError } from 'zod';
|
||||
import { type ZodError } from 'zod';
|
||||
|
||||
import { AxiosError } from './apiTransport';
|
||||
import { type AxiosError } from './apiTransport';
|
||||
import { DELAYS } from './configuration';
|
||||
|
||||
declare module '@tanstack/react-query' {
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
import { useState } from 'react';
|
||||
import { useMutationState, useQueryClient } from '@tanstack/react-query';
|
||||
import { useMutationState } from '@tanstack/react-query';
|
||||
|
||||
import { KEYS } from './configuration';
|
||||
|
||||
export const useMutationErrors = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const [ignored, setIgnored] = useState<(Error | null)[]>([]);
|
||||
const [ignored, setIgnored] = useState<Error[]>([]);
|
||||
const mutationErrors = useMutationState({
|
||||
filters: { mutationKey: [KEYS.global_mutation], status: 'error' },
|
||||
select: mutation => mutation.state.error
|
||||
select: mutation => mutation.state.error!
|
||||
});
|
||||
|
||||
console.log(queryClient.getMutationCache().getAll());
|
||||
|
||||
function resetErrors() {
|
||||
setIgnored(mutationErrors);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { CProps } from '../props';
|
||||
import { type Styling } from '../props';
|
||||
|
||||
interface DividerProps extends CProps.Styling {
|
||||
interface DividerProps extends Styling {
|
||||
/** Indicates whether the divider is vertical. */
|
||||
vertical?: boolean;
|
||||
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { CProps } from '../props';
|
||||
|
||||
/**
|
||||
* `flex` column container.
|
||||
* This component is useful for creating vertical layouts with flexbox.
|
||||
*/
|
||||
export function FlexColumn({ className, children, ...restProps }: CProps.Div) {
|
||||
export function FlexColumn({ className, children, ...restProps }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div className={clsx('cc-column', className)} {...restProps}>
|
||||
{children}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { CProps } from '../props';
|
||||
import { type Styling } from '../props';
|
||||
|
||||
interface OverlayProps extends CProps.Styling {
|
||||
interface OverlayProps extends Styling {
|
||||
/** Id of the overlay. */
|
||||
id?: string;
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
'use client';
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { ITooltip, Tooltip as TooltipImpl } from 'react-tooltip';
|
||||
import { type ITooltip, Tooltip as TooltipImpl } from 'react-tooltip';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { usePreferencesStore } from '@/stores/preferences';
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { globals } from '@/utils/constants';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
|
||||
import { CProps } from '../props';
|
||||
import { type Button as ButtonStyle, type Control } from '../props';
|
||||
|
||||
interface ButtonProps extends CProps.Control, CProps.Colors, CProps.Button {
|
||||
interface ButtonProps extends Control, ButtonStyle {
|
||||
/** Icon to display first. */
|
||||
icon?: React.ReactNode;
|
||||
|
||||
|
@ -32,7 +32,6 @@ export function Button({
|
|||
disabled,
|
||||
noBorder,
|
||||
noOutline,
|
||||
colors = 'clr-btn-default',
|
||||
className,
|
||||
...restProps
|
||||
}: ButtonProps) {
|
||||
|
@ -43,20 +42,19 @@ export function Button({
|
|||
className={clsx(
|
||||
'inline-flex gap-2 items-center justify-center',
|
||||
'select-none disabled:cursor-auto',
|
||||
'cc-animate-color',
|
||||
'clr-btn-default cc-animate-color',
|
||||
{
|
||||
'border rounded': !noBorder,
|
||||
'border rounded-sm': !noBorder,
|
||||
'px-1': dense,
|
||||
'px-3 py-1': !dense,
|
||||
'cursor-progress': loading,
|
||||
'cursor-pointer': !loading,
|
||||
'outline-none': noOutline,
|
||||
'outline-hidden': noOutline,
|
||||
'clr-outline': !noOutline
|
||||
},
|
||||
className,
|
||||
colors
|
||||
className
|
||||
)}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globals.tooltip : undefined}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
|
||||
data-tooltip-html={titleHtml}
|
||||
data-tooltip-content={title}
|
||||
data-tooltip-hidden={hideTitle}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { globals } from '@/utils/constants';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
|
||||
import { CProps } from '../props';
|
||||
import { type Button } from '../props';
|
||||
|
||||
interface MiniButtonProps extends CProps.Button {
|
||||
interface MiniButtonProps extends Button {
|
||||
/** Button type. */
|
||||
type?: 'button' | 'submit';
|
||||
|
||||
|
@ -43,12 +43,12 @@ export function MiniButton({
|
|||
'cursor-pointer disabled:cursor-auto',
|
||||
{
|
||||
'px-1 py-1': !noPadding,
|
||||
'outline-none': noHover,
|
||||
'outline-hidden': noHover,
|
||||
'clr-hover': !noHover
|
||||
},
|
||||
className
|
||||
)}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globals.tooltip : undefined}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
|
||||
data-tooltip-html={titleHtml}
|
||||
data-tooltip-content={title}
|
||||
data-tooltip-hidden={hideTitle}
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { globals } from '@/utils/constants';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
|
||||
import { CProps } from '../props';
|
||||
import { type Button } from '../props';
|
||||
|
||||
interface SelectorButtonProps extends CProps.Button {
|
||||
interface SelectorButtonProps extends Button {
|
||||
/** Text to display in the button. */
|
||||
text?: string;
|
||||
|
||||
/** Icon to display in the button. */
|
||||
icon?: React.ReactNode;
|
||||
|
||||
/** Classnames for the colors of the button. */
|
||||
colors?: string;
|
||||
|
||||
/** Indicates if button background should be transparent. */
|
||||
transparent?: boolean;
|
||||
}
|
||||
|
@ -26,7 +23,6 @@ export function SelectorButton({
|
|||
icon,
|
||||
title,
|
||||
titleHtml,
|
||||
colors = 'clr-btn-default',
|
||||
className,
|
||||
transparent,
|
||||
hideTitle,
|
||||
|
@ -44,12 +40,11 @@ export function SelectorButton({
|
|||
'cc-animate-color',
|
||||
{
|
||||
'clr-hover': transparent,
|
||||
'border': !transparent
|
||||
'clr-btn-default border': !transparent
|
||||
},
|
||||
className,
|
||||
!transparent && colors
|
||||
className
|
||||
)}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globals.tooltip : undefined}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
|
||||
data-tooltip-html={titleHtml}
|
||||
data-tooltip-content={title}
|
||||
data-tooltip-hidden={hideTitle}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { CProps } from '../props';
|
||||
import { type Button } from '../props';
|
||||
|
||||
interface SubmitButtonProps extends CProps.Button {
|
||||
interface SubmitButtonProps extends Button {
|
||||
/** Text to display in the button. */
|
||||
text?: string;
|
||||
|
||||
|
|
|
@ -3,27 +3,27 @@
|
|||
|
||||
import { useMemo, useState } from 'react';
|
||||
import {
|
||||
ColumnSort,
|
||||
type ColumnSort,
|
||||
createColumnHelper,
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
PaginationState,
|
||||
RowData,
|
||||
type PaginationState,
|
||||
type RowData,
|
||||
type RowSelectionState,
|
||||
SortingState,
|
||||
TableOptions,
|
||||
type SortingState,
|
||||
type TableOptions,
|
||||
useReactTable,
|
||||
type VisibilityState
|
||||
} from '@tanstack/react-table';
|
||||
|
||||
import { CProps } from '../props';
|
||||
import { type Styling } from '../props';
|
||||
|
||||
import DefaultNoData from './DefaultNoData';
|
||||
import PaginationTools from './PaginationTools';
|
||||
import TableBody from './TableBody';
|
||||
import TableFooter from './TableFooter';
|
||||
import TableHeader from './TableHeader';
|
||||
import { DefaultNoData } from './DefaultNoData';
|
||||
import { PaginationTools } from './PaginationTools';
|
||||
import { TableBody } from './TableBody';
|
||||
import { TableFooter } from './TableFooter';
|
||||
import { TableHeader } from './TableHeader';
|
||||
|
||||
export { type ColumnSort, createColumnHelper, type RowSelectionState, type VisibilityState };
|
||||
|
||||
|
@ -37,7 +37,7 @@ export interface IConditionalStyle<TData> {
|
|||
}
|
||||
|
||||
export interface DataTableProps<TData extends RowData>
|
||||
extends CProps.Styling,
|
||||
extends Styling,
|
||||
Pick<TableOptions<TData>, 'data' | 'columns' | 'onRowSelectionChange' | 'onColumnVisibilityChange'> {
|
||||
/** Id of the component. */
|
||||
id?: string;
|
||||
|
@ -67,10 +67,10 @@ export interface DataTableProps<TData extends RowData>
|
|||
noDataComponent?: React.ReactNode;
|
||||
|
||||
/** Callback to be called when a row is clicked. */
|
||||
onRowClicked?: (rowData: TData, event: CProps.EventMouse) => void;
|
||||
onRowClicked?: (rowData: TData, event: React.MouseEvent<Element>) => void;
|
||||
|
||||
/** Callback to be called when a row is double clicked. */
|
||||
onRowDoubleClicked?: (rowData: TData, event: CProps.EventMouse) => void;
|
||||
onRowDoubleClicked?: (rowData: TData, event: React.MouseEvent<Element>) => void;
|
||||
|
||||
/** Enable row selection. */
|
||||
enableRowSelection?: boolean;
|
||||
|
@ -109,7 +109,7 @@ export interface DataTableProps<TData extends RowData>
|
|||
* @param headPosition - Top position of sticky header (0 if no other sticky elements are present).
|
||||
* No sticky header if omitted
|
||||
*/
|
||||
function DataTable<TData extends RowData>({
|
||||
export function DataTable<TData extends RowData>({
|
||||
id,
|
||||
style,
|
||||
className,
|
||||
|
@ -141,7 +141,7 @@ function DataTable<TData extends RowData>({
|
|||
...restProps
|
||||
}: DataTableProps<TData>) {
|
||||
const [sorting, setSorting] = useState<SortingState>(initialSorting ? [initialSorting] : []);
|
||||
const [lastSelected, setLastSelected] = useState<string | undefined>(undefined);
|
||||
const [lastSelected, setLastSelected] = useState<string | null>(null);
|
||||
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
|
@ -198,7 +198,7 @@ function DataTable<TData extends RowData>({
|
|||
enableRowSelection={enableRowSelection}
|
||||
enableSorting={enableSorting}
|
||||
headPosition={headPosition}
|
||||
resetLastSelected={() => setLastSelected(undefined)}
|
||||
resetLastSelected={() => setLastSelected(null)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
|
@ -229,5 +229,3 @@ function DataTable<TData extends RowData>({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DataTable;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
function defaultNoDataComponent() {
|
||||
export function DefaultNoData() {
|
||||
return <div className='p-2 text-center'>Данные отсутствуют</div>;
|
||||
}
|
||||
|
||||
export default defaultNoDataComponent;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
'use no memo';
|
||||
|
||||
import { useCallback } from 'react';
|
||||
import { Table } from '@tanstack/react-table';
|
||||
import { type Table } from '@tanstack/react-table';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { prefixes } from '@/utils/constants';
|
||||
|
@ -16,7 +16,7 @@ interface PaginationToolsProps<TData> {
|
|||
onChangePaginationOption?: (newValue: number) => void;
|
||||
}
|
||||
|
||||
function PaginationTools<TData>({
|
||||
export function PaginationTools<TData>({
|
||||
id,
|
||||
table,
|
||||
paginationOptions,
|
||||
|
@ -106,5 +106,3 @@ function PaginationTools<TData>({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PaginationTools;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use no memo';
|
||||
|
||||
import { Table } from '@tanstack/react-table';
|
||||
import { type Table } from '@tanstack/react-table';
|
||||
|
||||
import { CheckboxTristate } from '../Input';
|
||||
|
||||
|
@ -9,7 +9,7 @@ interface SelectAllProps<TData> {
|
|||
resetLastSelected: () => void;
|
||||
}
|
||||
|
||||
function SelectAll<TData>({ table, resetLastSelected }: SelectAllProps<TData>) {
|
||||
export function SelectAll<TData>({ table, resetLastSelected }: SelectAllProps<TData>) {
|
||||
function handleChange(value: boolean | null) {
|
||||
resetLastSelected();
|
||||
table.toggleAllPageRowsSelected(value !== false);
|
||||
|
@ -26,5 +26,3 @@ function SelectAll<TData>({ table, resetLastSelected }: SelectAllProps<TData>) {
|
|||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default SelectAll;
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
'use no memo';
|
||||
|
||||
import { Row } from '@tanstack/react-table';
|
||||
import { type Row } from '@tanstack/react-table';
|
||||
|
||||
import { Checkbox } from '../Input';
|
||||
|
||||
interface SelectRowProps<TData> {
|
||||
row: Row<TData>;
|
||||
onChangeLastSelected: (newValue: string | undefined) => void;
|
||||
onChangeLastSelected: (newValue: string) => void;
|
||||
}
|
||||
|
||||
function SelectRow<TData>({ row, onChangeLastSelected }: SelectRowProps<TData>) {
|
||||
export function SelectRow<TData>({ row, onChangeLastSelected }: SelectRowProps<TData>) {
|
||||
function handleChange(value: boolean) {
|
||||
onChangeLastSelected(row.id);
|
||||
row.toggleSelected(value);
|
||||
|
@ -17,5 +17,3 @@ function SelectRow<TData>({ row, onChangeLastSelected }: SelectRowProps<TData>)
|
|||
|
||||
return <Checkbox tabIndex={-1} value={row.getIsSelected()} onChange={handleChange} />;
|
||||
}
|
||||
|
||||
export default SelectRow;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use no memo';
|
||||
|
||||
import { Column } from '@tanstack/react-table';
|
||||
import { type Column } from '@tanstack/react-table';
|
||||
|
||||
import { IconSortAsc, IconSortDesc } from '../Icons';
|
||||
|
||||
|
@ -8,7 +8,7 @@ interface SortingIconProps<TData> {
|
|||
column: Column<TData>;
|
||||
}
|
||||
|
||||
function SortingIcon<TData>({ column }: SortingIconProps<TData>) {
|
||||
export function SortingIcon<TData>({ column }: SortingIconProps<TData>) {
|
||||
return (
|
||||
<>
|
||||
{{
|
||||
|
@ -18,5 +18,3 @@ function SortingIcon<TData>({ column }: SortingIconProps<TData>) {
|
|||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default SortingIcon;
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
'use no memo';
|
||||
|
||||
import { Cell, flexRender, Row, Table } from '@tanstack/react-table';
|
||||
import { type Cell, flexRender, type Row, type Table } from '@tanstack/react-table';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { CProps } from '../props';
|
||||
|
||||
import SelectRow from './SelectRow';
|
||||
import { IConditionalStyle } from '.';
|
||||
import { SelectRow } from './SelectRow';
|
||||
import { type IConditionalStyle } from '.';
|
||||
|
||||
interface TableBodyProps<TData> {
|
||||
table: Table<TData>;
|
||||
|
@ -15,14 +13,14 @@ interface TableBodyProps<TData> {
|
|||
enableRowSelection?: boolean;
|
||||
conditionalRowStyles?: IConditionalStyle<TData>[];
|
||||
|
||||
lastSelected: string | undefined;
|
||||
onChangeLastSelected: (newValue: string | undefined) => void;
|
||||
lastSelected: string | null;
|
||||
onChangeLastSelected: (newValue: string | null) => void;
|
||||
|
||||
onRowClicked?: (rowData: TData, event: CProps.EventMouse) => void;
|
||||
onRowDoubleClicked?: (rowData: TData, event: CProps.EventMouse) => void;
|
||||
onRowClicked?: (rowData: TData, event: React.MouseEvent<Element>) => void;
|
||||
onRowDoubleClicked?: (rowData: TData, event: React.MouseEvent<Element>) => void;
|
||||
}
|
||||
|
||||
function TableBody<TData>({
|
||||
export function TableBody<TData>({
|
||||
table,
|
||||
dense,
|
||||
noHeader,
|
||||
|
@ -33,7 +31,7 @@ function TableBody<TData>({
|
|||
onRowClicked,
|
||||
onRowDoubleClicked
|
||||
}: TableBodyProps<TData>) {
|
||||
function handleRowClicked(target: Row<TData>, event: CProps.EventMouse) {
|
||||
function handleRowClicked(target: Row<TData>, event: React.MouseEvent<Element>) {
|
||||
onRowClicked?.(target.original, event);
|
||||
if (enableRowSelection && target.getCanSelect()) {
|
||||
if (event.shiftKey && !!lastSelected && lastSelected !== target.id) {
|
||||
|
@ -49,7 +47,7 @@ function TableBody<TData>({
|
|||
newSelection[row.id] = !target.getIsSelected();
|
||||
});
|
||||
table.setRowSelection(prev => ({ ...prev, ...newSelection }));
|
||||
onChangeLastSelected(undefined);
|
||||
onChangeLastSelected(null);
|
||||
} else {
|
||||
onChangeLastSelected(target.id);
|
||||
target.toggleSelected(!target.getIsSelected());
|
||||
|
@ -94,7 +92,7 @@ function TableBody<TData>({
|
|||
width: noHeader && index === 0 ? `calc(var(--col-${cell.column.id}-size) * 1px)` : 'auto'
|
||||
}}
|
||||
onClick={event => handleRowClicked(row, event)}
|
||||
onDoubleClick={event => (onRowDoubleClicked ? onRowDoubleClicked(row.original, event) : undefined)}
|
||||
onDoubleClick={event => onRowDoubleClicked?.(row.original, event)}
|
||||
>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</td>
|
||||
|
@ -104,5 +102,3 @@ function TableBody<TData>({
|
|||
</tbody>
|
||||
);
|
||||
}
|
||||
|
||||
export default TableBody;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
'use no memo';
|
||||
|
||||
import { flexRender, Header, HeaderGroup, Table } from '@tanstack/react-table';
|
||||
import { flexRender, type Header, type HeaderGroup, type Table } from '@tanstack/react-table';
|
||||
|
||||
interface TableFooterProps<TData> {
|
||||
table: Table<TData>;
|
||||
}
|
||||
|
||||
function TableFooter<TData>({ table }: TableFooterProps<TData>) {
|
||||
export function TableFooter<TData>({ table }: TableFooterProps<TData>) {
|
||||
return (
|
||||
<tfoot>
|
||||
{table.getFooterGroups().map((footerGroup: HeaderGroup<TData>) => (
|
||||
|
@ -21,5 +21,3 @@ function TableFooter<TData>({ table }: TableFooterProps<TData>) {
|
|||
</tfoot>
|
||||
);
|
||||
}
|
||||
|
||||
export default TableFooter;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
'use no memo';
|
||||
|
||||
import { flexRender, Header, HeaderGroup, Table } from '@tanstack/react-table';
|
||||
import { flexRender, type Header, type HeaderGroup, type Table } from '@tanstack/react-table';
|
||||
|
||||
import SelectAll from './SelectAll';
|
||||
import SortingIcon from './SortingIcon';
|
||||
import { SelectAll } from './SelectAll';
|
||||
import { SortingIcon } from './SortingIcon';
|
||||
|
||||
interface TableHeaderProps<TData> {
|
||||
table: Table<TData>;
|
||||
|
@ -13,7 +13,7 @@ interface TableHeaderProps<TData> {
|
|||
resetLastSelected: () => void;
|
||||
}
|
||||
|
||||
function TableHeader<TData>({
|
||||
export function TableHeader<TData>({
|
||||
table,
|
||||
headPosition,
|
||||
enableRowSelection,
|
||||
|
@ -61,5 +61,3 @@ function TableHeader<TData>({
|
|||
</thead>
|
||||
);
|
||||
}
|
||||
|
||||
export default TableHeader;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export {
|
||||
createColumnHelper,
|
||||
default,
|
||||
DataTable,
|
||||
type IConditionalStyle,
|
||||
type RowSelectionState,
|
||||
type VisibilityState
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
IconHide,
|
||||
IconMoveDown,
|
||||
IconMoveUp,
|
||||
IconProps,
|
||||
type IconProps,
|
||||
IconPublic,
|
||||
IconSettings,
|
||||
IconShow,
|
||||
|
|
|
@ -2,9 +2,9 @@ import clsx from 'clsx';
|
|||
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
import { CProps } from '../props';
|
||||
import { type Styling } from '../props';
|
||||
|
||||
interface DropdownProps extends CProps.Styling {
|
||||
interface DropdownProps extends Styling {
|
||||
/** Indicates whether the dropdown should stretch to the left. */
|
||||
stretchLeft?: boolean;
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { CProps } from '@/components/props';
|
||||
import { globals } from '@/utils/constants';
|
||||
import { type Button } from '@/components/props';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
|
||||
interface DropdownButtonProps extends CProps.Button {
|
||||
interface DropdownButtonProps extends Button {
|
||||
/** Icon to display first (not used if children are provided). */
|
||||
icon?: React.ReactNode;
|
||||
|
||||
|
@ -36,7 +36,7 @@ export function DropdownButton({
|
|||
onClick={onClick}
|
||||
className={clsx(
|
||||
'px-3 py-1 inline-flex items-center gap-2',
|
||||
'text-left text-sm overflow-ellipsis whitespace-nowrap',
|
||||
'text-left text-sm text-ellipsis whitespace-nowrap',
|
||||
'disabled:clr-text-controls',
|
||||
'cc-animate-color',
|
||||
{
|
||||
|
@ -46,7 +46,7 @@ export function DropdownButton({
|
|||
},
|
||||
className
|
||||
)}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globals.tooltip : undefined}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
|
||||
data-tooltip-html={titleHtml}
|
||||
data-tooltip-content={title}
|
||||
data-tooltip-hidden={hideTitle}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { Checkbox, CheckboxProps } from '../Input';
|
||||
import { Checkbox, type CheckboxProps } from '../Input';
|
||||
|
||||
/** Animated {@link Checkbox} inside a {@link Dropdown} item. */
|
||||
export function DropdownCheckbox({ onChange: setValue, disabled, ...restProps }: CheckboxProps) {
|
||||
|
@ -8,7 +8,7 @@ export function DropdownCheckbox({ onChange: setValue, disabled, ...restProps }:
|
|||
<div
|
||||
className={clsx(
|
||||
'px-3 py-1',
|
||||
'text-left overflow-ellipsis whitespace-nowrap',
|
||||
'text-left text-ellipsis whitespace-nowrap',
|
||||
'disabled:clr-text-controls cc-animate-color',
|
||||
!!setValue && !disabled && 'clr-hover'
|
||||
)}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { useRef, useState } from 'react';
|
||||
|
||||
import useClickedOutside from '@/hooks/useClickedOutside';
|
||||
import { useClickedOutside } from '@/hooks/useClickedOutside';
|
||||
|
||||
export function useDropdown() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { EdgeProps, getStraightPath } from 'reactflow';
|
||||
import { type EdgeProps, getStraightPath } from 'reactflow';
|
||||
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
const RADIUS = PARAMETER.graphNodeRadius + PARAMETER.graphNodePadding;
|
||||
|
||||
function DynamicEdge({ id, markerEnd, style, ...props }: EdgeProps) {
|
||||
export function DynamicEdge({ id, markerEnd, style, ...props }: EdgeProps) {
|
||||
const sourceY = props.sourceY - PARAMETER.graphNodeRadius - PARAMETER.graphHandleSize;
|
||||
const targetY = props.targetY + PARAMETER.graphNodeRadius + PARAMETER.graphHandleSize;
|
||||
|
||||
|
@ -32,5 +32,3 @@ function DynamicEdge({ id, markerEnd, style, ...props }: EdgeProps) {
|
|||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default DynamicEdge;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import clsx from 'clsx';
|
||||
import { ZodError } from 'zod';
|
||||
|
||||
import { AxiosError, isAxiosError } from '@/backend/apiTransport';
|
||||
import { type AxiosError, isAxiosError } from '@/backend/apiTransport';
|
||||
import { isResponseHtml } from '@/utils/utils';
|
||||
|
||||
import { PrettyJson } from './View';
|
||||
|
||||
export type ErrorData = string | Error | AxiosError | ZodError | undefined | null;
|
||||
export type ErrorData = string | Error | AxiosError | ZodError;
|
||||
|
||||
interface InfoErrorProps {
|
||||
error: ErrorData;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { globals } from '@/utils/constants';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
|
||||
import { CheckboxChecked } from '../Icons';
|
||||
import { CProps } from '../props';
|
||||
import { type Button } from '../props';
|
||||
|
||||
export interface CheckboxProps extends Omit<CProps.Button, 'value' | 'onClick' | 'onChange'> {
|
||||
export interface CheckboxProps extends Omit<Button, 'value' | 'onClick' | 'onChange'> {
|
||||
/** Label to display next to the checkbox. */
|
||||
label?: string;
|
||||
|
||||
|
@ -35,7 +35,7 @@ export function Checkbox({
|
|||
}: CheckboxProps) {
|
||||
const cursor = disabled ? 'cursor-arrow' : onChange ? 'cursor-pointer' : '';
|
||||
|
||||
function handleClick(event: CProps.EventMouse): void {
|
||||
function handleClick(event: React.MouseEvent<Element>): void {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (disabled || !onChange) {
|
||||
|
@ -49,14 +49,14 @@ export function Checkbox({
|
|||
type='button'
|
||||
className={clsx(
|
||||
'flex items-center gap-2', //
|
||||
'outline-none',
|
||||
'outline-hidden',
|
||||
'focus-frame',
|
||||
cursor,
|
||||
className
|
||||
)}
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globals.tooltip : undefined}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
|
||||
data-tooltip-html={titleHtml}
|
||||
data-tooltip-content={title}
|
||||
data-tooltip-hidden={hideTitle}
|
||||
|
@ -66,7 +66,7 @@ export function Checkbox({
|
|||
className={clsx(
|
||||
'max-w-[1rem] min-w-[1rem] h-4', //
|
||||
'pt-[0.05rem] pl-[0.05rem]',
|
||||
'border rounded-sm',
|
||||
'border rounded-xs',
|
||||
'cc-animate-color',
|
||||
{
|
||||
'bg-sec-600 text-sec-0': value !== false,
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { globals } from '@/utils/constants';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
|
||||
import { CheckboxChecked, CheckboxNull } from '../Icons';
|
||||
import { CProps } from '../props';
|
||||
|
||||
import { CheckboxProps } from './Checkbox';
|
||||
import { type CheckboxProps } from './Checkbox';
|
||||
|
||||
export interface CheckboxTristateProps extends Omit<CheckboxProps, 'value' | 'onChange'> {
|
||||
/** Current value - `null`, `true` or `false`. */
|
||||
|
@ -31,7 +30,7 @@ export function CheckboxTristate({
|
|||
}: CheckboxTristateProps) {
|
||||
const cursor = disabled ? 'cursor-arrow' : onChange ? 'cursor-pointer' : '';
|
||||
|
||||
function handleClick(event: CProps.EventMouse): void {
|
||||
function handleClick(event: React.MouseEvent<Element>): void {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (disabled || !onChange) {
|
||||
|
@ -51,14 +50,14 @@ export function CheckboxTristate({
|
|||
type='button'
|
||||
className={clsx(
|
||||
'flex items-center gap-2', //
|
||||
'outline-none',
|
||||
'outline-hidden',
|
||||
'focus-frame',
|
||||
cursor,
|
||||
className
|
||||
)}
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globals.tooltip : undefined}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
|
||||
data-tooltip-html={titleHtml}
|
||||
data-tooltip-content={title}
|
||||
data-tooltip-hidden={hideTitle}
|
||||
|
@ -68,7 +67,7 @@ export function CheckboxTristate({
|
|||
className={clsx(
|
||||
'w-4 h-4', //
|
||||
'pt-[0.05rem] pl-[0.05rem]',
|
||||
'border rounded-sm',
|
||||
'border rounded-xs',
|
||||
'cc-animate-color',
|
||||
{
|
||||
'bg-sec-600 text-sec-0': value !== false,
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { FieldError, GlobalError } from 'react-hook-form';
|
||||
import { type FieldError, type GlobalError } from 'react-hook-form';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { CProps } from '../props';
|
||||
import { type Styling } from '../props';
|
||||
|
||||
interface ErrorFieldProps extends CProps.Styling {
|
||||
interface ErrorFieldProps extends Styling {
|
||||
error?: FieldError | GlobalError;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@ import clsx from 'clsx';
|
|||
|
||||
import { Button } from '../Control';
|
||||
import { IconUpload } from '../Icons';
|
||||
import { CProps } from '../props';
|
||||
import { type Titled } from '../props';
|
||||
|
||||
import { Label } from './Label';
|
||||
|
||||
interface FileInputProps extends Omit<CProps.Input, 'accept' | 'type'> {
|
||||
interface FileInputProps extends Titled, Omit<React.ComponentProps<'input'>, 'accept' | 'type'> {
|
||||
/** Label to display in file upload button. */
|
||||
label: string;
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { CProps } from '../props';
|
||||
|
||||
interface LabelProps extends CProps.Label {
|
||||
interface LabelProps extends Omit<React.ComponentProps<'label'>, 'children'> {
|
||||
/** Text to display. */
|
||||
text?: string;
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@ import clsx from 'clsx';
|
|||
|
||||
import { Overlay } from '@/components/Container';
|
||||
import { IconSearch } from '@/components/Icons';
|
||||
import { CProps } from '@/components/props';
|
||||
import { type Styling } from '@/components/props';
|
||||
|
||||
import { TextInput } from './TextInput';
|
||||
|
||||
interface SearchBarProps extends CProps.Styling {
|
||||
interface SearchBarProps extends Styling {
|
||||
/** Id of the search bar. */
|
||||
id?: string;
|
||||
|
||||
|
@ -48,12 +48,13 @@ export function SearchBar({
|
|||
<TextInput
|
||||
id={id}
|
||||
noOutline
|
||||
transparent
|
||||
placeholder={placeholder}
|
||||
type='search'
|
||||
className={clsx('outline-none bg-transparent', !noIcon && 'pl-10')}
|
||||
className={clsx('bg-transparent', !noIcon && 'pl-10')}
|
||||
noBorder={noBorder}
|
||||
value={query}
|
||||
onChange={event => (onChangeQuery ? onChangeQuery(event.target.value) : undefined)}
|
||||
onChange={event => onChangeQuery?.(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
'use client';
|
||||
|
||||
import Select, {
|
||||
ClearIndicatorProps,
|
||||
type ClearIndicatorProps,
|
||||
components,
|
||||
DropdownIndicatorProps,
|
||||
GroupBase,
|
||||
Props,
|
||||
StylesConfig
|
||||
type DropdownIndicatorProps,
|
||||
type GroupBase,
|
||||
type Props,
|
||||
type StylesConfig
|
||||
} from 'react-select';
|
||||
|
||||
import useWindowSize from '@/hooks/useWindowSize';
|
||||
import { useWindowSize } from '@/hooks/useWindowSize';
|
||||
import { APP_COLORS, SELECT_THEME } from '@/styling/colors';
|
||||
|
||||
import { IconClose, IconDropArrow, IconDropArrowUp } from '../Icons';
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
'use client';
|
||||
|
||||
import Select, {
|
||||
ClearIndicatorProps,
|
||||
type ClearIndicatorProps,
|
||||
components,
|
||||
DropdownIndicatorProps,
|
||||
GroupBase,
|
||||
Props,
|
||||
StylesConfig
|
||||
type DropdownIndicatorProps,
|
||||
type GroupBase,
|
||||
type Props,
|
||||
type StylesConfig
|
||||
} from 'react-select';
|
||||
|
||||
import useWindowSize from '@/hooks/useWindowSize';
|
||||
import { useWindowSize } from '@/hooks/useWindowSize';
|
||||
import { APP_COLORS, SELECT_THEME } from '@/styling/colors';
|
||||
|
||||
import { IconClose, IconDropArrow, IconDropArrowUp } from '../Icons';
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { globals, PARAMETER } from '@/utils/constants';
|
||||
import { globalIDs, PARAMETER } from '@/utils/constants';
|
||||
|
||||
import { Overlay } from '../Container';
|
||||
import { MiniButton } from '../Control';
|
||||
import { IconDropArrow, IconPageRight } from '../Icons';
|
||||
import { CProps } from '../props';
|
||||
import { type Styling } from '../props';
|
||||
|
||||
interface SelectTreeProps<ItemType> extends CProps.Styling {
|
||||
interface SelectTreeProps<ItemType> extends Styling {
|
||||
/** Current value. */
|
||||
value: ItemType;
|
||||
|
||||
|
@ -66,13 +66,13 @@ export function SelectTree<ItemType>({
|
|||
);
|
||||
}
|
||||
|
||||
function handleClickFold(event: CProps.EventMouse, target: ItemType, showChildren: boolean) {
|
||||
function handleClickFold(event: React.MouseEvent<Element>, target: ItemType, showChildren: boolean) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onFoldItem(target, showChildren);
|
||||
}
|
||||
|
||||
function handleSetValue(event: CProps.EventMouse, target: ItemType) {
|
||||
function handleSetValue(event: React.MouseEvent<Element>, target: ItemType) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onChange(target);
|
||||
|
@ -93,7 +93,7 @@ export function SelectTree<ItemType>({
|
|||
value === item && 'clr-selected',
|
||||
!isActive && 'pointer-events-none'
|
||||
)}
|
||||
data-tooltip-id={globals.tooltip}
|
||||
data-tooltip-id={globalIDs.tooltip}
|
||||
data-tooltip-html={getDescription(item)}
|
||||
onClick={event => handleSetValue(event, item)}
|
||||
style={{
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { Label } from '../Input/Label';
|
||||
import { CProps } from '../props';
|
||||
import { type Editor, type ErrorProcessing, type Titled } from '../props';
|
||||
|
||||
import { ErrorField } from './ErrorField';
|
||||
|
||||
export interface TextAreaProps extends CProps.Editor, CProps.ErrorProcessing, CProps.Colors, CProps.TextArea {
|
||||
export interface TextAreaProps extends Editor, ErrorProcessing, Titled, React.ComponentProps<'textarea'> {
|
||||
/** Indicates that the input should be transparent. */
|
||||
transparent?: boolean;
|
||||
|
||||
/** Indicates that padding should be minimal. */
|
||||
dense?: boolean;
|
||||
|
||||
|
@ -23,6 +26,7 @@ export function TextArea({
|
|||
id,
|
||||
label,
|
||||
required,
|
||||
transparent,
|
||||
rows,
|
||||
dense,
|
||||
noBorder,
|
||||
|
@ -31,7 +35,6 @@ export function TextArea({
|
|||
className,
|
||||
fitContent,
|
||||
error,
|
||||
colors = 'clr-input',
|
||||
...restProps
|
||||
}: TextAreaProps) {
|
||||
return (
|
||||
|
@ -40,7 +43,7 @@ export function TextArea({
|
|||
'w-full',
|
||||
{
|
||||
'flex flex-col': !dense,
|
||||
'flex flex-grow items-center gap-3': dense
|
||||
'flex grow items-center gap-3': dense
|
||||
},
|
||||
dense && className
|
||||
)}
|
||||
|
@ -53,14 +56,15 @@ export function TextArea({
|
|||
'leading-tight',
|
||||
'overflow-x-hidden overflow-y-auto',
|
||||
{
|
||||
'cc-fit-content': fitContent,
|
||||
'field-sizing-content': fitContent,
|
||||
'resize-none': noResize,
|
||||
'border': !noBorder,
|
||||
'flex-grow max-w-full': dense,
|
||||
'grow max-w-full': dense,
|
||||
'mt-2': !dense && !!label,
|
||||
'clr-outline': !noOutline
|
||||
'clr-outline': !noOutline,
|
||||
'bg-transparent': transparent,
|
||||
'clr-input': !transparent
|
||||
},
|
||||
colors,
|
||||
!dense && className
|
||||
)}
|
||||
rows={rows}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { Label } from '../Input/Label';
|
||||
import { CProps } from '../props';
|
||||
import { type Editor, type ErrorProcessing, type Titled } from '../props';
|
||||
|
||||
import { ErrorField } from './ErrorField';
|
||||
|
||||
interface TextInputProps extends CProps.Editor, CProps.ErrorProcessing, CProps.Colors, CProps.Input {
|
||||
interface TextInputProps extends Editor, ErrorProcessing, Titled, React.ComponentProps<'input'> {
|
||||
/** Indicates that the input should be transparent. */
|
||||
transparent?: boolean;
|
||||
|
||||
/** Indicates that padding should be minimal. */
|
||||
dense?: boolean;
|
||||
|
||||
|
@ -30,8 +33,8 @@ export function TextInput({
|
|||
noOutline,
|
||||
allowEnter,
|
||||
disabled,
|
||||
transparent,
|
||||
className,
|
||||
colors = 'clr-input',
|
||||
onKeyDown,
|
||||
error,
|
||||
...restProps
|
||||
|
@ -54,12 +57,13 @@ export function TextInput({
|
|||
'leading-tight truncate hover:text-clip',
|
||||
{
|
||||
'px-3': !noBorder || !disabled,
|
||||
'flex-grow max-w-full': dense,
|
||||
'grow max-w-full': dense,
|
||||
'mt-2': !dense && !!label,
|
||||
'border': !noBorder,
|
||||
'clr-outline': !noOutline
|
||||
'clr-outline': !noOutline,
|
||||
'bg-transparent': transparent,
|
||||
'clr-input': !transparent
|
||||
},
|
||||
colors,
|
||||
!dense && className
|
||||
)}
|
||||
onKeyDown={!allowEnter && !onKeyDown ? preventEnterCapture : onKeyDown}
|
||||
|
|
|
@ -2,20 +2,20 @@
|
|||
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { BadgeHelp, HelpTopic } from '@/features/help';
|
||||
import { BadgeHelp, type HelpTopic } from '@/features/help';
|
||||
|
||||
import useEscapeKey from '@/hooks/useEscapeKey';
|
||||
import { useEscapeKey } from '@/hooks/useEscapeKey';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { prepareTooltip } from '@/utils/utils';
|
||||
|
||||
import { Button, MiniButton, SubmitButton } from '../Control';
|
||||
import { IconClose } from '../Icons';
|
||||
import { CProps } from '../props';
|
||||
import { type Styling } from '../props';
|
||||
|
||||
import { ModalBackdrop } from './ModalBackdrop';
|
||||
|
||||
export interface ModalProps extends CProps.Styling {
|
||||
export interface ModalProps extends Styling {
|
||||
/** Title of the modal window. */
|
||||
header?: string;
|
||||
|
||||
|
@ -115,7 +115,7 @@ export function ModalForm({
|
|||
|
||||
<div
|
||||
className={clsx(
|
||||
'overscroll-contain max-h-[calc(100svh-8rem)] max-w-[100svw] xs:max-w-[calc(100svw-2rem)] outline-none',
|
||||
'overscroll-contain max-h-[calc(100svh-8rem)] max-w-[100svw] xs:max-w-[calc(100svw-2rem)] outline-hidden',
|
||||
{
|
||||
'overflow-auto': !overflowVisible,
|
||||
'overflow-visible': overflowVisible
|
||||
|
|
|
@ -4,7 +4,7 @@ import clsx from 'clsx';
|
|||
|
||||
import { BadgeHelp } from '@/features/help';
|
||||
|
||||
import useEscapeKey from '@/hooks/useEscapeKey';
|
||||
import { useEscapeKey } from '@/hooks/useEscapeKey';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { prepareTooltip } from '@/utils/utils';
|
||||
|
@ -13,7 +13,7 @@ import { Button, MiniButton } from '../Control';
|
|||
import { IconClose } from '../Icons';
|
||||
|
||||
import { ModalBackdrop } from './ModalBackdrop';
|
||||
import { ModalProps } from './ModalForm';
|
||||
import { type ModalProps } from './ModalForm';
|
||||
|
||||
interface ModalViewProps extends ModalProps {}
|
||||
|
||||
|
@ -60,7 +60,7 @@ export function ModalView({
|
|||
|
||||
<div
|
||||
className={clsx(
|
||||
'overscroll-contain max-h-[calc(100svh-8rem)] max-w-[100svw] xs:max-w-[calc(100svw-2rem)] outline-none',
|
||||
'overscroll-contain max-h-[calc(100svh-8rem)] max-w-[100svw] xs:max-w-[calc(100svw-2rem)] outline-hidden',
|
||||
{
|
||||
'overflow-auto': !overflowVisible,
|
||||
'overflow-visible': overflowVisible
|
||||
|
|
|
@ -2,11 +2,11 @@ import type { TabProps as TabPropsImpl } from 'react-tabs';
|
|||
import { Tab as TabImpl } from 'react-tabs';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { globals } from '@/utils/constants';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
|
||||
import { CProps } from '../props';
|
||||
import { type Titled } from '../props';
|
||||
|
||||
interface TabLabelProps extends Omit<TabPropsImpl, 'children'>, CProps.Titled {
|
||||
interface TabLabelProps extends Omit<TabPropsImpl, 'children'>, Titled {
|
||||
/** Label to display in the tab. */
|
||||
label?: string;
|
||||
}
|
||||
|
@ -23,11 +23,11 @@ export function TabLabel({ label, title, titleHtml, hideTitle, className, ...oth
|
|||
'clr-hover cc-animate-color duration-150',
|
||||
'text-sm whitespace-nowrap font-controls',
|
||||
'select-none hover:cursor-pointer',
|
||||
'outline-none',
|
||||
'outline-hidden',
|
||||
className
|
||||
)}
|
||||
tabIndex='-1'
|
||||
data-tooltip-id={!!title || !!titleHtml ? globals.tooltip : undefined}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
|
||||
data-tooltip-html={titleHtml}
|
||||
data-tooltip-content={title}
|
||||
data-tooltip-hidden={hideTitle}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { CProps } from '@/components/props';
|
||||
import { globals } from '@/utils/constants';
|
||||
import { type Styling, type Titled } from '@/components/props';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
|
||||
interface IndicatorProps extends CProps.Titled, CProps.Styling {
|
||||
interface IndicatorProps extends Titled, Styling {
|
||||
/** Icon to display. */
|
||||
icon: React.ReactNode;
|
||||
|
||||
|
@ -19,13 +19,13 @@ export function Indicator({ icon, title, titleHtml, hideTitle, noPadding, classN
|
|||
<div
|
||||
className={clsx(
|
||||
'clr-text-controls',
|
||||
'outline-none',
|
||||
'outline-hidden',
|
||||
{
|
||||
'px-1 py-1': !noPadding
|
||||
},
|
||||
className
|
||||
)}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globals.tooltip : undefined}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
|
||||
data-tooltip-html={titleHtml}
|
||||
data-tooltip-content={title}
|
||||
data-tooltip-hidden={hideTitle}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { CProps } from '@/components/props';
|
||||
|
||||
/**
|
||||
* Wraps content in a div with a centered text.
|
||||
*/
|
||||
export function NoData({ className, children, ...restProps }: CProps.Div) {
|
||||
export function NoData({ className, children, ...restProps }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div className={clsx('p-3 flex flex-col items-center text-center select-none w-full', className)} {...restProps}>
|
||||
{children}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import useWindowSize from '@/hooks/useWindowSize';
|
||||
import { useWindowSize } from '@/hooks/useWindowSize';
|
||||
import { useFitHeight } from '@/stores/appLayout';
|
||||
|
||||
/** Maximum width of the viewer. */
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { CProps } from '@/components/props';
|
||||
import { globals } from '@/utils/constants';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
import { truncateToLastWord } from '@/utils/utils';
|
||||
|
||||
export interface TextContentProps extends CProps.Styling {
|
||||
import { type Styling } from '../props';
|
||||
|
||||
export interface TextContentProps extends Styling {
|
||||
/** Text to display. */
|
||||
text: string;
|
||||
|
||||
|
@ -24,7 +25,7 @@ export function TextContent({ className, text, maxLength, noTooltip, ...restProp
|
|||
return (
|
||||
<div
|
||||
className={clsx('text-xs text-pretty', className)}
|
||||
data-tooltip-id={isTruncated && !noTooltip ? globals.value_tooltip : undefined}
|
||||
data-tooltip-id={isTruncated && !noTooltip ? globalIDs.value_tooltip : undefined}
|
||||
data-tooltip-html={isTruncated && !noTooltip ? text : undefined}
|
||||
{...restProps}
|
||||
>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { CProps } from '@/components/props';
|
||||
import { globals } from '@/utils/constants';
|
||||
import { globalIDs } from '@/utils/constants';
|
||||
|
||||
import { MiniButton } from '../Control';
|
||||
import { type Styling, type Titled } from '../props';
|
||||
|
||||
interface ValueIconProps extends CProps.Styling, CProps.Titled {
|
||||
interface ValueIconProps extends Styling, Titled {
|
||||
/** Id of the component. */
|
||||
id?: string;
|
||||
|
||||
|
@ -19,7 +19,7 @@ interface ValueIconProps extends CProps.Styling, CProps.Titled {
|
|||
textClassName?: string;
|
||||
|
||||
/** Callback to be called when the component is clicked. */
|
||||
onClick?: (event: CProps.EventMouse) => void;
|
||||
onClick?: (event: React.MouseEvent<Element>) => void;
|
||||
|
||||
/** Number of symbols to display in a small size. */
|
||||
smallThreshold?: number;
|
||||
|
@ -61,7 +61,7 @@ export function ValueIcon({
|
|||
className
|
||||
)}
|
||||
{...restProps}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globals.tooltip : undefined}
|
||||
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
|
||||
data-tooltip-html={titleHtml}
|
||||
data-tooltip-content={title}
|
||||
data-tooltip-hidden={hideTitle}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { CProps } from '@/components/props';
|
||||
import { type Styling } from '@/components/props';
|
||||
|
||||
interface ValueLabeledProps extends CProps.Styling {
|
||||
interface ValueLabeledProps extends Styling {
|
||||
/** Id of the component. */
|
||||
id?: string;
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { CProps } from '@/components/props';
|
||||
import { type Styling, type Titled } from '@/components/props';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
import { ValueIcon } from './ValueIcon';
|
||||
|
||||
interface ValueStatsProps extends CProps.Styling, CProps.Titled {
|
||||
interface ValueStatsProps extends Styling, Titled {
|
||||
/** Id of the component. */
|
||||
id: string;
|
||||
|
||||
|
|
73
rsconcept/frontend/src/components/props.d.ts
vendored
73
rsconcept/frontend/src/components/props.d.ts
vendored
|
@ -1,31 +1,22 @@
|
|||
// =========== Module contains interfaces for common UI elements. ==========
|
||||
import React from 'react';
|
||||
import { FieldError } from 'react-hook-form';
|
||||
import type React from 'react';
|
||||
import { type FieldError } from 'react-hook-form';
|
||||
|
||||
export namespace CProps {
|
||||
/**
|
||||
/**
|
||||
* Represents an object that can have inline styles and CSS class names for styling.
|
||||
*/
|
||||
export interface Styling {
|
||||
export interface Styling {
|
||||
/** Optional inline styles for the component. */
|
||||
style?: React.CSSProperties;
|
||||
|
||||
/** Optional CSS class name(s) for the component. */
|
||||
className?: string;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an object that can have a color or set of colors.
|
||||
*/
|
||||
export interface Colors {
|
||||
/** Optional color or set of colors applied via classNames. */
|
||||
colors?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Represents an object that can have a title with optional HTML rendering and a flag to hide the title.
|
||||
*/
|
||||
export interface Titled {
|
||||
export interface Titled {
|
||||
/** Tooltip: `plain text`. */
|
||||
title?: string;
|
||||
|
||||
|
@ -34,22 +25,22 @@ export namespace CProps {
|
|||
|
||||
/** Indicates whether the `title` should be hidden. */
|
||||
hideTitle?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Represents an object that can have an error message.
|
||||
*/
|
||||
export interface ErrorProcessing {
|
||||
export interface ErrorProcessing {
|
||||
error?: FieldError;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Represents `control` component with optional title and configuration options.
|
||||
*
|
||||
* @remarks
|
||||
* This type extends the {@link Titled} interface, adding properties to control the visual and interactive behavior of a component.
|
||||
*/
|
||||
export type Control = Titled & {
|
||||
export type Control = Titled & {
|
||||
/** Indicates whether the control is disabled. */
|
||||
disabled?: boolean;
|
||||
|
||||
|
@ -58,46 +49,20 @@ export namespace CProps {
|
|||
|
||||
/** Indicates whether the control should render without an outline. */
|
||||
noOutline?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* Represents `editor` component that includes a label, control features, and optional title properties.
|
||||
*
|
||||
* @remarks
|
||||
* This type extends the {@link Control} type, inheriting title-related properties and additional configuration options, while also adding an optional label.
|
||||
*/
|
||||
export type Editor = Control & {
|
||||
export type Editor = Control & {
|
||||
/** Text label. */
|
||||
label?: string;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents `div` component with all standard HTML attributes and React-specific properties.
|
||||
*/
|
||||
export type Div = React.ComponentProps<'div'>;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Represents `button` component with optional title and HTML attributes.
|
||||
*/
|
||||
export type Button = Titled & Omit<React.ComponentProps<'button'>, 'children' | 'type'>;
|
||||
|
||||
/**
|
||||
* Represents `label` component with HTML attributes.
|
||||
*/
|
||||
export type Label = Omit<React.ComponentProps<'label'>, 'children'>;
|
||||
|
||||
/**
|
||||
* Represents `textarea` component with optional title and HTML attributes.
|
||||
*/
|
||||
export type TextArea = Titled & React.ComponentProps<'textarea'>;
|
||||
|
||||
/**
|
||||
* Represents `input` component with optional title and HTML attributes.
|
||||
*/
|
||||
export type Input = Titled & React.ComponentProps<'input'>;
|
||||
|
||||
/**
|
||||
* Represents `mouse event` in React.
|
||||
*/
|
||||
export type EventMouse = React.MouseEvent<Element, MouseEvent>;
|
||||
}
|
||||
export type Button = Titled & Omit<React.ComponentProps<'button'>, 'children' | 'type'>;
|
||||
|
|
|
@ -5,12 +5,12 @@ import { DELAYS, KEYS } from '@/backend/configuration';
|
|||
import { infoMsg } from '@/utils/labels';
|
||||
|
||||
import {
|
||||
IChangePasswordDTO,
|
||||
ICurrentUser,
|
||||
IPasswordTokenDTO,
|
||||
IRequestPasswordDTO,
|
||||
IResetPasswordDTO,
|
||||
IUserLoginDTO
|
||||
type IChangePasswordDTO,
|
||||
type ICurrentUser,
|
||||
type IPasswordTokenDTO,
|
||||
type IRequestPasswordDTO,
|
||||
type IResetPasswordDTO,
|
||||
type IUserLoginDTO
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||
import { KEYS } from '@/backend/configuration';
|
||||
|
||||
import { authApi } from './api';
|
||||
import { IChangePasswordDTO } from './types';
|
||||
import { type IChangePasswordDTO } from './types';
|
||||
|
||||
export const useChangePassword = () => {
|
||||
const client = useQueryClient();
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||
import { KEYS } from '@/backend/configuration';
|
||||
|
||||
import { authApi } from './api';
|
||||
import { IUserLoginDTO } from './types';
|
||||
import { type IUserLoginDTO } from './types';
|
||||
|
||||
export const useLogin = () => {
|
||||
const client = useQueryClient();
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useMutation } from '@tanstack/react-query';
|
|||
import { KEYS } from '@/backend/configuration';
|
||||
|
||||
import { authApi } from './api';
|
||||
import { IRequestPasswordDTO } from './types';
|
||||
import { type IRequestPasswordDTO } from './types';
|
||||
|
||||
export const useRequestPasswordReset = () => {
|
||||
const mutation = useMutation({
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useMutation } from '@tanstack/react-query';
|
|||
import { KEYS } from '@/backend/configuration';
|
||||
|
||||
import { authApi } from './api';
|
||||
import { IPasswordTokenDTO, IResetPasswordDTO } from './types';
|
||||
import { type IPasswordTokenDTO, type IResetPasswordDTO } from './types';
|
||||
|
||||
export const useResetPassword = () => {
|
||||
const validateMutation = useMutation({
|
||||
|
|
|
@ -8,17 +8,17 @@ import { urls, useConceptNavigation } from '@/app';
|
|||
|
||||
import { isAxiosError } from '@/backend/apiTransport';
|
||||
import { SubmitButton, TextURL } from '@/components/Control';
|
||||
import { ErrorData } from '@/components/InfoError';
|
||||
import { type ErrorData } from '@/components/InfoError';
|
||||
import { TextInput } from '@/components/Input';
|
||||
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||
import { useQueryStrings } from '@/hooks/useQueryStrings';
|
||||
import { resources } from '@/utils/constants';
|
||||
|
||||
import { IUserLoginDTO, schemaUserLogin } from '../backend/types';
|
||||
import { type IUserLoginDTO, schemaUserLogin } from '../backend/types';
|
||||
import { useAuthSuspense } from '../backend/useAuth';
|
||||
import { useLogin } from '../backend/useLogin';
|
||||
import { ExpectedAnonymous } from '../components/ExpectedAnonymous';
|
||||
|
||||
function LoginPage() {
|
||||
export function LoginPage() {
|
||||
const router = useConceptNavigation();
|
||||
const query = useQueryStrings();
|
||||
const initialName = query.get('username') ?? '';
|
||||
|
@ -92,8 +92,6 @@ function LoginPage() {
|
|||
);
|
||||
}
|
||||
|
||||
export default LoginPage;
|
||||
|
||||
// ====== Internals =========
|
||||
function ServerError({ error }: { error: ErrorData }): React.ReactElement | null {
|
||||
if (isAxiosError(error) && error.response && error.response.status === 400) {
|
||||
|
|
|
@ -7,10 +7,10 @@ import { urls, useConceptNavigation } from '@/app';
|
|||
|
||||
import { isAxiosError } from '@/backend/apiTransport';
|
||||
import { SubmitButton } from '@/components/Control';
|
||||
import { ErrorData, InfoError } from '@/components/InfoError';
|
||||
import { type ErrorData, InfoError } from '@/components/InfoError';
|
||||
import { TextInput } from '@/components/Input';
|
||||
import { Loader } from '@/components/Loader';
|
||||
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||
import { useQueryStrings } from '@/hooks/useQueryStrings';
|
||||
|
||||
import { useResetPassword } from '../backend/useResetPassword';
|
||||
|
||||
|
@ -24,9 +24,6 @@ export function Component() {
|
|||
const [newPassword, setNewPassword] = useState('');
|
||||
const [newPasswordRepeat, setNewPasswordRepeat] = useState('');
|
||||
|
||||
const passwordColor =
|
||||
!!newPassword && !!newPasswordRepeat && newPassword !== newPasswordRepeat ? 'bg-warn-100' : 'clr-input';
|
||||
|
||||
const canSubmit = !!newPassword && !!newPasswordRepeat && newPassword === newPasswordRepeat;
|
||||
|
||||
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||
|
@ -61,7 +58,6 @@ export function Component() {
|
|||
label='Новый пароль'
|
||||
autoComplete='new-password'
|
||||
allowEnter
|
||||
colors={passwordColor}
|
||||
value={newPassword}
|
||||
onChange={event => {
|
||||
setNewPassword(event.target.value);
|
||||
|
@ -73,7 +69,6 @@ export function Component() {
|
|||
label='Повторите новый'
|
||||
autoComplete='new-password'
|
||||
allowEnter
|
||||
colors={passwordColor}
|
||||
value={newPasswordRepeat}
|
||||
onChange={event => {
|
||||
setNewPasswordRepeat(event.target.value);
|
||||
|
|
|
@ -5,7 +5,7 @@ import clsx from 'clsx';
|
|||
|
||||
import { isAxiosError } from '@/backend/apiTransport';
|
||||
import { SubmitButton, TextURL } from '@/components/Control';
|
||||
import { ErrorData } from '@/components/InfoError';
|
||||
import { type ErrorData } from '@/components/InfoError';
|
||||
import { TextInput } from '@/components/Input';
|
||||
|
||||
import { useRequestPasswordReset } from '../backend/useRequestPasswordReset';
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
import React, { Suspense } from 'react';
|
||||
|
||||
import { PlacesType, Tooltip } from '@/components/Container';
|
||||
import { type PlacesType, Tooltip } from '@/components/Container';
|
||||
import { TextURL } from '@/components/Control';
|
||||
import { IconHelp } from '@/components/Icons';
|
||||
import { Loader } from '@/components/Loader';
|
||||
import { CProps } from '@/components/props';
|
||||
import { type Styling } from '@/components/props';
|
||||
import { usePreferencesStore } from '@/stores/preferences';
|
||||
|
||||
import { HelpTopic } from '../models/helpTopic';
|
||||
import { type HelpTopic } from '../models/helpTopic';
|
||||
|
||||
const TopicPage = React.lazy(() => import('@/features/help/pages/ManualsPage/TopicPage'));
|
||||
const TopicPage = React.lazy(() =>
|
||||
import('@/features/help/pages/ManualsPage/TopicPage').then(module => ({ default: module.TopicPage }))
|
||||
);
|
||||
|
||||
interface BadgeHelpProps extends CProps.Styling {
|
||||
interface BadgeHelpProps extends Styling {
|
||||
/** Topic to display in a tooltip. */
|
||||
topic: HelpTopic;
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ interface InfoCstClassProps {
|
|||
header?: string;
|
||||
}
|
||||
|
||||
function InfoCstClass({ header }: InfoCstClassProps) {
|
||||
export function InfoCstClass({ header }: InfoCstClassProps) {
|
||||
return (
|
||||
<div className='flex flex-col gap-1 mb-2 dense'>
|
||||
{header ? <h1>{header}</h1> : null}
|
||||
|
@ -31,5 +31,3 @@ function InfoCstClass({ header }: InfoCstClassProps) {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default InfoCstClass;
|
||||
|
|
|
@ -10,7 +10,7 @@ interface InfoCstStatusProps {
|
|||
title?: string;
|
||||
}
|
||||
|
||||
function InfoCstStatus({ title }: InfoCstStatusProps) {
|
||||
export function InfoCstStatus({ title }: InfoCstStatusProps) {
|
||||
return (
|
||||
<div className='flex flex-col gap-1 mb-2 dense'>
|
||||
{title ? <h1>{title}</h1> : null}
|
||||
|
@ -37,5 +37,3 @@ function InfoCstStatus({ title }: InfoCstStatusProps) {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default InfoCstStatus;
|
||||
|
|
|
@ -2,7 +2,7 @@ import { urls } from '@/app';
|
|||
|
||||
import { TextURL } from '@/components/Control';
|
||||
|
||||
import { HelpTopic } from '../models/helpTopic';
|
||||
import { type HelpTopic } from '../models/helpTopic';
|
||||
|
||||
interface TextURLProps {
|
||||
/** Text to display. */
|
||||
|
|
|
@ -2,7 +2,7 @@ import { removeTags } from '@/utils/utils';
|
|||
|
||||
import { LinkTopic } from '../components/LinkTopic';
|
||||
import { describeHelpTopic, labelHelpTopic } from '../labels';
|
||||
import { HelpTopic } from '../models/helpTopic';
|
||||
import { type HelpTopic } from '../models/helpTopic';
|
||||
|
||||
interface TopicItemProps {
|
||||
topic: HelpTopic;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { IconHide, IconImmutable, IconPrivate, IconProtected, IconPublic } from '@/components/Icons';
|
||||
|
||||
function HelpAccess() {
|
||||
export function HelpAccess() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Организация доступов</h1>
|
||||
|
@ -29,5 +29,3 @@ function HelpAccess() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpAccess;
|
||||
|
|
|
@ -4,7 +4,7 @@ import { external_urls } from '@/utils/constants';
|
|||
import { Subtopics } from '../components/Subtopics';
|
||||
import { HelpTopic } from '../models/helpTopic';
|
||||
|
||||
function HelpConceptSystem() {
|
||||
export function HelpConcept() {
|
||||
return (
|
||||
<div className='text-justify'>
|
||||
<h1>Концептуализация</h1>
|
||||
|
@ -43,5 +43,3 @@ function HelpConceptSystem() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpConceptSystem;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { TextURL } from '@/components/Control';
|
||||
import { external_urls, PARAMETER } from '@/utils/constants';
|
||||
|
||||
function HelpExteor() {
|
||||
export function HelpExteor() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Экстеор</h1>
|
||||
|
@ -38,5 +38,3 @@ function HelpExteor() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpExteor;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Subtopics } from '../components/Subtopics';
|
||||
import { HelpTopic } from '../models/helpTopic';
|
||||
|
||||
function HelpInfo() {
|
||||
export function HelpInfo() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Справочная информация и документы</h1>
|
||||
|
@ -14,5 +14,3 @@ function HelpInfo() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpInfo;
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
import { Subtopics } from '../components/Subtopics';
|
||||
import { HelpTopic } from '../models/helpTopic';
|
||||
|
||||
function HelpInterface() {
|
||||
export function HelpInterface() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Пользовательский интерфейс</h1>
|
||||
|
@ -60,5 +60,3 @@ function HelpInterface() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpInterface;
|
||||
|
|
|
@ -5,7 +5,7 @@ import { LinkTopic } from '../components/LinkTopic';
|
|||
import { TopicItem } from '../components/TopicItem';
|
||||
import { HelpTopic } from '../models/helpTopic';
|
||||
|
||||
function HelpMain() {
|
||||
export function HelpMain() {
|
||||
return (
|
||||
<div className='text-justify'>
|
||||
<h1>Портал</h1>
|
||||
|
@ -66,5 +66,3 @@ function HelpMain() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpMain;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { EmbedYoutube } from '@/components/View';
|
||||
import useWindowSize from '@/hooks/useWindowSize';
|
||||
import { useWindowSize } from '@/hooks/useWindowSize';
|
||||
import { external_urls, youtube } from '@/utils/constants';
|
||||
|
||||
import { Subtopics } from '../components/Subtopics';
|
||||
import { HelpTopic } from '../models/helpTopic';
|
||||
|
||||
function HelpRSLang() {
|
||||
export function HelpRSLang() {
|
||||
const windowSize = useWindowSize();
|
||||
|
||||
const videoHeight = (() => {
|
||||
|
@ -35,5 +35,3 @@ function HelpRSLang() {
|
|||
</div>
|
||||
</div>);
|
||||
}
|
||||
|
||||
export default HelpRSLang;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
function HelpTerminologyControl() {
|
||||
export function HelpTerminologyControl() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Терминологизация</h1>
|
||||
|
@ -24,5 +24,3 @@ function HelpTerminologyControl() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpTerminologyControl;
|
||||
|
|
|
@ -30,7 +30,7 @@ import {
|
|||
import { LinkTopic } from '../components/LinkTopic';
|
||||
import { HelpTopic } from '../models/helpTopic';
|
||||
|
||||
function HelpThesaurus() {
|
||||
export function HelpThesaurus() {
|
||||
return (
|
||||
<div className='text-justify'>
|
||||
<h1>Тезаурус</h1>
|
||||
|
@ -281,5 +281,3 @@ function HelpThesaurus() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpThesaurus;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { IconEditor, IconNewVersion, IconShare, IconUpload, IconVersions } from '@/components/Icons';
|
||||
|
||||
function HelpVersions() {
|
||||
export function HelpVersions() {
|
||||
return (
|
||||
<div className=''>
|
||||
<h1>Версионирование схем</h1>
|
||||
|
@ -28,5 +28,3 @@ function HelpVersions() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpVersions;
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
import { LinkTopic } from '../../components/LinkTopic';
|
||||
import { HelpTopic } from '../../models/helpTopic';
|
||||
|
||||
function HelpConceptOSS() {
|
||||
export function HelpConceptOSS() {
|
||||
return (
|
||||
<div className='text-justify'>
|
||||
<h1>Операционная схема синтеза</h1>
|
||||
|
@ -70,5 +70,3 @@ function HelpConceptOSS() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpConceptOSS;
|
||||
|
|
|
@ -3,7 +3,7 @@ import { IconPredecessor } from '@/components/Icons';
|
|||
import { LinkTopic } from '../../components/LinkTopic';
|
||||
import { HelpTopic } from '../../models/helpTopic';
|
||||
|
||||
function HelpConceptPropagation() {
|
||||
export function HelpConceptPropagation() {
|
||||
return (
|
||||
<div className='text-justify'>
|
||||
<h1>Сквозные изменения</h1>
|
||||
|
@ -44,5 +44,3 @@ function HelpConceptPropagation() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpConceptPropagation;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { LinkTopic } from '../../components/LinkTopic';
|
||||
import { HelpTopic } from '../../models/helpTopic';
|
||||
|
||||
function HelpConceptRelations() {
|
||||
export function HelpConceptRelations() {
|
||||
return (
|
||||
<div className='text-justify'>
|
||||
<h1>Связи между конституентами</h1>
|
||||
|
@ -38,5 +38,3 @@ function HelpConceptRelations() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpConceptRelations;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { LinkTopic } from '../../components/LinkTopic';
|
||||
import { HelpTopic } from '../../models/helpTopic';
|
||||
|
||||
function HelpConceptSynthesis() {
|
||||
export function HelpConceptSynthesis() {
|
||||
return (
|
||||
<div className='text-justify'>
|
||||
<h1>Синтез концептуальных схем</h1>
|
||||
|
@ -46,5 +46,3 @@ function HelpConceptSynthesis() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpConceptSynthesis;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { LinkTopic } from '../../components/LinkTopic';
|
||||
import { HelpTopic } from '../../models/helpTopic';
|
||||
|
||||
function HelpConceptSystem() {
|
||||
export function HelpConceptSystem() {
|
||||
return (
|
||||
<div className='text-justify'>
|
||||
<h1>Концептуальная схема – Система определений</h1>
|
||||
|
@ -61,5 +61,3 @@ function HelpConceptSystem() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HelpConceptSystem;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user