Improve GraphUI. Add sizing parameter

This commit is contained in:
IRBorisov 2024-04-07 19:22:19 +03:00
parent 26fe49b352
commit f041bd172e
16 changed files with 224 additions and 121 deletions

View File

@ -22,6 +22,9 @@ export { LuGlasses as IconReader } from 'react-icons/lu';
export { FiBell as IconFollow } from 'react-icons/fi'; export { FiBell as IconFollow } from 'react-icons/fi';
export { FiBellOff as IconFollowOff } from 'react-icons/fi'; export { FiBellOff as IconFollowOff } from 'react-icons/fi';
export { LuChevronDown as IconDropArrow } from 'react-icons/lu';
export { LuChevronUp as IconDropArrowUp } from 'react-icons/lu';
export { TbColumns as IconList } from 'react-icons/tb'; export { TbColumns as IconList } from 'react-icons/tb';
export { TbColumnsOff as IconListOff } from 'react-icons/tb'; export { TbColumnsOff as IconListOff } from 'react-icons/tb';
export { BiFontFamily as IconText } from 'react-icons/bi'; export { BiFontFamily as IconText } from 'react-icons/bi';

View File

@ -3,6 +3,7 @@
import { GraphCanvas as GraphUI } from 'reagraph'; import { GraphCanvas as GraphUI } from 'reagraph';
export { type GraphEdge, type GraphNode, type GraphCanvasRef, type LayoutTypes, Sphere, useSelection } from 'reagraph'; export { type GraphEdge, type GraphNode, type GraphCanvasRef, Sphere, useSelection } from 'reagraph';
export { type LayoutTypes as GraphLayout } from 'reagraph';
export default GraphUI; export default GraphUI;

View File

