Rework graph UI pt1

This commit is contained in:
IRBorisov 2024-04-03 15:51:57 +03:00
parent 3ee7e110cf
commit c1f69f23e8
14 changed files with 202 additions and 151 deletions

View File

@ -133,6 +133,7 @@
"компаратив",
"конституент",
"Конституента",
"конституентами",
"конституенту",
"конституенты",
"неинтерпретируемый",

View File

@ -23,7 +23,7 @@ function HelpTermGraph() {
<div className='dense'>
<h1>Клавиши</h1>
<p><b>Клик на конституенту</b> - выделение</p>
<p><b>Клик на выделенную</b> - редактирование</p>
<p><b>Двойной клик</b> - редактирование</p>
<p><b>Delete</b> - удалить выбранные</p>
<br />

View File

@ -45,7 +45,7 @@ function SelectorButton({
{...restProps}
>
{icon ? icon : null}
{text ? <div className={'whitespace-nowrap pb-1'}>{text}</div> : null}
{text ? <div className={'whitespace-nowrap'}>{text}</div> : null}
</button>
);
}

View File

@ -69,6 +69,7 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
}}
>
<GraphUI
animated={false}
nodes={nodes}
edges={edges}
layoutType='hierarchicalTd'

View File

@ -13,31 +13,29 @@ import RSListToolbar from './RSListToolbar';
import RSTable from './RSTable';
interface EditorRSListProps {
selected: ConstituentaID[];
setSelected: React.Dispatch<React.SetStateAction<ConstituentaID[]>>;
onOpenEdit: (cstID: ConstituentaID) => void;
}
function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps) {
function EditorRSList({ onOpenEdit }: EditorRSListProps) {
const { calculateHeight } = useConceptOptions();
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const controller = useRSEdit();
useLayoutEffect(() => {
if (!controller.schema || selected.length === 0) {
if (!controller.schema || controller.selected.length === 0) {
setRowSelection({});
} else {
const newRowSelection: RowSelectionState = {};
controller.schema.items.forEach((cst, index) => {
newRowSelection[String(index)] = selected.includes(cst.id);
newRowSelection[String(index)] = controller.selected.includes(cst.id);
});
setRowSelection(newRowSelection);
}
}, [selected, controller.schema]);
}, [controller.selected, controller.schema]);
function handleRowSelection(updater: React.SetStateAction<RowSelectionState>) {
if (!controller.schema) {
setSelected([]);
controller.deselectAll();
} else {
const newRowSelection = typeof updater === 'function' ? updater(rowSelection) : updater;
const newSelection: ConstituentaID[] = [];
@ -46,7 +44,7 @@ function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps)
newSelection.push(cst.id);
}
});
setSelected(newSelection);
controller.setSelection(newSelection);
}
}
@ -54,7 +52,7 @@ function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps)
if (!controller.isContentEditable || controller.isProcessing) {
return;
}
if (event.key === 'Delete' && selected.length > 0) {
if (event.key === 'Delete' && controller.selected.length > 0) {
event.preventDefault();
controller.deleteCst();
return;
@ -69,7 +67,7 @@ function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps)
}
function processAltKey(code: string): boolean {
if (selected.length > 0) {
if (controller.selected.length > 0) {
// prettier-ignore
switch (code) {
case 'ArrowUp': controller.moveUp(); return true;
@ -100,12 +98,12 @@ function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps)
{controller.isContentEditable ? (
<SelectedCounter
totalCount={controller.schema?.stats?.count_all ?? 0}
selectedCount={selected.length}
selectedCount={controller.selected.length}
position='top-[0.3rem] left-2'
/>
) : null}
{controller.isContentEditable ? <RSListToolbar selectedCount={selected.length} /> : null}
{controller.isContentEditable ? <RSListToolbar /> : null}
<div
className={clsx('border-b', {
'pt-[2.3rem]': controller.isContentEditable,

View File

@ -1,6 +1,3 @@
'use client';
import { useMemo } from 'react';
import { BiDownArrowCircle, BiDownvote, BiDuplicate, BiPlusCircle, BiTrash, BiUpvote } from 'react-icons/bi';
import BadgeHelp from '@/components/man/BadgeHelp';
@ -17,33 +14,28 @@ import { getCstTypeShortcut, labelCstType, prepareTooltip } from '@/utils/labels
import { useRSEdit } from '../RSEditContext';
interface RSListToolbarProps {
selectedCount: number;
}
function RSListToolbar({ selectedCount }: RSListToolbarProps) {
function RSListToolbar() {
const controller = useRSEdit();
const insertMenu = useDropdown();
const nothingSelected = useMemo(() => selectedCount === 0, [selectedCount]);
return (
<Overlay position='top-1 right-1/2 translate-x-1/2' className='flex items-start'>
<MiniButton
titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')}
icon={<BiUpvote size='1.25rem' className='icon-primary' />}
disabled={controller.isProcessing || nothingSelected}
disabled={controller.isProcessing || controller.nothingSelected}
onClick={controller.moveUp}
/>
<MiniButton
titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')}
icon={<BiDownvote size='1.25rem' className='icon-primary' />}
disabled={controller.isProcessing || nothingSelected}
disabled={controller.isProcessing || controller.nothingSelected}
onClick={controller.moveDown}
/>
<MiniButton
titleHtml={prepareTooltip('Клонировать конституенту', 'Alt + V')}
icon={<BiDuplicate size='1.25rem' className='icon-green' />}
disabled={controller.isProcessing || selectedCount !== 1}
disabled={controller.isProcessing || controller.selected.length !== 1}
onClick={controller.cloneCst}
/>
<MiniButton
@ -74,7 +66,7 @@ function RSListToolbar({ selectedCount }: RSListToolbarProps) {
<MiniButton
titleHtml={prepareTooltip('Удалить выбранные', 'Delete')}
icon={<BiTrash size='1.25rem' className='icon-red' />}
disabled={controller.isProcessing || nothingSelected}
disabled={controller.isProcessing || controller.nothingSelected}
onClick={controller.deleteCst}
/>
<BadgeHelp topic={HelpTopic.CST_LIST} offset={5} />

View File

@ -17,6 +17,7 @@ import { colorBgGraphNode } from '@/styling/color';
import { storage, TIMEOUT_GRAPH_REFRESH } from '@/utils/constants';
import { useRSEdit } from '../RSEditContext';
import GraphSelectors from './GraphSelectors';
import GraphSidebar from './GraphSidebar';
import GraphToolbar from './GraphToolbar';
import TermGraph from './TermGraph';
@ -24,12 +25,10 @@ import useGraphFilter from './useGraphFilter';
import ViewHidden from './ViewHidden';
interface EditorTermGraphProps {
selected: ConstituentaID[];
setSelected: React.Dispatch<React.SetStateAction<ConstituentaID[]>>;
onOpenEdit: (cstID: ConstituentaID) => void;
}
function EditorTermGraph({ selected, setSelected, onOpenEdit }: EditorTermGraphProps) {
function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
const controller = useRSEdit();
const { colors } = useConceptOptions();
@ -53,8 +52,6 @@ function EditorTermGraph({ selected, setSelected, onOpenEdit }: EditorTermGraphP
const [hidden, setHidden] = useState<ConstituentaID[]>([]);
const nothingSelected = useMemo(() => selected.length === 0, [selected]);
const [layout, setLayout] = useLocalStorage<LayoutTypes>(storage.rsgraphLayout, 'treeTd2d');
const [coloringScheme, setColoringScheme] = useLocalStorage<GraphColoringScheme>(
storage.rsgraphColoringScheme,
@ -118,33 +115,18 @@ function EditorTermGraph({ selected, setSelected, onOpenEdit }: EditorTermGraphP
return result;
}, [filtered.nodes]);
const handleGraphSelection = useCallback(
(newID: ConstituentaID) => {
setSelected(prev => [...prev, newID]);
},
[setSelected]
);
function toggleDismissed(cstID: ConstituentaID) {
setSelected(prev => {
if (prev.includes(cstID)) {
return [...prev.filter(id => id !== cstID)];
} else {
return [...prev, cstID];
}
});
}
function handleCreateCst() {
if (!controller.schema) {
return;
}
const definition = selected.map(id => controller.schema!.items.find(cst => cst.id === id)!.alias).join(' ');
controller.createCst(selected.length === 0 ? CstType.BASE : CstType.TERM, false, definition);
const definition = controller.selected
.map(id => controller.schema!.items.find(cst => cst.id === id)!.alias)
.join(' ');
controller.createCst(controller.nothingSelected ? CstType.BASE : CstType.TERM, false, definition);
}
function handleDeleteCst() {
if (!controller.schema || selected.length === 0) {
if (!controller.schema || controller.nothingSelected) {
return;
}
controller.deleteCst();
@ -178,6 +160,37 @@ function EditorTermGraph({ selected, setSelected, onOpenEdit }: EditorTermGraphP
}
}
const graph = useMemo(
() => (
<TermGraph
nodes={nodes}
edges={edges}
selectedIDs={controller.selected}
layout={layout}
is3D={is3D}
orbit={orbit}
onSelect={controller.select}
onDeselect={controller.deselect}
setHoverID={setHoverID}
onEdit={onOpenEdit}
toggleResetView={toggleResetView}
/>
),
[
edges,
nodes,
controller.selected,
layout,
is3D,
orbit,
setHoverID,
onOpenEdit,
toggleResetView,
controller.select,
controller.deselect
]
);
return (
<div tabIndex={-1} onKeyDown={handleKeyDown}>
<AnimatePresence>
@ -193,12 +206,11 @@ function EditorTermGraph({ selected, setSelected, onOpenEdit }: EditorTermGraphP
<SelectedCounter
hideZero
totalCount={controller.schema?.stats?.count_all ?? 0}
selectedCount={selected.length}
selectedCount={controller.selected.length}
position='top-[0.3rem] left-0'
/>
<GraphToolbar
nothingSelected={nothingSelected}
is3D={is3D}
orbit={orbit}
noText={filterParams.noText}
@ -225,36 +237,27 @@ function EditorTermGraph({ selected, setSelected, onOpenEdit }: EditorTermGraphP
</Overlay>
) : null}
<Overlay position='top-0 left-0' className='cc-column w-[13.5rem]'>
<GraphSidebar
coloring={coloringScheme}
layout={layout}
setLayout={handleChangeLayout}
setColoring={setColoringScheme}
/>
<ViewHidden
items={hidden}
selected={selected}
schema={controller.schema}
coloringScheme={coloringScheme}
toggleSelection={toggleDismissed}
onEdit={onOpenEdit}
/>
<Overlay position='top-9 left-0' className='flex gap-1'>
<div className='cc-column w-[13.5rem]'>
<GraphSelectors
coloring={coloringScheme}
layout={layout}
setLayout={handleChangeLayout}
setColoring={setColoringScheme}
/>
<ViewHidden
items={hidden}
selected={controller.selected}
schema={controller.schema}
coloringScheme={coloringScheme}
toggleSelection={controller.toggleSelect}
onEdit={onOpenEdit}
/>
</div>
<GraphSidebar />
</Overlay>
<TermGraph
nodes={nodes}
edges={edges}
selectedIDs={selected}
layout={layout}
is3D={is3D}
orbit={orbit}
onSelect={handleGraphSelection}
setHoverID={setHoverID}
onEdit={onOpenEdit}
onDeselectAll={() => setSelected([])}
toggleResetView={toggleResetView}
/>
{graph}
</div>
);
}

View File

@ -0,0 +1,37 @@
import { LayoutTypes } from 'reagraph';
import SelectSingle from '@/components/ui/SelectSingle';
import { GraphColoringScheme } from '@/models/miscellaneous';
import { mapLabelColoring, mapLabelLayout } from '@/utils/labels';
import { SelectorGraphColoring, SelectorGraphLayout } from '@/utils/selectors';
interface GraphSelectorsProps {
coloring: GraphColoringScheme;
layout: LayoutTypes;
setLayout: (newValue: LayoutTypes) => void;
setColoring: (newValue: GraphColoringScheme) => void;
}
function GraphSelectors({ coloring, setColoring, layout, setLayout }: GraphSelectorsProps) {
return (
<div className='px-2 text-sm select-none'>
<SelectSingle
placeholder='Выберите цвет'
options={SelectorGraphColoring}
isSearchable={false}
value={coloring ? { value: coloring, label: mapLabelColoring.get(coloring) } : null}
onChange={data => setColoring(data?.value ?? SelectorGraphColoring[0].value)}
/>
<SelectSingle
placeholder='Способ расположения'
options={SelectorGraphLayout}
isSearchable={false}
value={layout ? { value: layout, label: mapLabelLayout.get(layout) } : null}
onChange={data => setLayout(data?.value ?? SelectorGraphLayout[0].value)}
/>
</div>
);
}
export default GraphSelectors;

View File

@ -1,34 +1,44 @@
import { LayoutTypes } from 'reagraph';
import { BiGitBranch, BiGitMerge, BiReset } from 'react-icons/bi';
import { LuExpand, LuMaximize, LuMinimize } from 'react-icons/lu';
import SelectSingle from '@/components/ui/SelectSingle';
import { GraphColoringScheme } from '@/models/miscellaneous';
import { mapLabelColoring, mapLabelLayout } from '@/utils/labels';
import { SelectorGraphColoring, SelectorGraphLayout } from '@/utils/selectors';
import MiniButton from '@/components/ui/MiniButton';
interface GraphSidebarProps {
coloring: GraphColoringScheme;
layout: LayoutTypes;
import { useRSEdit } from '../RSEditContext';
setLayout: (newValue: LayoutTypes) => void;
setColoring: (newValue: GraphColoringScheme) => void;
}
function GraphSidebar() {
const controller = useRSEdit();
function GraphSidebar({ coloring, setColoring, layout, setLayout }: GraphSidebarProps) {
return (
<div className='px-2 text-sm select-none mt-9'>
<SelectSingle
placeholder='Выберите цвет'
options={SelectorGraphColoring}
isSearchable={false}
value={coloring ? { value: coloring, label: mapLabelColoring.get(coloring) } : null}
onChange={data => setColoring(data?.value ?? SelectorGraphColoring[0].value)}
<div className='flex flex-col gap-1 clr-app'>
<MiniButton
titleHtml='<b>Сбросить выделение</b>'
icon={<BiReset size='1.25rem' className='icon-primary' />}
onClick={controller.deselectAll}
/>
<SelectSingle
placeholder='Способ расположения'
options={SelectorGraphLayout}
isSearchable={false}
value={layout ? { value: layout, label: mapLabelLayout.get(layout) } : null}
onChange={data => setLayout(data?.value ?? SelectorGraphLayout[0].value)}
<MiniButton
titleHtml='<b>Выделение базиса</b> - замыкание выделения влияющими конституентами'
icon={<LuMinimize size='1.25rem' className='icon-primary' />}
disabled={controller.nothingSelected}
/>
<MiniButton
titleHtml='<b>Максимизация части</b> - замыкание выделения конституентами, зависимыми только от выделенных'
icon={<LuMaximize size='1.25rem' className='icon-primary' />}
disabled={controller.nothingSelected}
/>
<MiniButton
title='Выделить все зависимые'
icon={<LuExpand size='1.25rem' className='icon-primary' />}
disabled={controller.nothingSelected}
/>
<MiniButton
title='Выделить поставщиков'
icon={<BiGitBranch size='1.25rem' className='icon-primary' />}
disabled={controller.nothingSelected}
/>
<MiniButton
title='Выделить потребителей'
icon={<BiGitMerge size='1.25rem' className='icon-primary' />}
disabled={controller.nothingSelected}
/>
</div>
);

View File

@ -1,6 +1,7 @@
'use client';
import { BiCollapse, BiFilterAlt, BiFont, BiFontFamily, BiPlanet, BiPlusCircle, BiTrash } from 'react-icons/bi';
import { BiFilterAlt, BiFont, BiFontFamily, BiPlanet, BiPlusCircle, BiTrash } from 'react-icons/bi';
import { LuImage } from 'react-icons/lu';
import BadgeHelp from '@/components/man/BadgeHelp';
import MiniButton from '@/components/ui/MiniButton';
@ -10,7 +11,6 @@ import { HelpTopic } from '@/models/miscellaneous';
import { useRSEdit } from '../RSEditContext';
interface GraphToolbarProps {
nothingSelected: boolean;
is3D: boolean;
orbit: boolean;
@ -26,7 +26,6 @@ interface GraphToolbarProps {
}
function GraphToolbar({
nothingSelected,
is3D,
noText,
toggleNoText,
@ -40,7 +39,7 @@ function GraphToolbar({
const controller = useRSEdit();
return (
<Overlay position='top-1 right-1/2 translate-x-1/2' className='flex'>
<Overlay position='top-0 pt-1 right-1/2 translate-x-1/2 clr-app' className='flex'>
<MiniButton
title='Настройки фильтрации узлов и связей'
icon={<BiFilterAlt size='1.25rem' className='icon-primary' />}
@ -58,7 +57,7 @@ function GraphToolbar({
onClick={toggleNoText}
/>
<MiniButton
icon={<BiCollapse size='1.25rem' className='icon-primary' />}
icon={<LuImage size='1.25rem' className='icon-primary' />}
title='Восстановить камеру'
onClick={onResetViewpoint}
/>
@ -80,7 +79,7 @@ function GraphToolbar({
<MiniButton
title='Удалить выбранные'
icon={<BiTrash size='1.25rem' className='icon-red' />}
disabled={nothingSelected || controller.isProcessing}
disabled={controller.nothingSelected || controller.isProcessing}
onClick={onDelete}
/>
) : null}

View File

@ -20,7 +20,7 @@ interface TermGraphProps {
setHoverID: (newID: ConstituentaID | undefined) => void;
onEdit: (cstID: ConstituentaID) => void;
onSelect: (newID: ConstituentaID) => void;
onDeselectAll: () => void;
onDeselect: (newID: ConstituentaID) => void;
toggleResetView: boolean;
}
@ -38,54 +38,45 @@ function TermGraph({
setHoverID,
onEdit,
onSelect,
onDeselectAll
onDeselect
}: TermGraphProps) {
const { calculateHeight, darkMode } = useConceptOptions();
const graphRef = useRef<GraphCanvasRef | null>(null);
const { selections, actives, setSelections, onCanvasClick, onNodePointerOver, onNodePointerOut } = useSelection({
const { selections, setSelections } = useSelection({
ref: graphRef,
nodes,
edges,
type: 'multi', // 'single' | 'multi' | 'multiModifier'
pathSelectionType: 'out',
pathHoverType: 'all',
focusOnSelect: false
type: 'multi'
});
const handleHoverIn = useCallback(
(node: GraphNode) => {
setHoverID(Number(node.id));
if (onNodePointerOver) onNodePointerOver(node);
},
[onNodePointerOver, setHoverID]
[setHoverID]
);
const handleHoverOut = useCallback(
(node: GraphNode) => {
setHoverID(undefined);
if (onNodePointerOut) onNodePointerOut(node);
},
[onNodePointerOut, setHoverID]
);
const handleHoverOut = useCallback(() => {
setHoverID(undefined);
}, [setHoverID]);
const handleNodeClick = useCallback(
(node: GraphNode) => {
if (selections.includes(node.id)) {
onEdit(Number(node.id));
onDeselect(Number(node.id));
} else {
onSelect(Number(node.id));
}
},
[onSelect, selections, onEdit]
[onSelect, selections, onDeselect]
);
const handleCanvasClick = useCallback(
(event: MouseEvent) => {
onDeselectAll();
if (onCanvasClick) onCanvasClick(event);
const handleNodeDoubleClick = useCallback(
(node: GraphNode) => {
onEdit(Number(node.id));
},
[onCanvasClick, onDeselectAll]
[onEdit]
);
useLayoutEffect(() => {
@ -108,15 +99,15 @@ function TermGraph({
<div className='outline-none'>
<div className='relative' style={{ width: canvasWidth, height: canvasHeight }}>
<GraphUI
draggable
ref={graphRef}
nodes={nodes}
edges={edges}
ref={graphRef}
animated={false}
draggable
layoutType={layout}
selections={selections}
actives={actives}
onNodeDoubleClick={handleNodeDoubleClick}
onNodeClick={handleNodeClick}
onCanvasClick={handleCanvasClick}
onNodePointerOver={handleHoverIn}
onNodePointerOut={handleHoverOut}
cameraMode={orbit ? 'orbit' : is3D ? 'rotate' : 'pan'}

View File

@ -48,12 +48,23 @@ import { EXTEOR_TRS_FILE } from '@/utils/constants';
interface IRSEditContext {
schema?: IRSForm;
selected: ConstituentaID[];
isMutable: boolean;
isContentEditable: boolean;
isProcessing: boolean;
canProduceStructure: boolean;
nothingSelected: boolean;
setSelection: (selected: ConstituentaID[]) => void;
select: (target: ConstituentaID) => void;
deselect: (target: ConstituentaID) => void;
toggleSelect: (target: ConstituentaID) => void;
deselectAll: () => void;
viewVersion: (version?: number) => void;
createVersion: () => void;
editVersions: () => void;
moveUp: () => void;
moveDown: () => void;
@ -70,13 +81,11 @@ interface IRSEditContext {
share: () => void;
toggleSubscribe: () => void;
download: () => void;
reindex: () => void;
produceStructure: () => void;
inlineSynthesis: () => void;
substitute: () => void;
createVersion: () => void;
editVersions: () => void;
}
const RSEditContext = createContext<IRSEditContext | null>(null);
@ -119,6 +128,7 @@ export const RSEditState = ({
);
}, [user?.is_staff, mode, model.isOwned]);
const isContentEditable = useMemo(() => isMutable && !model.isArchive, [isMutable, model.isArchive]);
const nothingSelected = useMemo(() => selected.length === 0, [selected]);
const [showUpload, setShowUpload] = useState(false);
const [showClone, setShowClone] = useState(false);
@ -464,12 +474,23 @@ export const RSEditState = ({
<RSEditContext.Provider
value={{
schema: model.schema,
selected,
isMutable,
isContentEditable,
isProcessing: model.processing,
canProduceStructure,
nothingSelected,
setSelection: (selected: ConstituentaID[]) => setSelected(selected),
select: (target: ConstituentaID) => setSelected(prev => [...prev, target]),
deselect: (target: ConstituentaID) => setSelected(prev => prev.filter(id => id !== target)),
toggleSelect: (target: ConstituentaID) =>
setSelected(prev => (prev.includes(target) ? prev.filter(id => id !== target) : [...prev, target])),
deselectAll: () => setSelected([]),
viewVersion,
createVersion: () => setShowCreateVersion(true),
editVersions: () => setShowEditVersions(true),
moveUp,
moveDown,
@ -486,13 +507,11 @@ export const RSEditState = ({
claim,
share,
toggleSubscribe,
reindex,
inlineSynthesis: () => setShowInlineSynthesis(true),
produceStructure,
substitute,
createVersion: () => setShowCreateVersion(true),
editVersions: () => setShowEditVersions(true)
substitute
}}
>
{model.schema ? (

View File

@ -204,7 +204,7 @@ function RSTabs() {
</TabPanel>
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_LIST ? '' : 'none' }}>
<EditorRSList selected={selected} setSelected={setSelected} onOpenEdit={onOpenCst} />
<EditorRSList onOpenEdit={onOpenCst} />
</TabPanel>
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_EDIT ? '' : 'none' }}>
@ -217,7 +217,7 @@ function RSTabs() {
</TabPanel>
<TabPanel style={{ display: activeTab === RSTabID.TERM_GRAPH ? '' : 'none' }}>
<EditorTermGraph selected={selected} setSelected={setSelected} onOpenEdit={onOpenCst} />
<EditorTermGraph onOpenEdit={onOpenCst} />
</TabPanel>
</AnimateFade>
</Tabs>

View File

@ -179,7 +179,7 @@ export const graphLightT = {
activeFill: '#1DE9AC',
opacity: 1,
selectedOpacity: 1,
inactiveOpacity: 0.2,
inactiveOpacity: 0.5,
label: {
color: '#2A6475',
stroke: '#fff',
@ -231,7 +231,7 @@ export const graphDarkT = {
activeFill: '#1DE9AC',
opacity: 1,
selectedOpacity: 1,
inactiveOpacity: 0.2,
inactiveOpacity: 0.5,
label: {
stroke: '#1E2026',
color: '#ACBAC7',