Major UI refactoring: change Select component + colors

This commit is contained in:
IRBorisov 2023-09-08 02:15:20 +03:00
parent 5b006f54dd
commit cc81cc4b6b
13 changed files with 385 additions and 250 deletions

View File

@ -20,7 +20,7 @@ This readme file is used mostly to document project dependencies
- react-tabs
- react-intl
- react-data-table-component
- react-dropdown-select
- react-select
- react-error-boundary
- reagraph
- react-tooltip

View File

@ -16,11 +16,11 @@
"react": "^18.2.0",
"react-data-table-component": "^7.5.4",
"react-dom": "^18.2.0",
"react-dropdown-select": "^4.10.0",
"react-error-boundary": "^4.0.11",
"react-intl": "^6.4.4",
"react-loader-spinner": "^5.4.5",
"react-router-dom": "^6.15.0",
"react-select": "^5.7.4",
"react-tabs": "^6.0.2",
"react-toastify": "^9.1.3",
"react-tooltip": "^5.21.3",
@ -2240,28 +2240,6 @@
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz",
"integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA=="
},
"node_modules/@emotion/styled": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz",
"integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==",
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.11.0",
"@emotion/is-prop-valid": "^1.2.1",
"@emotion/serialize": "^1.1.2",
"@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
"@emotion/utils": "^1.2.1"
},
"peerDependencies": {
"@emotion/react": "^11.0.0-rc.0",
"react": ">=16.8.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@emotion/stylis": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
@ -4111,6 +4089,14 @@
"@types/react": "*"
}
},
"node_modules/@types/react-transition-group": {
"version": "4.4.6",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz",
"integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/scheduler": {
"version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
@ -5565,6 +5551,15 @@
"node": ">=6.0.0"
}
},
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"node_modules/draco3d": {
"version": "1.5.6",
"resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.6.tgz",
@ -8572,6 +8567,11 @@
"tmpl": "1.0.5"
}
},
"node_modules/memoize-one": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
},
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@ -9342,20 +9342,6 @@
"react": "^18.2.0"
}
},
"node_modules/react-dropdown-select": {
"version": "4.10.0",
"resolved": "https://registry.npmjs.org/react-dropdown-select/-/react-dropdown-select-4.10.0.tgz",
"integrity": "sha512-GiUeZZqN8Z/PQFWyihFeuLwlUQ2IJxiu4ep8Y6bonF8ynUk8dAdLgjg3O4YIa3gtS8/Paeli8AtH46AcaPzWVg==",
"dependencies": {
"@emotion/react": "11.11.1",
"@emotion/styled": "11.11.0"
},
"peerDependencies": {
"prop-types": ">=15.7.2",
"react": ">=16.x",
"react-dom": ">=16.x"
}
},
"node_modules/react-error-boundary": {
"version": "4.0.11",
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.11.tgz",
@ -9517,6 +9503,26 @@
"react-dom": ">=16.8"
}
},
"node_modules/react-select": {
"version": "5.7.4",
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.7.4.tgz",
"integrity": "sha512-NhuE56X+p9QDFh4BgeygHFIvJJszO1i1KSkg/JPcIJrbovyRtI+GuOEa4XzFCEpZRAEoEI8u/cAHK+jG/PgUzQ==",
"dependencies": {
"@babel/runtime": "^7.12.0",
"@emotion/cache": "^11.4.0",
"@emotion/react": "^11.8.1",
"@floating-ui/dom": "^1.0.1",
"@types/react-transition-group": "^4.4.0",
"memoize-one": "^6.0.0",
"prop-types": "^15.6.0",
"react-transition-group": "^4.3.0",
"use-isomorphic-layout-effect": "^1.1.2"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-tabs": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-6.0.2.tgz",
@ -9562,6 +9568,21 @@
"react-dom": ">=16.14.0"
}
},
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
},
"peerDependencies": {
"react": ">=16.6.0",
"react-dom": ">=16.6.0"
}
},
"node_modules/react-use-gesture": {
"version": "9.1.3",
"resolved": "https://registry.npmjs.org/react-use-gesture/-/react-use-gesture-9.1.3.tgz",
@ -10576,6 +10597,19 @@
"punycode": "^2.1.0"
}
},
"node_modules/use-isomorphic-layout-effect": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
"integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",

View File