@ -9,10 +9,12 @@ import { selectDarkT, selectLightT } from '@/styling/color';
interface SelectSingleProps<Option, Group extends GroupBase<Option> = GroupBase<Option>> interface SelectSingleProps<Option, Group extends GroupBase<Option> = GroupBase<Option>>
extends Omit<Props<Option, false, Group>, 'theme' | 'menuPortalTarget'> { extends Omit<Props<Option, false, Group>, 'theme' | 'menuPortalTarget'> {
noPortal?: boolean; noPortal?: boolean;
noBorder?: boolean;
} }
function SelectSingle<Option, Group extends GroupBase<Option> = GroupBase<Option>>({ function SelectSingle<Option, Group extends GroupBase<Option> = GroupBase<Option>>({
noPortal, noPortal,
noBorder,
...restProps ...restProps
}: SelectSingleProps<Option, Group>) { }: SelectSingleProps<Option, Group>) {
const { darkMode, colors } = useConceptOptions(); const { darkMode, colors } = useConceptOptions();
@ -20,32 +22,33 @@ function SelectSingle<Option, Group extends GroupBase<Option> = GroupBase<Option
const adjustedStyles: StylesConfig<Option, false, Group> = useMemo( const adjustedStyles: StylesConfig<Option, false, Group> = useMemo(
() => ({ () => ({
control: (styles, { isDisabled }) => ({ control: (defaultStyles, { isDisabled }) => ({
...styles, ...defaultStyles,
borderRadius: '0.25rem', borderRadius: '0.25rem',
...(noBorder ? { borderWidth: 0 } : {}),
cursor: isDisabled ? 'not-allowed' : 'pointer', cursor: isDisabled ? 'not-allowed' : 'pointer',
boxShadow: 'none' boxShadow: 'none'
}), }),
menuPortal: styles => ({ menuPortal: defaultStyles => ({
...styles, ...defaultStyles,
zIndex: 9999 zIndex: 9999
}), }),
menuList: styles => ({ menuList: defaultStyles => ({
...styles, ...defaultStyles,
padding: '0px' padding: '0px'
}), }),
option: (styles, { isSelected }) => ({ option: (defaultStyles, { isSelected }) => ({
...styles, ...defaultStyles,
backgroundColor: isSelected ? colors.bgSelected : styles.backgroundColor, backgroundColor: isSelected ? colors.bgSelected : defaultStyles.backgroundColor,
color: isSelected ? colors.fgSelected : styles.color, color: isSelected ? colors.fgSelected : defaultStyles.color,
borderWidth: '1px', borderWidth: '1px',
borderColor: colors.border borderColor: colors.border
}), }),
input: styles => ({ ...styles }), input: defaultStyles => ({ ...defaultStyles }),
placeholder: styles => ({ ...styles }), placeholder: defaultStyles => ({ ...defaultStyles }),
singleValue: styles => ({ ...styles }) singleValue: defaultStyles => ({ ...defaultStyles })
}), }),
[colors] [colors, noBorder]
); );
return ( return (

View File

@ -85,8 +85,9 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
return ( return (
<> <>
<div className='flex'> <div className='flex divide-x border-x border-t rounded-t-md'>
<SelectSingle <SelectSingle
noBorder
placeholder='Выберите категорию' placeholder='Выберите категорию'
className='flex-grow border-none' className='flex-grow border-none'
options={categorySelector} options={categorySelector}
@ -102,6 +103,7 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
isClearable isClearable
/> />
<SelectSingle <SelectSingle
noBorder
placeholder='Источник' placeholder='Источник'
className='w-[12rem]' className='w-[12rem]'
options={templateSelector} options={templateSelector}

View File

@ -27,7 +27,7 @@ function DlgGraphParams({ hideWindow, initial, onConfirm }: DlgGraphParamsProps)
header='Настройки графа термов' header='Настройки графа термов'
onSubmit={handleSubmit} onSubmit={handleSubmit}
submitText='Применить' submitText='Применить'
className='flex gap-12 px-6 py-2' className='flex gap-6 px-6 py-2 w-[35rem]'
> >
<div className='flex flex-col gap-1'> <div className='flex flex-col gap-1'>
<h1 className='mb-2'>Преобразования</h1> <h1 className='mb-2'>Преобразования</h1>

View File

@ -26,7 +26,12 @@ export enum DependencyMode {
/** /**
* Represents graph node coloring scheme. * Represents graph node coloring scheme.
*/ */
export type GraphColoringScheme = 'none' | 'status' | 'type'; export type GraphColoring = 'none' | 'status' | 'type';
/**
* Represents graph node sizing scheme.
*/
export type GraphSizing = 'none' | 'complex' | 'derived';
/** /**
* Represents font styles. * Represents font styles.

View File

@ -1,7 +1,7 @@
/** /**
* Module: API for miscellaneous frontend model types. Future targets for refactoring aimed at extracting modules. * Module: API for miscellaneous frontend model types. Future targets for refactoring aimed at extracting modules.
*/ */
import { DependencyMode, FontStyle, ILibraryFilter, LibraryFilterStrategy } from './miscellaneous'; import { DependencyMode, FontStyle, GraphSizing, ILibraryFilter, LibraryFilterStrategy } from './miscellaneous';
import { IConstituenta, IRSForm } from './rsform'; import { IConstituenta, IRSForm } from './rsform';
/** /**
@ -56,3 +56,16 @@ export function filterFromStrategy(strategy: LibraryFilterStrategy): ILibraryFil
case LibraryFilterStrategy.OWNED: return { is_owned: true }; case LibraryFilterStrategy.OWNED: return { is_owned: true };
} }
} }
/**
* Apply {@link GraphSizing} to a given {@link IConstituenta}.
*/
export function applyNodeSizing(target: IConstituenta, sizing: GraphSizing): number | undefined {
if (sizing === 'none') {
return undefined;
} else if (sizing === 'complex') {
return target.is_simple_expression ? 1 : 2;
} else {
return target.parent ? 1 : 2;
}
}

View File

@ -3,15 +3,16 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { AnimatePresence } from 'framer-motion'; import { AnimatePresence } from 'framer-motion';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
import { GraphEdge, GraphNode, LayoutTypes } from 'reagraph';
import InfoConstituenta from '@/components/info/InfoConstituenta'; import InfoConstituenta from '@/components/info/InfoConstituenta';
import SelectedCounter from '@/components/info/SelectedCounter'; import SelectedCounter from '@/components/info/SelectedCounter';
import { GraphEdge, GraphLayout, GraphNode } from '@/components/ui/GraphUI';
import Overlay from '@/components/ui/Overlay'; import Overlay from '@/components/ui/Overlay';
import { useConceptOptions } from '@/context/OptionsContext'; import { useConceptOptions } from '@/context/OptionsContext';
import DlgGraphParams from '@/dialogs/DlgGraphParams'; import DlgGraphParams from '@/dialogs/DlgGraphParams';
import useLocalStorage from '@/hooks/useLocalStorage'; import useLocalStorage from '@/hooks/useLocalStorage';
import { GraphColoringScheme, GraphFilterParams } from '@/models/miscellaneous'; import { GraphColoring, GraphFilterParams, GraphSizing } from '@/models/miscellaneous';
import { applyNodeSizing } from '@/models/miscellaneousAPI';
import { ConstituentaID, CstType } from '@/models/rsform'; import { ConstituentaID, CstType } from '@/models/rsform';
import { colorBgGraphNode } from '@/styling/color'; import { colorBgGraphNode } from '@/styling/color';
import { PARAMETER, storage } from '@/utils/constants'; import { PARAMETER, storage } from '@/utils/constants';
@ -52,11 +53,9 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
const [hidden, setHidden] = useState<ConstituentaID[]>([]); const [hidden, setHidden] = useState<ConstituentaID[]>([]);
const [layout, setLayout] = useLocalStorage<LayoutTypes>(storage.rsgraphLayout, 'treeTd2d'); const [layout, setLayout] = useLocalStorage<GraphLayout>(storage.rsgraphLayout, 'treeTd2d');
const [coloringScheme, setColoringScheme] = useLocalStorage<GraphColoringScheme>( const [coloring, setColoring] = useLocalStorage<GraphColoring>(storage.rsgraphColoring, 'type');
storage.rsgraphColoringScheme, const [sizing, setSizing] = useLocalStorage<GraphSizing>(storage.rsgraphSizing, 'derived');
'type'
);
const [orbit, setOrbit] = useState(false); const [orbit, setOrbit] = useState(false);
const is3D = useMemo(() => layout.includes('3d'), [layout]); const is3D = useMemo(() => layout.includes('3d'), [layout]);
@ -91,15 +90,15 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
if (cst) { if (cst) {
result.push({ result.push({
id: String(node.id), id: String(node.id),
fill: colorBgGraphNode(cst, coloringScheme, colors), fill: colorBgGraphNode(cst, coloring, colors),
label: cst.alias, label: cst.alias,
subLabel: !filterParams.noText ? cst.term_resolved : undefined, subLabel: !filterParams.noText ? cst.term_resolved : undefined,
size: cst.parent_alias ? 1 : 2 size: applyNodeSizing(cst, sizing)
}); });
} }
}); });
return result; return result;
}, [controller.schema, coloringScheme, filtered.nodes, filterParams.noText, colors]); }, [controller.schema, coloring, sizing, filtered.nodes, filterParams.noText, colors]);
const edges: GraphEdge[] = useMemo(() => { const edges: GraphEdge[] = useMemo(() => {
const result: GraphEdge[] = []; const result: GraphEdge[] = [];
@ -132,7 +131,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
controller.deleteCst(); controller.deleteCst();
} }
function handleChangeLayout(newLayout: LayoutTypes) { function handleChangeLayout(newLayout: GraphLayout) {
if (newLayout === layout) { if (newLayout === layout) {
return; return;
} }
@ -221,7 +220,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
hideZero hideZero
totalCount={controller.schema?.stats?.count_all ?? 0} totalCount={controller.schema?.stats?.count_all ?? 0}
selectedCount={controller.selected.length} selectedCount={controller.selected.length}
position='top-[0.3rem] left-0' position='top-[4.3rem] sm:top-[0.3rem] left-0'
/> />
<GraphToolbar <GraphToolbar
@ -253,19 +252,21 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
</Overlay> </Overlay>
) : null} ) : null}
<Overlay position='top-9 left-0' className='flex gap-1'> <Overlay position='top-[6.25rem] sm:top-9 left-0' className='flex gap-1'>
<div className='cc-column w-[13.5rem]'> <div className='flex flex-col ml-2 w-[13.5rem]'>
<GraphSelectors <GraphSelectors
coloring={coloringScheme} coloring={coloring}
layout={layout} layout={layout}
sizing={sizing}
setLayout={handleChangeLayout} setLayout={handleChangeLayout}
setColoring={setColoringScheme} setColoring={setColoring}
setSizing={setSizing}
/> />
<ViewHidden <ViewHidden
items={hidden} items={hidden}
selected={controller.selected} selected={controller.selected}
schema={controller.schema} schema={controller.schema}
coloringScheme={coloringScheme} coloringScheme={coloring}
toggleSelection={controller.toggleSelect} toggleSelection={controller.toggleSelect}
onEdit={onOpenEdit} onEdit={onOpenEdit}
/> />

