fbd-editor/src/components/Toolbar.jsx
2026-05-24 07:07:27 +03:00

138 lines
5.6 KiB
JavaScript

import React, { useState, useMemo, useLayoutEffect, useRef } from 'react'
import {ToolbarBlock} from './ToolbarElements'
const NAME_CATEGORIES = {
logical: 'Логические',
arithmetic: 'Арифметические',
timers: 'Таймеры',
counters: 'Счётчики',
comparisons: 'Сравнения',
other: 'Другие',
data: 'Данные',
customized: 'Пользовательские'
};
const BLOCK_CATEGORIES = {
logical: [
{ type: 'and', label: 'AND', inputs: ['BOOL', 'BOOL'], outputs: ['BOOL'] },
{ type: 'or', label: 'OR', inputs: ['BOOL', 'BOOL'], outputs: ['BOOL'] },
{ type: 'not', label: 'NOT', inputs: ['BOOL'], outputs: ['BOOL'] },
{ type: 'xor', label: 'XOR', inputs: ['BOOL', 'BOOL'], outputs: ['BOOL'] }],
arithmetic: [
{ type: 'add', label: 'ADD', inputs: ['$/INT/DINT/REAL', '$/INT/DINT/REAL'], outputs: ['$/INT/DINT/REAL'] },
{ type: 'sub', label: 'SUB', inputs: ['$/INT/DINT/REAL', '$/INT/DINT/REAL'], outputs: ['$/INT/DINT/REAL'] },
{ type: 'mul', label: 'MUL', inputs: ['$/INT/DINT/REAL', '$/INT/DINT/REAL'], outputs: ['$/INT/DINT/REAL'] },
{ type: 'div', label: 'DIV', inputs: ['$/INT/DINT/REAL', '$/INT/DINT/REAL'], outputs: ['$/INT/DINT/REAL'] }],
timers: [
{ type: 'ton', label: 'TON', inputs: ['BOOL', 'TIME'], outputs: ['BOOL', 'TIME'] },
{ type: 'tof', label: 'TOF', inputs: ['BOOL', 'TIME'], outputs: ['BOOL', 'TIME'] },
{ type: 'tp', label: 'TP', inputs: ['BOOL', 'TIME'], outputs: ['BOOL', 'TIME'] }],
counters: [
{ type: 'ctu', label: 'CTU', inputs: ['BOOL', 'BOOL', '$/INT/DINT'], outputs: ['BOOL', '$/INT/DINT'] },
{ type: 'ctd', label: 'CTD', inputs: ['BOOL', 'BOOL', '$/INT/DINT'], outputs: ['BOOL', '$/INT/DINT'] },
{ type: 'ctud', label: 'CTUD', inputs: ['BOOL', 'BOOL', 'BOOL', 'BOOL', '$/INT/DINT'], outputs: ['BOOL', 'BOOL', '$/INT/DINT'] }],
comparisons: [
{ type: 'gt', label: '>', inputs: ['$/INT/DINT/REAL/TIME', '$/INT/DINT/REAL/TIME'], outputs: ['BOOL'] },
{ type: 'lt', label: '<', inputs: ['$/INT/DINT/REAL/TIME', '$/INT/DINT/REAL/TIME'], outputs: ['BOOL'] },
{ type: 'eq', label: '=', inputs: ['$/BOOl/INT/DINT/REAL/TIME/STRING', '$/BOOl/INT/DINT/REAL/TIME/STRING'], outputs: ['BOOL'] }],
other: [
{ type: 'sr', label: 'SR', inputs: ['BOOL', 'BOOL'], outputs: ['BOOL'] },
{ type: 'rs', label: 'RS', inputs: ['BOOL', 'BOOL'], outputs: ['BOOL'] },
{ type: 'move', label: 'MOVE', inputs: ['$/BOOl/INT/DINT/REAL/TIME/STRING'], outputs: ['$/BOOl/INT/DINT/REAL/TIME/STRING'] }],
data: [
{ type: 'input', label: 'input', inputs: [], outputs: ['BOOl/INT/DINT/REAL/TIME/STRING'] },
{ type: 'output', label: 'output', inputs: ['BOOl/INT/DINT/REAL/TIME/STRING'], outputs: [] },
{ type: 'const', label: 'const', inputs: [], outputs: ['BOOl/INT/DINT/REAL/TIME/STRING'] },
{ type: 'switch_type', label: '*_TO_**', inputs: ['BOOl/INT/DINT/REAL/TIME/STRING'], outputs: ['BOOl/INT/DINT/REAL/TIME/STRING'] }
]
// customized: []
}
const LINES_TYPES = [
{ type: 'bool', stroke: '#000', dasharray: '0' },
{ type: 'a', stroke: '#000', dasharray: '4 4' }, // Пунктир
{ type: 'b', stroke: '#D8000A', dasharray: '0' }, // Красная
{ type: 'c', stroke: '#000', dasharray: '1 3' }, // Точечная
{ type: 'd', stroke: '#008000', dasharray: '0' }, // Зеленая
{ type: 'e', stroke: '#1717D8', dasharray: '0' } // Синяя
];
const BLOCK_SCALE = 1
function Toolbar() {
const [activeTab, setActiveTab] = useState('logical')
const containerRef = useRef(null)
const [caseWidth, setCaseWidth] = useState(0)
const tabs = useMemo(() => Object.keys(BLOCK_CATEGORIES), [])
const activeBlocks = BLOCK_CATEGORIES[activeTab] || []
useLayoutEffect(() => {
if (!containerRef.current)
return
function updateSize() {
const rect = containerRef.current.getBoundingClientRect()
setCaseWidth(rect.width / 2)
}
updateSize()
const observer = new ResizeObserver(() => updateSize())
observer.observe(containerRef.current)
return () => observer.disconnect()
}, [])
function onDragStart(event, block) {
const offsetX = event.nativeEvent.offsetX * BLOCK_SCALE
const offsetY = event.nativeEvent.offsetY * BLOCK_SCALE
event.dataTransfer.setData('application/reactflow', JSON.stringify({
type: block.type,
label: block.label,
inputs: block.inputs,
outputs: block.outputs,
offsetX,
offsetY
}))
event.dataTransfer.effectAllowed = 'move'
}
return(
<div className = "toolbar">
<h4 style = {{ marginTop: '3px', marginBottom: '15px', textAlign: 'center' }}>Блоки</h4>
<div className = "toolbarElements">
{ /*Вкладки*/ }
<div className = "tabButtons">
{tabs.map((tab) => (
<button
key = {tab}
className = { activeTab === tab ? 'activeTab' : 'notActiveTab' }
data-row = {
['logical', 'arithmetic', 'timers', 'counters'].includes(tab) ? '1' :
['comparisons', 'other', 'data'].includes(tab) ? '2' :
'3'
}
onClick = {() => setActiveTab(tab)}
title = {NAME_CATEGORIES[tab]}
> { NAME_CATEGORIES[tab] } </button>
))}
</div>
{ /*Блоки*/ }
<div className = "toolbarBlocks">
{activeBlocks.map((block) => (
<ToolbarBlock
key = {block.type}
block = {block}
onDragStart = {onDragStart}
scale = {BLOCK_SCALE}
/>
))}
</div>
</div>
</div>
)
}
export default Toolbar