@ -20,11 +20,11 @@
"react": "^18.2.0",
"react-data-table-component": "^7.5.4",
"react-dom": "^18.2.0",
"react-dropdown-select": "^4.10.0",
"react-error-boundary": "^4.0.11",
"react-intl": "^6.4.4",
"react-loader-spinner": "^5.4.5",
"react-router-dom": "^6.15.0",
"react-select": "^5.7.4",
"react-tabs": "^6.0.2",
"react-toastify": "^9.1.3",
"react-tooltip": "^5.21.3",

View File

@ -1,19 +0,0 @@
import { PropsWithRef } from 'react';
import Select, { SelectProps } from 'react-dropdown-select';
interface ConceptSelectProps<T>
extends Omit<PropsWithRef<SelectProps<T>>, 'noDataLabel'> {
}
function ConceptSelect<T extends object | string>({ className, ...props }: ConceptSelectProps<T>) {
return (
<Select
className={`overflow-x-ellipsis whitespace-nowrap clr-border clr-input outline-none ${className}`}
{...props}
noDataLabel='Список пуст'
/>
);
}
export default ConceptSelect;

View File

@ -0,0 +1,66 @@
import { useMemo } from 'react';
import Select, { GroupBase, Props, StylesConfig } from 'react-select';
import { useConceptTheme } from '../../context/ThemeContext';
import { selectDarkT, selectLightT } from '../../utils/color';
interface ConceptSelectSingleProps<
Option,
Group extends GroupBase<Option> = GroupBase<Option>
>
extends Omit<Props<Option, false, Group>, 'theme'> {
}
function ConceptSelectSingle<
Option,
Group extends GroupBase<Option> = GroupBase<Option>
> ({ ...props }: ConceptSelectSingleProps<Option, Group>) {
const { darkMode, colors } = useConceptTheme();
const themeColors = useMemo(
() => {
return !darkMode ? selectLightT : selectDarkT;
}, [darkMode]);
const adjustedStyles: StylesConfig<Option, false, Group> = useMemo(
() => {
return {
control: (styles, { isDisabled }) => {
return {
...styles,
borderRadius: '0.25rem',
cursor: isDisabled ? 'not-allowed' : 'pointer'
};
},
option: (styles, { isSelected }) => {
return {
...styles,
backgroundColor: isSelected ? colors.bgSelected : styles.backgroundColor,
color: isSelected ? colors.fgSelected : styles.color,
borderWidth: '1px',
borderColor: colors.border
};
},
input: (styles) => ({...styles}),
placeholder: (styles) => ({...styles}),
singleValue: (styles) => ({...styles}),
};
}, [colors]);
return (
<Select
noOptionsMessage={() => 'Список пуст'}
theme={theme => ({
...theme,
borderRadius: 0,
colors: {
...theme.colors,
...themeColors
},
})}
styles={adjustedStyles}
{...props}
/>
);
}
export default ConceptSelectSingle;

View File