View File

@ -1,35 +1,46 @@
import { LayoutTypes } from 'reagraph'; import { GraphLayout } from '@/components/ui/GraphUI';
import SelectSingle from '@/components/ui/SelectSingle'; import SelectSingle from '@/components/ui/SelectSingle';
import { GraphColoringScheme } from '@/models/miscellaneous'; import { GraphColoring, GraphSizing } from '@/models/miscellaneous';
import { mapLabelColoring, mapLabelLayout } from '@/utils/labels'; import { mapLabelColoring, mapLabelLayout, mapLabelSizing } from '@/utils/labels';
import { SelectorGraphColoring, SelectorGraphLayout } from '@/utils/selectors'; import { SelectorGraphColoring, SelectorGraphLayout, SelectorGraphSizing } from '@/utils/selectors';
interface GraphSelectorsProps { interface GraphSelectorsProps {
coloring: GraphColoringScheme; coloring: GraphColoring;
layout: LayoutTypes; layout: GraphLayout;
sizing: GraphSizing;
setLayout: (newValue: LayoutTypes) => void; setLayout: (newValue: GraphLayout) => void;
setColoring: (newValue: GraphColoringScheme) => void; setColoring: (newValue: GraphColoring) => void;
setSizing: (newValue: GraphSizing) => void;
} }
function GraphSelectors({ coloring, setColoring, layout, setLayout }: GraphSelectorsProps) { function GraphSelectors({ coloring, setColoring, layout, setLayout, sizing, setSizing }: GraphSelectorsProps) {
return ( return (
<div className='px-2 text-sm select-none'> <div className='select-none border rounded-t-md rounded-b-none divide-y'>
<SelectSingle
placeholder='Выберите цвет'
options={SelectorGraphColoring}
isSearchable={false}
value={coloring ? { value: coloring, label: mapLabelColoring.get(coloring) } : null}
onChange={data => setColoring(data?.value ?? SelectorGraphColoring[0].value)}
/>
<SelectSingle <SelectSingle
noBorder
placeholder='Способ расположения' placeholder='Способ расположения'
options={SelectorGraphLayout} options={SelectorGraphLayout}
isSearchable={false} isSearchable={false}
value={layout ? { value: layout, label: mapLabelLayout.get(layout) } : null} value={layout ? { value: layout, label: mapLabelLayout.get(layout) } : null}
onChange={data => setLayout(data?.value ?? SelectorGraphLayout[0].value)} onChange={data => setLayout(data?.value ?? SelectorGraphLayout[0].value)}
/> />
<SelectSingle
noBorder
placeholder='Цветовая схема'
options={SelectorGraphColoring}
isSearchable={false}
value={coloring ? { value: coloring, label: mapLabelColoring.get(coloring) } : null}
onChange={data => setColoring(data?.value ?? SelectorGraphColoring[0].value)}
/>
<SelectSingle
noBorder
placeholder='Размер узлов'
options={SelectorGraphSizing}
isSearchable={false}
value={layout ? { value: sizing, label: mapLabelSizing.get(sizing) } : null}
onChange={data => setSizing(data?.value ?? SelectorGraphSizing[0].value)}
/>
</div> </div>
); );
} }