@ -1,6 +1,6 @@
import { Extension } from '@codemirror/state';
import { tags as t } from '@lezer/highlight';
import { tags } from '@lezer/highlight';
import { createTheme } from '@uiw/codemirror-themes';
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
import { EditorView } from 'codemirror';
@ -64,9 +64,9 @@ function RSInput({
}, [internalRef, innerref]);
const cursor = useMemo(() => editable ? 'cursor-text': 'cursor-default', [editable]);
const lightTheme: Extension = useMemo(
const customTheme: Extension = useMemo(
() => createTheme({
theme: 'light',
theme: darkMode ? 'dark' : 'light',
settings: {
fontFamily: 'inherit',
background: editable ? colors.bgInput : colors.bgDefault,
@ -74,35 +74,15 @@ function RSInput({
selection: colors.bgHover
},
styles: [
{ tag: t.name, class: 'text-[#b266ff] cursor-default' }, // GlobalID
{ tag: t.variableName, class: 'text-[#24821a]' }, // LocalID
{ tag: t.propertyName, class: '' }, // Radical
{ tag: t.keyword, class: 'text-[#001aff]' }, // keywords
{ tag: t.literal, class: 'text-[#001aff]' }, // literals
{ tag: t.controlKeyword, class: 'font-semibold'}, // R | I | D
{ tag: t.unit, class: 'text-[0.75rem]' }, // indicies
{ tag: tags.name, color: colors.fgPurple, cursor: 'default' }, // GlobalID
{ tag: tags.variableName, color: colors.fgGreen }, // LocalID
{ tag: tags.propertyName, color: colors.fgTeal }, // Radical
{ tag: tags.keyword, color: colors.fgBlue }, // keywords
{ tag: tags.literal, color: colors.fgBlue }, // literals
{ tag: tags.controlKeyword, fontWeight: '500'}, // R | I | D
{ tag: tags.unit, fontSize: '0.75rem' }, // indicies
]
}), [editable, colors]);
const darkTheme: Extension = useMemo(
() => createTheme({
theme: 'dark',
settings: {
fontFamily: 'inherit',
background: editable ? colors.bgInput : colors.bgDefault,
foreground: colors.fgDefault,
selection: colors.bgHover
},
styles: [
{ tag: t.name, class: 'text-[#dfbfff] cursor-default' }, // GlobalID
{ tag: t.variableName, class: 'text-[#69bf60]' }, // LocalID
{ tag: t.propertyName, class: '' }, // Radical
{ tag: t.keyword, class: 'text-[#808dff]' }, // keywords
{ tag: t.literal, class: 'text-[#808dff]' }, // literals
{ tag: t.controlKeyword, class: 'font-semibold'}, // R | I | D
{ tag: t.unit, class: 'text-[0.75rem]' }, // indicies
]
}), [editable, colors]);
}), [editable, colors, darkMode]);
const editorExtensions = useMemo(
() => [
@ -137,7 +117,7 @@ function RSInput({
}, [thisRef]);
return (
<div className={`flex flex-col w-full ${cursor}`}>
<div className={`flex flex-col w-full ${cursor}`}>
{label &&
<Label
text={label}
@ -148,7 +128,7 @@ function RSInput({
<CodeMirror id={id}
ref={thisRef}
basicSetup={editorSetup}
theme={darkMode ? darkTheme : lightTheme}
theme={customTheme}
extensions={editorExtensions}
indentWithTab={false}
onChange={onChange}

View File

@ -6,52 +6,48 @@
:root {
/* Light Theme */
--cl-bg-120: #ffffff;
--cl-bg-100: #f9fafb;
--cl-bg-80: #f3f4f6;
--cl-bg-60: #e5e7eb;
--cl-bg-40: #d1d5db;
--cl-bg-120: hsl(000, 000%, 100%);
--cl-bg-100: hsl(220, 020%, 098%);
--cl-bg-80: hsl(220, 014%, 096%);
--cl-bg-60: hsl(220, 013%, 091%);
--cl-bg-40: hsl(216, 012%, 084%);
--cl-fg-40: #8c8c8c;
--cl-fg-60: #777777;
--cl-fg-80: #333333;
--cl-fg-100: #000000;
--cl-fg-60: hsl(000, 000%, 055%);
--cl-fg-80: hsl(000, 000%, 047%);
--cl-fg-100: hsl(000, 000%, 000%);
--cl-prim-bg-100: #3377ff;
--cl-prim-bg-80: #ccddff;
--cl-prim-bg-60: #e0ebff;
--cl-prim-bg-100: hsl(220, 100%, 060%);
--cl-prim-bg-80: hsl(220, 100%, 090%);
--cl-prim-bg-60: hsl(220, 100%, 094%);
--cl-prim-fg-60: #1a63ff;
--cl-prim-fg-80: #0051ff;
--cl-prim-fg-100: #ffffff;
--cl-prim-fg-80: hsl(220, 100%, 050%);
--cl-prim-fg-100: hsl(000, 000%, 100%);
--cl-red-bg-100: #ffe5e5;
--cl-red-fg-100: #dc2626;
--cl-green-fg-100: #4ade80;
--cl-red-bg-100: hsl(000, 100%, 095%);
--cl-red-fg-100: hsl(000, 072%, 051%);
--cl-green-fg-100: hsl(120, 080%, 058%);
/* Dark Theme */
--cd-bg-120: #0d0d0d;
--cd-bg-100: #181818;
--cd-bg-80: #272727;
--cd-bg-60: #383838;
--cd-bg-40: #595959;
--cd-bg-120: hsl(000, 000%, 005%);
--cd-bg-100: hsl(000, 000%, 009%);
--cd-bg-80: hsl(000, 000%, 015%);
--cd-bg-60: hsl(000, 000%, 022%);
--cd-bg-40: hsl(000, 000%, 035%);
--cd-fg-40: #878792;
--cd-fg-60: #bcbcc2;
--cd-fg-80: #d4d4d8;
--cd-fg-100: #e4e4e7;
--cd-fg-60: hsl(000, 000%, 055%);
--cd-fg-80: hsl(000, 000%, 080%);
--cd-fg-100: hsl(000, 000%, 093%);
--cd-prim-bg-100: #e66000;
--cd-prim-bg-80: #cc7700;
--cd-prim-bg-60: #995900;
--cd-prim-bg-100: hsl(025, 079%, 052%);
--cd-prim-bg-80: hsl(035, 080%, 043%);
--cd-prim-bg-60: hsl(045, 080%, 031%);
--cd-prim-fg-60: #ffa666;
--cd-prim-fg-80: #ff6a00;
--cd-prim-fg-100: #ffffff;
--cd-prim-fg-80: hsl(025, 080%, 050%);
--cd-prim-fg-100: hsl(000, 000%, 100%);
--cd-red-bg-100: #4d0000;
--cd-red-fg-100: #ff334b;
--cd-green-fg-100: #22c55e;
--cd-red-bg-100: hsl(000, 100%, 015%);
--cd-red-fg-100: hsl(000, 100%, 060%);
--cd-green-fg-100: hsl(120, 080%, 040%);
/* Import overrides */
--toastify-color-dark: var(--cd-bg-60);
@ -192,10 +188,10 @@
.clr-btn-default,
.clr-btn-primary
):disabled {
color: var(--cl-fg-60);
color: var(--cl-fg-80);
background-color: var(--cl-bg-60);
.dark & {
color: var(--cd-fg-60);
color: var(--cd-fg-80);
background-color: var(--cd-bg-60);
}
}
@ -220,9 +216,9 @@
):focus {
outline-width: 2px;
outline-style: solid;
outline-color: var(--cl-bg-40);
outline-color: var(--cl-prim-bg-100);
.dark & {
outline-color: var(--cd-bg-40);
outline-color: var(--cd-prim-bg-100);
}
}
@ -236,28 +232,28 @@
}
.clr-footer {
color: var(--cl-fg-40);
color: var(--cl-fg-60);
.dark & {
color: var(--cd-fg-40);
color: var(--cd-fg-60);
}
}
.clr-btn-nav {
color: var(--cl-fg-60);
color: var(--cl-fg-80);
.dark & {
color: var(--cd-fg-60);
color: var(--cd-fg-80);
}
}
.clr-btn-clear {
color: var(--cl-fg-60);
color: var(--cl-fg-80);
&:disabled {
color: var(--cl-fg-40);
color: var(--cl-fg-60);
}
.dark & {
color: var(--cd-fg-60);
color: var(--cd-fg-80);
&:disabled {
color: var(--cd-fg-40);
color: var(--cd-fg-60);
}
}
}
@ -293,10 +289,10 @@
}
.cm-editor.cm-focused {
border-color: var(--cl-bg-40);
outline-color: var(--cl-bg-40);
outline-color: var(--cl-prim-bg-100);
.dark & {
border-color: var(--cd-bg-40);
outline-color: var(--cd-bg-40);
outline-color: var(--cd-prim-bg-100);
}
@apply border shadow rounded outline-2 outline
}
@ -304,22 +300,3 @@
.rdt_TableCell{
font-size: 0.875rem;
}
.react-dropdown-select-item {
color: var(--cl-fg-100);
border-color: var(--cl-bg-40);
background-color: var(--cl-bg-100);
&:hover {
background-color: var(--cl-prim-bg-60);
}
.dark & {
color: var(--cd-fg-100);
border-color: var(--cd-bg-40);
background-color: var(--cd-bg-100);
&:hover {
background-color: var(--cd-prim-bg-60);
}
}
}

View File

@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import ConceptSelect from '../../components/Common/ConceptSelect';
import ConceptSelectSingle from '../../components/Common/ConceptSelectSingle';
import Modal from '../../components/Common/Modal';
import TextArea from '../../components/Common/TextArea';
import RSInput from '../../components/RSInput';
@ -55,15 +55,15 @@ function DlgCreateCst({ hideWindow, initial, onCreate }: DlgCreateCstProps) {
canSubmit={validated}
onSubmit={handleSubmit}
>
<div className='h-fit w-[35rem] px-2 mb-2 flex flex-col justify-stretch'>
<div className='h-fit w-[35rem] px-2 mb-2 flex flex-col justify-stretch'>
<div className='flex justify-center w-full'>
<ConceptSelect
className='my-2 min-w-[15rem] self-center'
options={CstTypeSelector}
placeholder='Выберите тип'
values={selectedType ? [{ value: selectedType, label: getCstTypeLabel(selectedType) }] : []}
onChange={data => setSelectedType(data.length > 0 ? data[0].value : CstType.BASE)}
/>
<ConceptSelectSingle
className='my-2 min-w-[15rem] self-center'
options={CstTypeSelector}
placeholder='Выберите тип'
value={selectedType ? { value: selectedType, label: getCstTypeLabel(selectedType) } : null}
onChange={data => setSelectedType(data?.value ?? CstType.BASE)}
/>
</div>
<TextArea id='term' label='Термин'
placeholder='Схемный или предметный термин, обозначающий данное понятие или утверждение'
@ -72,12 +72,14 @@ function DlgCreateCst({ hideWindow, initial, onCreate }: DlgCreateCstProps) {
spellCheck
onChange={event => setTerm(event.target.value)}
/>
<RSInput id='expression' label='Формальное выражение'
editable
height='5.5rem'
value={expression}
onChange={value => setExpression(value)}
/>
<div className='mt-3'>
<RSInput id='expression' label='Формальное выражение'
editable
height='5.5rem'
value={expression}
onChange={value => setExpression(value)}
/>
</div>
<TextArea id='definition' label='Текстовое определение'
placeholder='Лингвистическая интерпретация формального выражения'
rows={2}
@ -92,7 +94,7 @@ function DlgCreateCst({ hideWindow, initial, onCreate }: DlgCreateCstProps) {
spellCheck
onChange={event => setConvention(event.target.value)}
/>
</div>
</div>
</Modal>
);
}

View File

@ -1,6 +1,6 @@
import { useLayoutEffect, useState } from 'react';
import ConceptSelect from '../../components/Common/ConceptSelect';
import ConceptSelectSingle from '../../components/Common/ConceptSelectSingle';
import Modal from '../../components/Common/Modal';
import TextInput from '../../components/Common/TextInput';
import { useRSForm } from '../../context/RSFormContext';
@ -67,12 +67,12 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
submitText='Переименовать'
>
<div className='flex items-center gap-4 px-2 my-2 h-fit min-w-[25rem]'>
<ConceptSelect
<ConceptSelectSingle
className='min-w-[14rem] self-center'
options={CstTypeSelector}
placeholder='Выберите тип'
values={cstType ? [{ value: cstType, label: getCstTypeLabel(cstType) }] : []}
onChange={data => setCstType(data.length > 0 ? data[0].value : CstType.BASE)}
value={cstType ? { value: cstType, label: getCstTypeLabel(cstType) } : null}
onChange={data => setCstType(data?.value ?? CstType.BASE)}
/>
<div>
<TextInput id='alias' label='Имя'

View File

@ -5,7 +5,7 @@ import { GraphCanvas, GraphCanvasRef, GraphEdge,
import Button from '../../components/Common/Button';
import Checkbox from '../../components/Common/Checkbox';
import ConceptSelect from '../../components/Common/ConceptSelect';
import ConceptSelectSingle from '../../components/Common/ConceptSelectSingle';
import ConceptTooltip from '../../components/Common/ConceptTooltip';
import Divider from '../../components/Common/Divider';
import MiniButton from '../../components/Common/MiniButton';
@ -16,7 +16,7 @@ import { useRSForm } from '../../context/RSFormContext';
import { useConceptTheme } from '../../context/ThemeContext';
import useLocalStorage from '../../hooks/useLocalStorage';
import { graphDarkT, graphLightT, IColorTheme } from '../../utils/color';
import { prefixes, resources } from '../../utils/constants';
import { prefixes, resources, TIMEOUT_GRAPH_REFRESH } from '../../utils/constants';
import { Graph } from '../../utils/Graph';
import { CstType, IConstituenta, ICstCreateData } from '../../utils/models';
import { getCstClassColor, getCstStatusColor,
@ -220,9 +220,9 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
}, [selectedDismissed, selections]);
const nothingSelected = useMemo(() => allSelected.length === 0, [allSelected]);
const handleRecreate = useCallback(
const handleResetViewpoint = useCallback(
() => {
graphRef.current?.resetControls();
graphRef.current?.resetControls(true);
graphRef.current?.centerGraph();
}, []);
@ -293,6 +293,16 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
});
}
function handleChangeLayout(newLayout: LayoutTypes) {
if (newLayout === layout) {
return;
}
setLayout(newLayout);
setTimeout(() => {
handleResetViewpoint();
}, TIMEOUT_GRAPH_REFRESH);
}
function getOptions() {
return {
noHermits: noHermits,
@ -394,23 +404,23 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
widthClass='h-full'
onClick={() => setShowOptions(true)}
/>
<ConceptSelect
<ConceptSelectSingle
className='min-w-[9.8rem]'
options={GraphColoringSelector}
searchable={false}
isSearchable={false}
placeholder='Выберите цвет'
values={coloringScheme ? [{ value: coloringScheme, label: mapColoringLabels.get(coloringScheme) }] : []}
onChange={data => setColoringScheme(data.length > 0 ? data[0].value : GraphColoringSelector[0].value)}
value={coloringScheme ? { value: coloringScheme, label: mapColoringLabels.get(coloringScheme) } : null}
onChange={data => setColoringScheme(data?.value ?? GraphColoringSelector[0].value)}
/>
</div>
<ConceptSelect
<ConceptSelectSingle
className='w-full mt-1'
options={GraphLayoutSelector}
searchable={false}
isSearchable={false}
placeholder='Способ расположения'
values={layout ? [{ value: layout, label: mapLayoutLabels.get(layout) }] : []}
onChange={data => setLayout(data.length > 0 ? data[0].value : GraphLayoutSelector[0].value)}
value={layout ? { value: layout, label: mapLayoutLabels.get(layout) } : null}
onChange={data => handleChangeLayout(data?.value ?? GraphLayoutSelector[0].value)}
/>
<Checkbox
label='Скрыть текст'
@ -471,8 +481,8 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
</div>
<MiniButton
icon={<ArrowsRotateIcon size={5} />}
tooltip='Пересоздать граф'
onClick={handleRecreate}
tooltip='Восстановить камеру'
onClick={handleResetViewpoint}
/>
</div>
<ConceptTooltip anchorSelect='#items-graph-help'>

View File

@ -1,71 +1,108 @@
export interface IColorTheme {
red: string
green: string
blue: string
teal: string
orange: string
// =========== Modules contains all dynamic color definitions ==========
// ============= MAIN COLOR THEMES ==========
export interface IColorTheme {
bgDefault: string
bgInput: string
bgControls: string
bgDisabled: string
bgHover: string
bgPrimary: string
bgSelected: string
bgHover: string
bgWarning: string
border: string
fgDefault: string
fgSelected: string
fgDisabled: string
fgWarning: string
// Hightlight syntax accents
bgRed: string
bgGreen: string
bgBlue: string
bgPurple: string
bgTeal: string
bgOrange: string
fgRed: string
fgGreen: string
fgBlue: string
fgPurple: string
fgTeal: string
fgOrange: string
}
// =========== GENERAL THEMES =========
// ======= Light =======
export const lightT: IColorTheme = {
red: '#ffc9c9',
green: '#aaff80',
blue: '#b3bdff',
teal: '#a5e9fa',
orange: '#ffbb80',
bgDefault: 'var(--cl-bg-100)',
bgInput: 'var(--cl-bg-120)',
bgDefault: 'var(--cl-bg-100)',
bgInput: 'var(--cl-bg-120)',
bgControls: 'var(--cl-bg-80)',
bgDisabled: 'var(--cl-bg-60)',
bgHover: 'var(--cl-prim-bg-60)',
bgPrimary: 'var(--cl-prim-bg-100)',
bgSelected: 'var(--cl-prim-bg-80)',
bgWarning: 'var(--cl-red-bg-100)',
bgHover: 'var(--cl-prim-bg-60)',
bgWarning: 'var(--cl-red-bg-100)',
border: 'var(--cl-bg-40)',
border: 'var(--cl-bg-40)',
fgDefault: 'var(--cl-fg-100)',
fgDisabled: 'var(--cl-fg-60)',
fgWarning: 'var(--cl-red-fg-100)'
fgDefault: 'var(--cl-fg-100)',
fgSelected: 'var(--cl-fg-100)',
fgDisabled: 'var(--cl-fg-80)',
fgWarning: 'var(--cl-red-fg-100)',
// Hightlight syntax accents
bgRed: 'hsl(000, 100%, 089%)',
bgGreen: 'hsl(100, 100%, 075%)',
bgBlue: 'hsl(235, 100%, 085%)',
bgPurple: 'hsl(274, 089%, 081%)',
bgTeal: 'hsl(192, 089%, 081%)',
bgOrange: 'hsl(028, 100%, 075%)',
fgRed: 'hsl(000, 090%, 045%)',
fgGreen: 'hsl(100, 090%, 035%)',
fgBlue: 'hsl(235, 100%, 050%)',
fgPurple: 'hsl(270, 100%, 070%)',
fgTeal: 'hsl(192, 090%, 040%)',
fgOrange: 'hsl(030, 090%, 055%)'
};
// ======= DARK ========
export const darkT: IColorTheme = {
red: '#bf0d00',
green: '#2b8000',
blue: '#394bbf',
teal: '#007a99',
orange: '#964600',
bgDefault: 'var(--cd-bg-100)',
bgInput: 'var(--cd-bg-120)',
bgDefault: 'var(--cd-bg-100)',
bgInput: 'var(--cd-bg-120)',
bgControls: 'var(--cd-bg-80)',
bgDisabled: 'var(--cd-bg-60)',
bgHover: 'var(--cd-prim-bg-60)',
bgPrimary: 'var(--cd-prim-bg-100)',
bgSelected: 'var(--cd-prim-bg-80)',
bgWarning: 'var(--cd-red-bg-100)',
bgHover: 'var(--cd-prim-bg-60)',
bgWarning: 'var(--cd-red-bg-100)',
border: 'var(--cd-bg-40)',
border: 'var(--cd-bg-40)',
fgDefault: 'var(--cd-fg-100)',
fgDisabled: 'var(--cd-fg-60)',
fgWarning: 'var(--cd-red-fg-100)'
fgDefault: 'var(--cd-fg-100)',
fgSelected: 'var(--cd-fg-100)',
fgDisabled: 'var(--cd-fg-80)',
fgWarning: 'var(--cd-red-fg-100)',
// Hightlight syntax accents
bgRed: 'hsl(000, 080%, 037%)',
bgGreen: 'hsl(100, 080%, 025%)',
bgBlue: 'hsl(235, 054%, 049%)',
bgPurple: 'hsl(270, 080%, 050%)',
bgTeal: 'hsl(192, 080%, 030%)',
bgOrange: 'hsl(035, 100%, 035%)',
fgRed: 'hsl(000, 080%, 050%)',
fgGreen: 'hsl(100, 080%, 040%)',
fgBlue: 'hsl(235, 100%, 080%)',
fgPurple: 'hsl(270, 100%, 080%)',
fgTeal: 'hsl(192, 100%, 030%)',
fgOrange: 'hsl(035, 100%, 050%)'
};
// ========= DATA TABLE THEMES ========
export const dataTableLightT = {
text: {
@ -119,6 +156,51 @@ export const dataTableDarkT = {
}
};
// ============ SELECT THEMES ==========
export const selectLightT = {
primary: lightT.bgPrimary,
primary75: lightT.bgSelected,
primary50: lightT.bgHover,
primary25: lightT.bgHover,
danger: lightT.fgWarning,
dangerLight: lightT.bgWarning,
neutral0: lightT.bgInput,
neutral5: lightT.bgDefault,
neutral10: lightT.border,
neutral20: lightT.border,
neutral30: lightT.border,
neutral40: lightT.fgDisabled,
neutral50: lightT.fgWarning,
neutral60: lightT.fgDefault,
neutral70: lightT.fgWarning,
neutral80: lightT.fgDefault,
neutral90: lightT.fgWarning
}
export const selectDarkT = {
primary: darkT.bgPrimary,
primary75: darkT.bgSelected,
primary50: darkT.bgHover,
primary25: darkT.bgHover,
danger: darkT.fgWarning,
dangerLight: darkT.bgWarning,
neutral0: darkT.bgInput,
neutral5: darkT.bgDefault,
neutral10: darkT.border,
neutral20: darkT.border,
neutral30: darkT.border,
neutral40: darkT.fgDisabled,
neutral50: darkT.fgWarning,
neutral60: darkT.fgDefault,
neutral70: darkT.fgWarning,
neutral80: darkT.fgDefault,
neutral90: darkT.fgWarning
}
// ============ GRAPH THEMES ==========
export const graphLightT = {
canvas: {
@ -221,20 +303,22 @@ export const graphDarkT = {
// ======== Bracket Matching Themes ===========
export const bracketsLightT = {
'.cc-nonmatchingBracket': {
color: lightT.fgWarning,
color: lightT.fgRed,
fontWeight: 700,
},
'&.cm-focused .cc-matchingBracket': {
backgroundColor: lightT.bgSelected,
color: lightT.fgSelected
},
};
export const bracketsDarkT = {
'.cc-nonmatchingBracket': {
color: darkT.fgWarning,
color: darkT.fgRed,
fontWeight: 700,
},
'&.cm-focused .cc-matchingBracket': {
backgroundColor: darkT.bgSelected,
color: darkT.fgSelected
},
};

View File

@ -3,6 +3,7 @@ export const config = {
backend: import.meta.env.VITE_PORTAL_BACKEND as string
};
export const TIMEOUT_UI_REFRESH = 100;
export const TIMEOUT_GRAPH_REFRESH = 200;
export const youtube = {
intro: '0Ty9mu9sOJo'

View File

@ -315,12 +315,12 @@ export const GraphColoringSelector: {value: ColoringScheme, label: string}[] = [
export function getCstStatusColor(status: ExpressionStatus, colors: IColorTheme): string {
switch (status) {
case ExpressionStatus.VERIFIED: return colors.green;
case ExpressionStatus.INCORRECT: return colors.red;
case ExpressionStatus.INCALCULABLE: return colors.orange;
case ExpressionStatus.PROPERTY: return colors.teal;
case ExpressionStatus.UNKNOWN: return colors.blue;
case ExpressionStatus.UNDEFINED: return colors.blue;
case ExpressionStatus.VERIFIED: return colors.bgGreen;
case ExpressionStatus.INCORRECT: return colors.bgRed;
case ExpressionStatus.INCALCULABLE: return colors.bgOrange;
case ExpressionStatus.PROPERTY: return colors.bgTeal;
case ExpressionStatus.UNKNOWN: return colors.bgBlue;
case ExpressionStatus.UNDEFINED: return colors.bgBlue;
}
}
@ -392,10 +392,10 @@ export const mapTopicInfo: Map<HelpTopic, IDescriptor> = new Map([
export function getCstClassColor(cstClass: CstClass, colors: IColorTheme): string {
switch (cstClass) {
case CstClass.BASIC: return colors.green;
case CstClass.DERIVED: return colors.blue;
case CstClass.STATEMENT: return colors.red;
case CstClass.TEMPLATE: return colors.teal;
case CstClass.BASIC: return colors.bgGreen;
case CstClass.DERIVED: return colors.bgBlue;
case CstClass.STATEMENT: return colors.bgRed;
case CstClass.TEMPLATE: return colors.bgTeal;
}
}
@ -684,7 +684,7 @@ export function getASTNodeColor(node: ISyntaxTreeNode, colors: IColorTheme): str
case TokenID.PUNC_DEFINE:
case TokenID.PUNC_STRUCT:
case TokenID.ID_LOCAL:
return colors.green;
return colors.bgGreen;
case TokenID.ID_GLOBAL:
case TokenID.ID_FUNCTION:
@ -693,7 +693,7 @@ export function getASTNodeColor(node: ISyntaxTreeNode, colors: IColorTheme): str
case TokenID.LIT_INTEGER:
case TokenID.LIT_EMPTYSET:
case TokenID.LIT_INTSET:
return colors.teal;
return colors.bgTeal;
case TokenID.FORALL:
case TokenID.EXISTS:
@ -713,7 +713,7 @@ export function getASTNodeColor(node: ISyntaxTreeNode, colors: IColorTheme): str
case TokenID.SUBSET_OR_EQ:
case TokenID.SUBSET:
case TokenID.NOTSUBSET:
return colors.orange;
return colors.bgOrange;
case TokenID.NT_TUPLE:
case TokenID.NT_ENUMERATION:
@ -733,7 +733,7 @@ export function getASTNodeColor(node: ISyntaxTreeNode, colors: IColorTheme): str
case TokenID.CARD:
case TokenID.BOOL:
case TokenID.DEBOOL:
return colors.blue;
return colors.bgBlue;
case TokenID.NT_FUNC_DEFINITION:
case TokenID.NT_DECLARATIVE_EXPR:
@ -752,8 +752,8 @@ export function getASTNodeColor(node: ISyntaxTreeNode, colors: IColorTheme): str
case TokenID.PUNC_ASSIGN:
case TokenID.PUNC_ITERATE:
return colors.red;
return colors.bgRed;
}
// node
return colors.red;
return colors.bgRed;
}