View File

@ -2,7 +2,7 @@
import { useCallback, useLayoutEffect, useMemo, useRef } from 'react'; import { useCallback, useLayoutEffect, useMemo, useRef } from 'react';
import GraphUI, { GraphCanvasRef, GraphEdge, GraphNode, LayoutTypes, useSelection } from '@/components/ui/GraphUI'; import GraphUI, { GraphCanvasRef, GraphEdge, GraphLayout, GraphNode, useSelection } from '@/components/ui/GraphUI';
import { useConceptOptions } from '@/context/OptionsContext'; import { useConceptOptions } from '@/context/OptionsContext';
import { ConstituentaID } from '@/models/rsform'; import { ConstituentaID } from '@/models/rsform';
import { graphDarkT, graphLightT } from '@/styling/color'; import { graphDarkT, graphLightT } from '@/styling/color';
@ -13,7 +13,7 @@ interface TermGraphProps {
edges: GraphEdge[]; edges: GraphEdge[];
selectedIDs: ConstituentaID[]; selectedIDs: ConstituentaID[];
layout: LayoutTypes; layout: GraphLayout;
is3D: boolean; is3D: boolean;
orbit: boolean; orbit: boolean;

View File

@ -1,47 +1,84 @@
'use client'; 'use client';
import { useCallback, useMemo } from 'react'; import clsx from 'clsx';
import { motion } from 'framer-motion';
import { useMemo } from 'react';
import { IconDropArrow, IconDropArrowUp } from '@/components/Icons';
import ConstituentaTooltip from '@/components/info/ConstituentaTooltip'; import ConstituentaTooltip from '@/components/info/ConstituentaTooltip';
import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
import { useConceptOptions } from '@/context/OptionsContext'; import { useConceptOptions } from '@/context/OptionsContext';
import { GraphColoringScheme } from '@/models/miscellaneous'; import useLocalStorage from '@/hooks/useLocalStorage';
import useWindowSize from '@/hooks/useWindowSize';
import { GraphColoring } from '@/models/miscellaneous';
import { ConstituentaID, IRSForm } from '@/models/rsform'; import { ConstituentaID, IRSForm } from '@/models/rsform';
import { animateDropdown, animateHiddenHeader } from '@/styling/animations';
import { colorBgGraphNode } from '@/styling/color'; import { colorBgGraphNode } from '@/styling/color';
import { prefixes } from '@/utils/constants'; import { prefixes, storage } from '@/utils/constants';
interface ViewHiddenProps { interface ViewHiddenProps {
items: ConstituentaID[]; items: ConstituentaID[];
selected: ConstituentaID[]; selected: ConstituentaID[];
schema?: IRSForm; schema?: IRSForm;
coloringScheme: GraphColoringScheme; coloringScheme: GraphColoring;
toggleSelection: (cstID: ConstituentaID) => void; toggleSelection: (cstID: ConstituentaID) => void;
onEdit: (cstID: ConstituentaID) => void; onEdit: (cstID: ConstituentaID) => void;
} }
function ViewHidden({ items, selected, toggleSelection, schema, coloringScheme, onEdit }: ViewHiddenProps) { function ViewHidden({ items, selected, toggleSelection, schema, coloringScheme, onEdit }: ViewHiddenProps) {
const { colors, noNavigation } = useConceptOptions(); const { colors, calculateHeight } = useConceptOptions();
const windowSize = useWindowSize();
const dismissedHeight = useMemo(() => { const localSelected = useMemo(() => items.filter(id => selected.includes(id)), [items, selected]);
return !noNavigation ? 'calc(100vh - 28rem - 4px)' : 'calc(100vh - 22.2rem - 4px)'; const [isFolded, setIsFolded] = useLocalStorage(storage.rsgraphFoldHidden, false);
}, [noNavigation]);
const dismissedStyle = useCallback(
(cstID: ConstituentaID) => {
return selected.includes(cstID) ? { outlineWidth: '2px', outlineStyle: 'solid' } : {};
},
[selected]
);
if (!schema || items.length <= 0) { if (!schema || items.length <= 0) {
return null; return null;
} }
return ( return (
<div className='flex flex-col text-sm ml-2 border clr-app w-[12.5rem]'> <div className='flex flex-col'>
<p className='mt-2 text-center'> <Overlay position='right-[calc(0.5rem+1px)] top-2'>
<b>Скрытые конституенты</b> <MiniButton
</p> noPadding
<div className='flex flex-wrap justify-center gap-2 py-2 overflow-y-auto' style={{ maxHeight: dismissedHeight }}> noHover
title={!isFolded ? 'Свернуть' : 'Развернуть'}
icon={!isFolded ? <IconDropArrowUp size='1.25rem' /> : <IconDropArrow size='1.25rem' />}
onClick={() => setIsFolded(prev => !prev)}
/>
</Overlay>
<div
className={clsx(
'pt-2', //
'border-x',
'clr-input',
'select-none',
{
'pb-2 border-b': isFolded
}
)}
>
<motion.div
className='w-fit'
animate={!isFolded ? 'open' : 'closed'}
variants={animateHiddenHeader}
initial={false}
>{`Скрытые [${localSelected.length} | ${items.length}]`}</motion.div>
</div>
<motion.div
className={clsx(
'flex flex-wrap justify-center gap-2 py-2',
'border-x border-b rounded-b-md',
'clr-input',
'text-sm',
'overflow-y-auto'
)}
style={{ maxHeight: calculateHeight(windowSize.isSmall ? '12.rem + 2px' : '16.4rem + 2px') }}
initial={false}
animate={!isFolded ? 'open' : 'closed'}
variants={animateDropdown}
>
{items.map(cstID => { {items.map(cstID => {
const cst = schema.cstByID.get(cstID)!; const cst = schema.cstByID.get(cstID)!;
const adjustedColoring = coloringScheme === 'none' ? 'status' : coloringScheme; const adjustedColoring = coloringScheme === 'none' ? 'status' : coloringScheme;
@ -54,7 +91,7 @@ function ViewHidden({ items, selected, toggleSelection, schema, coloringScheme,
className='min-w-[3rem] rounded-md text-center cursor-pointer select-none' className='min-w-[3rem] rounded-md text-center cursor-pointer select-none'
style={{ style={{
backgroundColor: colorBgGraphNode(cst, adjustedColoring, colors), backgroundColor: colorBgGraphNode(cst, adjustedColoring, colors),
...dismissedStyle(cstID) ...(localSelected.includes(cstID) ? { outlineWidth: '2px', outlineStyle: 'solid' } : {})
}} }}
onClick={() => toggleSelection(cstID)} onClick={() => toggleSelection(cstID)}
onDoubleClick={() => onEdit(cstID)} onDoubleClick={() => onEdit(cstID)}
@ -65,7 +102,7 @@ function ViewHidden({ items, selected, toggleSelection, schema, coloringScheme,
</div> </div>
); );
})} })}
</div> </motion.div>
</div> </div>
); );
} }

View File

@ -74,6 +74,27 @@ export const animateSlideLeft: Variants = {
} }
}; };
export const animateHiddenHeader: Variants = {
open: {
translateX: 'calc(6.5rem - 50%)',
marginLeft: 0,
transition: {
type: 'spring',
bounce: 0,
duration: 0.3
}
},
closed: {
translateX: 0,
marginLeft: '0.75rem',
transition: {
type: 'spring',
bounce: 0,
duration: 0.3
}
}
};
export const animateDropdown: Variants = { export const animateDropdown: Variants = {
open: { open: {
clipPath: 'inset(0% 0% 0% 0%)', clipPath: 'inset(0% 0% 0% 0%)',

View File

@ -3,7 +3,7 @@
*/ */
import { GramData, Grammeme, NounGrams, PartOfSpeech, VerbGrams } from '@/models/language'; import { GramData, Grammeme, NounGrams, PartOfSpeech, VerbGrams } from '@/models/language';
import { GraphColoringScheme } from '@/models/miscellaneous'; import { GraphColoring } from '@/models/miscellaneous';
import { CstClass, ExpressionStatus, IConstituenta } from '@/models/rsform'; import { CstClass, ExpressionStatus, IConstituenta } from '@/models/rsform';
import { ISyntaxTreeNode, TokenID } from '@/models/rslang'; import { ISyntaxTreeNode, TokenID } from '@/models/rslang';
@ -458,7 +458,7 @@ export function colorFgGrammeme(gram: GramData, colors: IColorTheme): string {
/** /**
* Determines graph color for {@link IConstituenta}. * Determines graph color for {@link IConstituenta}.
*/ */
export function colorBgGraphNode(cst: IConstituenta, coloringScheme: GraphColoringScheme, colors: IColorTheme): string { export function colorBgGraphNode(cst: IConstituenta, coloringScheme: GraphColoring, colors: IColorTheme): string {
if (coloringScheme === 'type') { if (coloringScheme === 'type') {
return colorBgCstClass(cst.cst_class, colors); return colorBgCstClass(cst.cst_class, colors);
} }

View File

@ -91,7 +91,9 @@ export const storage = {
rsgraphFilter: 'rsgraph.filter_options', rsgraphFilter: 'rsgraph.filter_options',
rsgraphLayout: 'rsgraph.layout', rsgraphLayout: 'rsgraph.layout',
rsgraphColoringScheme: 'rsgraph.coloring_scheme', rsgraphColoring: 'rsgraph.coloring',
rsgraphSizing: 'rsgraph.sizing',
rsgraphFoldHidden: 'rsgraph.fold_hidden',
cstFilterMatch: 'cst.filter.match', cstFilterMatch: 'cst.filter.match',
cstFilterGraph: 'cst.filter.graph' cstFilterGraph: 'cst.filter.graph'

View File

@ -4,8 +4,17 @@
* Label is a short text used to represent an entity. * Label is a short text used to represent an entity.
* Description is a long description used in tooltips. * Description is a long description used in tooltips.
*/ */
import { GraphLayout } from '@/components/ui/GraphUI';
import { GramData, Grammeme, ReferenceType } from '@/models/language'; import { GramData, Grammeme, ReferenceType } from '@/models/language';
import { CstMatchMode, DependencyMode, HelpTopic, LibraryFilterStrategy, UserAccessMode } from '@/models/miscellaneous'; import {
CstMatchMode,
DependencyMode,
GraphColoring,
GraphSizing,
HelpTopic,
LibraryFilterStrategy,
UserAccessMode
} from '@/models/miscellaneous';
import { CstClass, CstType, ExpressionStatus, IConstituenta, IRSForm } from '@/models/rsform'; import { CstClass, CstType, ExpressionStatus, IConstituenta, IRSForm } from '@/models/rsform';
import { import {
IArgumentInfo, IArgumentInfo,
@ -284,29 +293,35 @@ export function describeLibraryFilter(strategy: LibraryFilterStrategy): string {
/** /**
* Retrieves label for graph layout mode. * Retrieves label for graph layout mode.
*/ */
export const mapLabelLayout: Map<string, string> = new Map([ export const mapLabelLayout: Map<GraphLayout, string> = new Map([
['treeTd2d', 'Граф: ДеревоВ 2D'],
['treeTd3d', 'Граф: ДеревоВ 3D'],
['forceatlas2', 'Граф: Атлас 2D'], ['forceatlas2', 'Граф: Атлас 2D'],
['forceDirected2d', 'Граф: Силы 2D'], ['forceDirected2d', 'Граф: Силы 2D'],
['forceDirected3d', 'Граф: Силы 3D'], ['forceDirected3d', 'Граф: Силы 3D'],
['treeTd2d', 'Граф: ДеревоВер 2D'], ['treeLr2d', 'Граф: ДеревоГ 2D'],
['treeTd3d', 'Граф: ДеревоВер 3D'], ['treeLr3d', 'Граф: ДеревоГ 3D'],
['treeLr2d', 'Граф: ДеревоГор 2D'],
['treeLr3d', 'Граф: ДеревоГор 3D'],
['radialOut2d', 'Граф: Радиус 2D'], ['radialOut2d', 'Граф: Радиус 2D'],
['radialOut3d', 'Граф: Радиус 3D'], ['radialOut3d', 'Граф: Радиус 3D'],
['circular2d', 'Граф: Круговая'], ['circular2d', 'Граф: Круговая']
['hierarchicalTd', 'Граф: ИерархияВер'],
['hierarchicalLr', 'Граф: ИерархияГор'],
['nooverlap', 'Граф: Без перекрытия']
]); ]);
/** /**
* Retrieves label for graph coloring mode. * Retrieves label for {@link GraphColoring}.
*/ */
export const mapLabelColoring: Map<string, string> = new Map([ export const mapLabelColoring: Map<GraphColoring, string> = new Map([
['none', 'Цвет: моно'], ['none', 'Цвет: Моно'],
['status', 'Цвет: статус'], ['status', 'Цвет: Статус'],
['type', 'Цвет: класс'] ['type', 'Цвет: Класс']
]);
/**
* Retrieves label for {@link GraphSizing}.
*/
export const mapLabelSizing: Map<GraphSizing, string> = new Map([
['none', 'Узлы: Моно'],
['derived', 'Узлы: Порожденные'],
['complex', 'Узлы: Простые']
]); ]);
/** /**

View File

@ -1,43 +1,32 @@
/** /**
* Module: Mappings for selector UI elements. Do not confuse with html selectors * Module: Mappings for selector UI elements. Do not confuse with html selectors
*/ */
import { LayoutTypes } from 'reagraph';
import { GraphLayout } from '@/components/ui/GraphUI';
import { type GramData, Grammeme, ReferenceType } from '@/models/language'; import { type GramData, Grammeme, ReferenceType } from '@/models/language';
import { grammemeCompare } from '@/models/languageAPI'; import { grammemeCompare } from '@/models/languageAPI';
import { GraphColoringScheme } from '@/models/miscellaneous'; import { GraphColoring, GraphSizing } from '@/models/miscellaneous';
import { CstType } from '@/models/rsform'; import { CstType } from '@/models/rsform';
import { labelGrammeme, labelReferenceType } from './labels'; import { labelGrammeme, labelReferenceType, mapLabelColoring, mapLabelLayout, mapLabelSizing } from './labels';
import { labelCstType } from './labels'; import { labelCstType } from './labels';
/** /**
* Represents options for GraphLayout selector. * Represents options for GraphLayout selector.
*/ */
export const SelectorGraphLayout: { value: LayoutTypes; label: string }[] = [ export const SelectorGraphLayout: { value: GraphLayout; label: string }[] = //
{ value: 'treeTd2d', label: 'Граф: ДеревоВ 2D' }, [...mapLabelLayout.entries()].map(item => ({ value: item[0], label: item[1] }));
{ value: 'treeTd3d', label: 'Граф: ДеревоВ 3D' }, /**
{ value: 'forceatlas2', label: 'Граф: Атлас 2D' }, * Represents options for {@link GraphColoring} selector.
{ value: 'forceDirected2d', label: 'Граф: Силы 2D' }, */
{ value: 'forceDirected3d', label: 'Граф: Силы 3D' }, export const SelectorGraphColoring: { value: GraphColoring; label: string }[] = //
{ value: 'treeLr2d', label: 'Граф: ДеревоГ 2D' }, [...mapLabelColoring.entries()].map(item => ({ value: item[0], label: item[1] }));
{ value: 'treeLr3d', label: 'Граф: ДеревоГ 3D' },
{ value: 'radialOut2d', label: 'Граф: Радиус 2D' },
{ value: 'radialOut3d', label: 'Граф: Радиус 3D' }
// { value: 'circular2d', label: 'circular2d'},
// { value: 'nooverlap', label: 'nooverlap'},
// { value: 'hierarchicalTd', label: 'hierarchicalTd'},
// { value: 'hierarchicalLr', label: 'hierarchicalLr'}
];
/** /**
* Represents options for {@link GraphColoringScheme} selector. * Represents options for {@link GraphSizing} selector.
*/ */
export const SelectorGraphColoring: { value: GraphColoringScheme; label: string }[] = [ export const SelectorGraphSizing: { value: GraphSizing; label: string }[] = //
{ value: 'none', label: 'Цвет: моно' }, [...mapLabelSizing.entries()].map(item => ({ value: item[0], label: item[1] }));
{ value: 'status', label: 'Цвет: статус' },
{ value: 'type', label: 'Цвет: класс' }
];
/** /**
* Represents options for {@link CstType} selector. * Represents options for {@link CstType} selector.