tycho-components 0.40.7 → 0.40.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,6 +11,7 @@ import { HelpTexts } from "./localization/HelpTexts";
11
11
  import { HistoryTexts } from "./localization/HistoryTexts";
12
12
  import { ParametersTexts } from "./localization/ParametersTexts";
13
13
  import { SentenceTexts } from "./localization/SentenceTexts";
14
+ import { ConlluGraphTexts } from "./localization/ConlluGraphTexts";
14
15
  import { TreeTexts } from "./localization/TreeTexts";
15
16
  import { UploadTexts } from "./localization/UploadTexts";
16
17
  function buildResources(namespaces) {
@@ -31,6 +32,7 @@ const CORE_NAMESPACES = {
31
32
  };
32
33
  const FEATURE_NAMESPACES = {
33
34
  comments: CommentsTexts,
35
+ 'conllu-graph': ConlluGraphTexts,
34
36
  tree: TreeTexts,
35
37
  sentence: SentenceTexts,
36
38
  parameters: ParametersTexts,
@@ -0,0 +1,35 @@
1
+ export declare const ConlluGraphTexts: {
2
+ en: {
3
+ 'button.tooltip.zoom.in': string;
4
+ 'button.tooltip.zoom.out': string;
5
+ 'button.tooltip.recenter': string;
6
+ 'button.tooltip.download': string;
7
+ 'popover.id': string;
8
+ 'popover.form': string;
9
+ 'popover.pos': string;
10
+ 'popover.xpos': string;
11
+ 'popover.lemma': string;
12
+ };
13
+ 'pt-BR': {
14
+ 'button.tooltip.zoom.in': string;
15
+ 'button.tooltip.zoom.out': string;
16
+ 'button.tooltip.recenter': string;
17
+ 'button.tooltip.download': string;
18
+ 'popover.id': string;
19
+ 'popover.form': string;
20
+ 'popover.pos': string;
21
+ 'popover.xpos': string;
22
+ 'popover.lemma': string;
23
+ };
24
+ it: {
25
+ 'button.tooltip.zoom.in': string;
26
+ 'button.tooltip.zoom.out': string;
27
+ 'button.tooltip.recenter': string;
28
+ 'button.tooltip.download': string;
29
+ 'popover.id': string;
30
+ 'popover.form': string;
31
+ 'popover.pos': string;
32
+ 'popover.xpos': string;
33
+ 'popover.lemma': string;
34
+ };
35
+ };
@@ -0,0 +1,35 @@
1
+ export const ConlluGraphTexts = {
2
+ en: {
3
+ 'button.tooltip.zoom.in': 'Zoom in',
4
+ 'button.tooltip.zoom.out': 'Zoom out',
5
+ 'button.tooltip.recenter': 'Recenter',
6
+ 'button.tooltip.download': 'Download',
7
+ 'popover.id': 'ID',
8
+ 'popover.form': 'Form',
9
+ 'popover.pos': 'POS tag',
10
+ 'popover.xpos': 'XPOS tag',
11
+ 'popover.lemma': 'Lemma',
12
+ },
13
+ 'pt-BR': {
14
+ 'button.tooltip.zoom.in': 'Aumentar zoom',
15
+ 'button.tooltip.zoom.out': 'Diminuir zoom',
16
+ 'button.tooltip.recenter': 'Recentralizar',
17
+ 'button.tooltip.download': 'Baixar',
18
+ 'popover.id': 'ID',
19
+ 'popover.form': 'Forma',
20
+ 'popover.pos': 'Etiqueta POS',
21
+ 'popover.xpos': 'Etiqueta XPOS',
22
+ 'popover.lemma': 'Lema',
23
+ },
24
+ it: {
25
+ 'button.tooltip.zoom.in': 'Zoom avanti',
26
+ 'button.tooltip.zoom.out': 'Zoom indietro',
27
+ 'button.tooltip.recenter': 'Ricentra',
28
+ 'button.tooltip.download': 'Scarica',
29
+ 'popover.id': 'ID',
30
+ 'popover.form': 'Forma',
31
+ 'popover.pos': 'Etichetta POS',
32
+ 'popover.xpos': 'Etichetta XPOS',
33
+ 'popover.lemma': 'Lemma',
34
+ },
35
+ };
@@ -0,0 +1,12 @@
1
+ import '@svgdotjs/svg.panzoom.js';
2
+ import { Conllu } from '../../configs/types/Conllu';
3
+ import { ConlluViewerItem } from '../../functions/conllu/ConlluViewerGraph';
4
+ import './style.scss';
5
+ export type ConlluGraphViewerProps = {
6
+ conllu: Conllu;
7
+ editable?: boolean;
8
+ onConlluChange?: (conllu: Conllu) => void;
9
+ onTokenClick?: (item: ConlluViewerItem) => void;
10
+ className?: string;
11
+ };
12
+ export default function ConlluGraphViewer({ conllu, editable, onConlluChange, onTokenClick, className, }: ConlluGraphViewerProps): import("react").JSX.Element | null;
@@ -0,0 +1,115 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Point, SVG as dotSVG } from '@svgdotjs/svg.js';
3
+ import '@svgdotjs/svg.panzoom.js';
4
+ import classNames from 'classnames';
5
+ import { useEffect, useRef, useState } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { IconButton } from 'tycho-storybook';
8
+ import ConlluUtils from '../../functions/conllu/ConlluUtils';
9
+ import ConlluViewerConverter from '../../functions/conllu/ConlluViewerConverter';
10
+ import { conlluViewerConfigurations } from '../../functions/conllu/ConlluViewerConstants';
11
+ import { drawDependencies, drawItems, drawMultis, } from './ConlluGraphViewerRenderer';
12
+ import './style.scss';
13
+ const ZOOM_FACTOR = 0.1;
14
+ const ZOOM_STEP = 1.1;
15
+ const DEFAULT_ZOOM = 1;
16
+ const applyDefaultView = (svg, graph) => {
17
+ svg.viewbox(0, 0, graph.width, graph.height);
18
+ const fitZoom = svg.zoom();
19
+ const initialZoom = Math.min(DEFAULT_ZOOM, fitZoom);
20
+ svg.zoom(initialZoom, new Point(graph.width / 2, graph.height / 2));
21
+ };
22
+ export default function ConlluGraphViewer({ conllu, editable = false, onConlluChange, onTokenClick, className, }) {
23
+ const { t } = useTranslation('conllu-graph');
24
+ const svgRef = useRef(null);
25
+ const svgDotRef = useRef(null);
26
+ const [graph, setGraph] = useState();
27
+ const [activeItem, setActiveItem] = useState();
28
+ const [draggedDep, setDraggedDep] = useState(null);
29
+ const [dragOverItem, setDragOverItem] = useState(null);
30
+ const handleGenerateImage = () => {
31
+ if (!svgRef.current || !graph) {
32
+ throw new Error('SVG element not found');
33
+ }
34
+ const svg = svgDotRef.current;
35
+ const previousViewbox = svg?.viewbox();
36
+ svg?.viewbox(0, 0, graph.width, graph.height);
37
+ try {
38
+ ConlluUtils.generateImageFromSVG(svgRef.current);
39
+ }
40
+ finally {
41
+ if (svg && previousViewbox) {
42
+ svg.viewbox(previousViewbox.x, previousViewbox.y, previousViewbox.width, previousViewbox.height);
43
+ }
44
+ }
45
+ };
46
+ const handleZoomIn = () => {
47
+ const svg = svgDotRef.current;
48
+ if (!svg)
49
+ return;
50
+ svg.zoom(svg.zoom() * ZOOM_STEP);
51
+ };
52
+ const handleZoomOut = () => {
53
+ const svg = svgDotRef.current;
54
+ if (!svg)
55
+ return;
56
+ svg.zoom(svg.zoom() / ZOOM_STEP);
57
+ };
58
+ const handleRecenter = () => {
59
+ const svg = svgDotRef.current;
60
+ if (!svg || !graph)
61
+ return;
62
+ applyDefaultView(svg, graph);
63
+ };
64
+ useEffect(() => {
65
+ try {
66
+ setGraph(new ConlluViewerConverter().execute(conllu));
67
+ }
68
+ catch (err) {
69
+ console.error(err);
70
+ setGraph(undefined);
71
+ }
72
+ }, [conllu]);
73
+ useEffect(() => {
74
+ if (!graph || !svgRef.current)
75
+ return;
76
+ const svg = dotSVG(svgRef.current);
77
+ svg.panZoom({ zoomFactor: ZOOM_FACTOR });
78
+ applyDefaultView(svg, graph);
79
+ svgDotRef.current = svg;
80
+ return () => {
81
+ svg.panZoom(false);
82
+ svgDotRef.current = null;
83
+ };
84
+ }, [graph?.width, graph?.height]);
85
+ useEffect(() => {
86
+ const svg = svgDotRef.current;
87
+ if (!svg)
88
+ return;
89
+ svg.panZoom({
90
+ zoomFactor: ZOOM_FACTOR,
91
+ panning: draggedDep == null,
92
+ });
93
+ }, [draggedDep]);
94
+ if (!graph) {
95
+ return null;
96
+ }
97
+ const rendererProps = {
98
+ graph,
99
+ activeItem,
100
+ setActiveItem,
101
+ editable,
102
+ conllu,
103
+ onTokenClick,
104
+ ...(editable
105
+ ? {
106
+ onDependencyChange: onConlluChange,
107
+ draggedDep,
108
+ setDraggedDep,
109
+ dragOverItem,
110
+ setDragOverItem,
111
+ }
112
+ : {}),
113
+ };
114
+ return (_jsxs("div", { className: classNames('conllu-graph-viewer-container', className), children: [_jsxs("div", { className: "floating-buttons", children: [_jsx(IconButton, { name: "zoom_in", title: t('button.tooltip.zoom.in'), size: "small", mode: "ghost", onClick: handleZoomIn }), _jsx(IconButton, { name: "zoom_out", title: t('button.tooltip.zoom.out'), size: "small", mode: "ghost", onClick: handleZoomOut }), _jsx(IconButton, { name: "center_focus_weak", title: t('button.tooltip.recenter'), size: "small", mode: "ghost", onClick: handleRecenter }), _jsx(IconButton, { name: "download", title: t('button.tooltip.download'), size: "small", mode: "ghost", onClick: handleGenerateImage })] }), _jsxs("svg", { ref: svgRef, id: conlluViewerConfigurations.svgID, width: "100%", height: "100%", preserveAspectRatio: "xMidYMid meet", children: [drawDependencies(rendererProps), drawItems(rendererProps), drawMultis(rendererProps)] })] }));
115
+ }
@@ -0,0 +1,25 @@
1
+ import { ReactElement } from 'react';
2
+ import { Conllu } from '../../configs/types/Conllu';
3
+ import { ConlluViewerGraph, ConlluViewerItem } from '../../functions/conllu/ConlluViewerGraph';
4
+ export type ConlluGraphViewerRendererProps = {
5
+ graph: ConlluViewerGraph;
6
+ activeItem: ConlluViewerItem | undefined;
7
+ setActiveItem: (item: ConlluViewerItem | undefined) => void;
8
+ editable?: boolean;
9
+ onDependencyChange?: (updatedConllu: Conllu) => void;
10
+ conllu: Conllu;
11
+ draggedDep?: {
12
+ depIndex: number;
13
+ variant: number;
14
+ } | null;
15
+ setDraggedDep?: (dep: {
16
+ depIndex: number;
17
+ variant: number;
18
+ } | null) => void;
19
+ dragOverItem?: number | null;
20
+ setDragOverItem?: (itemIndex: number | null) => void;
21
+ onTokenClick?: (item: ConlluViewerItem) => void;
22
+ };
23
+ export declare const drawDependencies: ({ graph, activeItem, editable, draggedDep, setDraggedDep, }: ConlluGraphViewerRendererProps) => ReactElement | null;
24
+ export declare const drawItems: ({ graph, activeItem, setActiveItem, editable, onDependencyChange, conllu, dragOverItem, setDragOverItem, draggedDep, setDraggedDep, onTokenClick, }: ConlluGraphViewerRendererProps) => ReactElement | null;
25
+ export declare const drawMultis: ({ graph, }: ConlluGraphViewerRendererProps) => ReactElement | null;
@@ -0,0 +1,195 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Tooltip } from '@mui/material';
3
+ import { useTranslation } from 'react-i18next';
4
+ import ConlluUtils from '../../functions/conllu/ConlluUtils';
5
+ import { calculateConlluTextWidth, throwConlluError, } from '../../functions/conllu/ConlluViewerConverter';
6
+ import { conlluViewerConfigurations } from '../../functions/conllu/ConlluViewerConstants';
7
+ import updateDependencyInConllu from '../../functions/conllu/updateDependencyInConllu';
8
+ const PopoverItem = ({ item }) => {
9
+ const { t } = useTranslation('conllu-graph');
10
+ return (_jsx("table", { children: _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("td", { children: t('popover.id') }), _jsx("td", { children: item.lineno + 1 })] }), _jsxs("tr", { children: [_jsx("td", { children: t('popover.form') }), _jsx("td", { children: item.word })] }), _jsxs("tr", { children: [_jsx("td", { children: t('popover.pos') }), _jsx("td", { children: item.postag })] }), _jsxs("tr", { children: [_jsx("td", { children: t('popover.xpos') }), _jsx("td", { children: item.xpostag })] }), _jsxs("tr", { children: [_jsx("td", { children: t('popover.lemma') }), _jsx("td", { children: item.lemma })] }), _jsx("tr", { children: _jsx("td", { colSpan: 2, children: item.attribs }) })] }) }));
11
+ };
12
+ export const drawDependencies = ({ graph, activeItem, editable = false, draggedDep, setDraggedDep, }) => {
13
+ const results = [];
14
+ for (let variant = 0; variant < 2; variant++) {
15
+ let e = 'n';
16
+ if (variant === 1) {
17
+ if (!graph.hasEnhanced) {
18
+ continue;
19
+ }
20
+ e = 'e';
21
+ }
22
+ const lines = [];
23
+ const arrows = [];
24
+ const whites = [];
25
+ const texts = [];
26
+ graph.dependencies.forEach((dep, i) => {
27
+ if (dep.rel[variant] === '') {
28
+ return;
29
+ }
30
+ let i1 = dep.end - 1;
31
+ let i2 = dep.headpos - 1;
32
+ if (dep.headpos === 0) {
33
+ i2 = i1;
34
+ }
35
+ let d1 = graph.items[i1].x2 - graph.items[i1].x1 - 20;
36
+ let x1 = graph.items[i1].x1 +
37
+ 10 +
38
+ Math.floor(d1 * ConlluUtils.calculateAnchor(graph, i1, i2, dep.lvl));
39
+ let d2 = graph.items[i2].x2 - graph.items[i2].x1 - 20;
40
+ let x2 = graph.items[i2].x1 +
41
+ 10 +
42
+ Math.floor(d2 * ConlluUtils.calculateAnchor(graph, i2, i1, dep.lvl));
43
+ let y1 = conlluViewerConfigurations.margin +
44
+ conlluViewerConfigurations.edgeFontSize +
45
+ conlluViewerConfigurations.edgeFontOffset +
46
+ conlluViewerConfigurations.lvlHeight * (graph.maxlvl + 1);
47
+ let y2 = conlluViewerConfigurations.margin +
48
+ conlluViewerConfigurations.edgeFontSize +
49
+ conlluViewerConfigurations.edgeFontOffset +
50
+ conlluViewerConfigurations.lvlHeight * (graph.maxlvl - dep.lvl);
51
+ if (dep.headpos === 0) {
52
+ y2 =
53
+ conlluViewerConfigurations.margin +
54
+ conlluViewerConfigurations.edgeFontSize +
55
+ conlluViewerConfigurations.edgeFontOffset;
56
+ lines.push(_jsx("path", { className: `e${e}${dep.end} q${dep.end}q${dep.headpos}`, d: `M${x1} ${y1} L${x1} ${y2}` }, `deps_${i}`));
57
+ }
58
+ else {
59
+ lines.push(_jsx("path", { className: `e${e}${dep.end} e${e}${dep.headpos} q${dep.end}q${dep.headpos} ${ConlluUtils.isActiveDependency(dep, activeItem)
60
+ ? 'active-line'
61
+ : ''}`, d: `M${x1} ${y1} L${x1} ${y2 + conlluViewerConfigurations.edgeDrop} C${x1} ${y2} ${x1} ${y2} ${(x1 + x2) / 2} ${y2} C${x2} ${y2} ${x2} ${y2} ${x2} ${y2 + conlluViewerConfigurations.edgeDrop} L${x2} ${y1}` }, `deps_${i}`));
62
+ }
63
+ const isDragging = editable &&
64
+ draggedDep?.depIndex === i &&
65
+ draggedDep?.variant === variant;
66
+ const diamondSize = 6;
67
+ const diamondOffset = 8;
68
+ const angle = 90;
69
+ const diamondY = y1 - diamondOffset;
70
+ arrows.push(_jsxs("g", { children: [dep.headpos !== 0 && (_jsx("polygon", { className: `e${e}${dep.end} e${e}${dep.headpos} q${dep.end}q${dep.headpos} ${ConlluUtils.isActiveDependency(dep, activeItem)
71
+ ? 'active-arrow'
72
+ : ''} ${isDragging ? 'dragging' : ''}`, points: `${x2},${diamondY - diamondSize} ${x2 + diamondSize},${diamondY} ${x2},${diamondY + diamondSize} ${x2 - diamondSize},${diamondY}`, transform: `rotate(${angle} ${x2} ${diamondY})`, style: editable ? { cursor: 'grab' } : undefined, onMouseDownCapture: editable && setDraggedDep
73
+ ? (e) => {
74
+ if (e.button === 0) {
75
+ e.stopPropagation();
76
+ e.preventDefault();
77
+ const dragData = JSON.stringify({
78
+ depIndex: i,
79
+ variant,
80
+ end: dep.end,
81
+ });
82
+ const dragElement = document.createElement('div');
83
+ dragElement.style.position = 'absolute';
84
+ dragElement.style.left = '-9999px';
85
+ dragElement.setAttribute('data-dependency', dragData);
86
+ document.body.appendChild(dragElement);
87
+ setDraggedDep({ depIndex: i, variant });
88
+ const handleMouseUp = () => {
89
+ document.body.removeChild(dragElement);
90
+ setDraggedDep(null);
91
+ document.removeEventListener('mouseup', handleMouseUp);
92
+ };
93
+ document.addEventListener('mouseup', handleMouseUp);
94
+ }
95
+ }
96
+ : undefined })), _jsx("path", { className: `e${e}${dep.end} e${e}${dep.headpos} q${dep.end}q${dep.headpos} ${ConlluUtils.isActiveDependency(dep, activeItem)
97
+ ? 'active-arrow'
98
+ : ''} ${isDragging ? 'dragging' : ''}`, d: `M${x1} ${y1} l3 -14 l-6 0 Z` })] }, `arrow-group-${i}_${variant}`));
99
+ const { w, h, l } = calculateConlluTextWidth(dep.rel[variant] + 'i', conlluViewerConfigurations.edgeFontSize, true);
100
+ whites.push(_jsx("rect", { x: `${(x1 + x2 - w) / 2 - conlluViewerConfigurations.edgeFontWhiteMargin}`, y: `${y2 -
101
+ l -
102
+ conlluViewerConfigurations.edgeFontOffset -
103
+ conlluViewerConfigurations.edgeFontWhiteMargin}`, width: `${w + 2 * conlluViewerConfigurations.edgeFontWhiteMargin}`, height: `${h + 2 * conlluViewerConfigurations.edgeFontWhiteMargin}` }, `whites_${i}`));
104
+ texts.push(_jsx("text", { className: `l${e}${dep.end} l${e}${dep.headpos} p${dep.end}p${dep.headpos}`, x: (x1 + x2) / 2, y: y2 - conlluViewerConfigurations.edgeFontOffset, children: ConlluUtils.escapeHTML(dep.rel[variant]) }, `texts_${i}`));
105
+ });
106
+ results.push(_jsxs("g", { className: variant === 0 ? 'normal' : 'enhnaced', style: variant === 0 && graph.hasEnhanced
107
+ ? { visibility: 'hidden' }
108
+ : undefined, children: [_jsx("g", { fill: "none", stroke: "black", strokeWidth: "1", children: lines }), _jsx("g", { fill: "black", stroke: "black", strokeWidth: "1", children: arrows }), _jsx("g", { fill: conlluViewerConfigurations.edgeLblBackground, stroke: conlluViewerConfigurations.edgeLblBackground, strokeWidth: "1", opacity: conlluViewerConfigurations.edgeLblOpacity, children: whites }), _jsx("g", { fontFamily: conlluViewerConfigurations.font, fontSize: conlluViewerConfigurations.edgeFontSize, textAnchor: "middle", children: texts })] }, variant));
109
+ }
110
+ return results.length > 0 ? _jsx(_Fragment, { children: results }) : null;
111
+ };
112
+ export const drawItems = ({ graph, activeItem, setActiveItem, editable = false, onDependencyChange, conllu, dragOverItem, setDragOverItem, draggedDep, setDraggedDep, onTokenClick, }) => {
113
+ const items = graph.items.map((item, i) => {
114
+ const color = item.enhanced ? '#ffe0e0' : '';
115
+ const isTooltipOpen = activeItem && activeItem.lineno === item.lineno;
116
+ const isDragging = draggedDep !== null && draggedDep !== undefined;
117
+ return (_jsx(Tooltip, { placement: "bottom", open: !!isTooltipOpen, title: _jsx(PopoverItem, { item: item }), arrow: true, children: _jsxs("g", { className: "pointer", onMouseDownCapture: editable && isDragging
118
+ ? (e) => {
119
+ e.stopPropagation();
120
+ }
121
+ : undefined, onClick: () => {
122
+ if (!editable || !isDragging) {
123
+ onTokenClick?.(item);
124
+ }
125
+ }, onMouseEnter: () => {
126
+ setActiveItem(item);
127
+ if (editable && isDragging && setDragOverItem) {
128
+ setDragOverItem(i);
129
+ }
130
+ }, onMouseLeave: () => {
131
+ setActiveItem(undefined);
132
+ if (editable && setDragOverItem) {
133
+ setDragOverItem(null);
134
+ }
135
+ }, onMouseUp: editable && isDragging
136
+ ? (e) => {
137
+ e.preventDefault();
138
+ e.stopPropagation();
139
+ setDragOverItem?.(null);
140
+ const depIndex = draggedDep.depIndex;
141
+ const variant = draggedDep.variant;
142
+ const newHeadpos = item.end;
143
+ if (onDependencyChange) {
144
+ const updatedConllu = updateDependencyInConllu(conllu, graph, depIndex, newHeadpos, variant);
145
+ onDependencyChange(updatedConllu);
146
+ }
147
+ setDraggedDep?.(null);
148
+ }
149
+ : undefined, children: [_jsx("rect", { className: `${ConlluUtils.isActiveItem(item, activeItem) ? 'active-item' : ''} ${editable && dragOverItem === i ? 'drag-over' : ''}`, x: item.x1, y: graph.offset, width: item.x2 - item.x1, height: conlluViewerConfigurations.nodeHeight, rx: "5", ry: "5", fill: color }), _jsx("text", { className: `item-postag ${item.enhanced ? 'enhanced' : ''}`, x: (item.x1 + item.x2) / 2, y: graph.offset +
150
+ conlluViewerConfigurations.nodeTweek +
151
+ conlluViewerConfigurations.nodeHeight / 4 +
152
+ graph.lower, textAnchor: "middle", children: ConlluUtils.escapeHTML(item.postag) }, `texts_2_${i}`), _jsx("text", { className: `item-text ${item.enhanced ? 'enhanced' : ''}`, x: (item.x1 + item.x2) / 2, y: graph.offset -
153
+ conlluViewerConfigurations.nodeTweek +
154
+ (conlluViewerConfigurations.nodeHeight * 3) / 4 +
155
+ graph.lower, textAnchor: "middle", children: ConlluUtils.escapeHTML(item.word) }, `texts_3_${i}`)] }) }, `items_${i}`));
156
+ });
157
+ return (_jsx("g", { fill: "#D0E0FF", stroke: "black", strokeWidth: "1", children: items }));
158
+ };
159
+ export const drawMultis = ({ graph, }) => {
160
+ const boxes = [];
161
+ const labels = [];
162
+ graph.multis.forEach((multi, i) => {
163
+ const aa = multi.id.split('-');
164
+ if (aa.length !== 2) {
165
+ throwConlluError(new Error(`Invalid range ${multi.id}`), multi.lineno);
166
+ }
167
+ let x1 = 0;
168
+ let x2 = 0;
169
+ let found1 = false;
170
+ let found2 = false;
171
+ graph.items.forEach((item) => {
172
+ if (aa[0] === item.here) {
173
+ x1 = item.x1;
174
+ found1 = true;
175
+ }
176
+ if (aa[1] === item.here) {
177
+ x2 = item.x2;
178
+ found2 = true;
179
+ return;
180
+ }
181
+ });
182
+ if (!(found1 && found2)) {
183
+ throwConlluError(new Error(`Invalid range ${multi.id}`), multi.lineno);
184
+ }
185
+ boxes.push(_jsx("rect", { x: x1, y: graph.offset +
186
+ conlluViewerConfigurations.nodeHeight +
187
+ conlluViewerConfigurations.multiSkip, width: x2 - x1, height: conlluViewerConfigurations.multiHeight, rx: "5", ry: "5" }, `boxes_${i}`));
188
+ labels.push(_jsx("text", { x: (x1 + x2) / 2, y: graph.offset +
189
+ conlluViewerConfigurations.nodeHeight +
190
+ conlluViewerConfigurations.multiSkip +
191
+ conlluViewerConfigurations.multiHeight / 2 +
192
+ graph.lower, children: ConlluUtils.escapeHTML(multi.word) }, `labels_${i}`));
193
+ });
194
+ return (_jsxs(_Fragment, { children: [_jsx("g", { fill: "#D0E0FF", stroke: "black", strokeWidth: "1", children: boxes }), _jsx("g", { fontFamily: conlluViewerConfigurations.font, fontSize: conlluViewerConfigurations.nodeFontSize, fontStyle: "italic", textAnchor: "middle", children: labels })] }));
195
+ };
@@ -0,0 +1,2 @@
1
+ export { default } from './ConlluGraphViewer';
2
+ export type { ConlluGraphViewerProps } from './ConlluGraphViewer';
@@ -0,0 +1 @@
1
+ export { default } from './ConlluGraphViewer';
@@ -0,0 +1,73 @@
1
+ .conllu-graph-viewer-container {
2
+ display: flex;
3
+ flex-direction: column;
4
+ width: 100%;
5
+ height: 100%;
6
+ text-align: center;
7
+ position: relative;
8
+ overflow: hidden;
9
+ background-color: white;
10
+
11
+ .floating-buttons {
12
+ position: absolute;
13
+ top: var(--spacing-100, 10px);
14
+ right: var(--spacing-100, 10px);
15
+ z-index: 1069;
16
+ display: flex;
17
+ gap: var(--spacing-50, 4px);
18
+ }
19
+
20
+ #canvas {
21
+ width: 100%;
22
+ height: 100%;
23
+ display: block;
24
+ background-color: white;
25
+
26
+ .item-postag {
27
+ fill: #000;
28
+ font-size: var(--font-size-body);
29
+ }
30
+
31
+ .item-text {
32
+ fill: #000;
33
+ font-size: var(--font-size-body);
34
+ font-style: italic;
35
+ font-weight: var(--font-weight-light);
36
+ }
37
+
38
+ .active-arrow {
39
+ stroke: var(--color-primary);
40
+ fill: var(--color-primary);
41
+ stroke-width: 3px;
42
+ }
43
+
44
+ .active-line {
45
+ stroke: var(--color-primary);
46
+ stroke-width: 3px;
47
+ }
48
+
49
+ .active-item {
50
+ stroke: var(--color-primary);
51
+ stroke-width: 3px;
52
+ }
53
+
54
+ .drag-over {
55
+ stroke: var(--color-success, #28a745);
56
+ stroke-width: 3px;
57
+ fill: rgba(40, 167, 69, 0.1);
58
+ }
59
+
60
+ .dragging {
61
+ opacity: 0.5;
62
+ cursor: grabbing !important;
63
+ }
64
+
65
+ g[style*='cursor: grab'] {
66
+ cursor: grab !important;
67
+ }
68
+
69
+ g[style*='cursor: grab']:active {
70
+ cursor: grabbing !important;
71
+ }
72
+ }
73
+ }
@@ -5,6 +5,8 @@ export { default as CommentService } from "./Comments/CommentService";
5
5
  export { default as Parameters } from "./Parameters";
6
6
  export { default as SentenceView } from "./SentenceView";
7
7
  export { default as TreeView } from "./TreeView";
8
+ export { default as ConlluGraphViewer } from "./ConlluGraphViewer";
9
+ export type { ConlluGraphViewerProps } from "./ConlluGraphViewer";
8
10
  export { default as CytoscapeTreeConverter } from "./TreeView/cytoscape/CytoscapeTreeConverter";
9
11
  export type { CytoscapeTreeConverterOptions } from "./TreeView/cytoscape/CytoscapeTreeConverter";
10
12
  export { default as SyntreesCytoscape } from "./TreeView/cytoscape/SyntreesCytoscape";
@@ -4,6 +4,7 @@ export { default as CommentService } from "./Comments/CommentService";
4
4
  export { default as Parameters } from "./Parameters";
5
5
  export { default as SentenceView } from "./SentenceView";
6
6
  export { default as TreeView } from "./TreeView";
7
+ export { default as ConlluGraphViewer } from "./ConlluGraphViewer";
7
8
  export { default as CytoscapeTreeConverter } from "./TreeView/cytoscape/CytoscapeTreeConverter";
8
9
  export { default as SyntreesCytoscape } from "./TreeView/cytoscape/SyntreesCytoscape";
9
10
  export { default as TreeViewSearch } from "./TreeView/TreeViewSearch/TreeViewSearch";
@@ -0,0 +1,4 @@
1
+ import { Conllu } from '../../configs/types/Conllu';
2
+ import { ConlluViewerGraph } from './ConlluViewerGraph';
3
+ declare const updateDependencyInConllu: (conllu: Conllu, graph: ConlluViewerGraph, depIndex: number, newHeadpos: number, variant: number) => Conllu;
4
+ export default updateDependencyInConllu;
@@ -0,0 +1,52 @@
1
+ const updateDependencyInConllu = (conllu, graph, depIndex, newHeadpos, variant) => {
2
+ const dep = graph.dependencies[depIndex];
3
+ if (!dep)
4
+ return conllu;
5
+ const endItem = graph.items.find((item) => item.end === dep.end);
6
+ if (!endItem)
7
+ return conllu;
8
+ const tokenIndex = endItem.lineno;
9
+ const token = conllu.tokens[tokenIndex];
10
+ if (!token)
11
+ return conllu;
12
+ const updatedConllu = {
13
+ ...conllu,
14
+ tokens: [...conllu.tokens],
15
+ };
16
+ if (variant === 0) {
17
+ const newHeadItem = graph.items.find((item) => item.end === newHeadpos);
18
+ if (newHeadItem) {
19
+ updatedConllu.tokens[tokenIndex] = {
20
+ ...token,
21
+ head: newHeadItem.here,
22
+ deprel: dep.rel[0],
23
+ };
24
+ }
25
+ }
26
+ else {
27
+ const newHeadItem = graph.items.find((item) => item.end === newHeadpos);
28
+ if (newHeadItem) {
29
+ const enhancedDeps = graph.dependencies
30
+ .map((d, idx) => ({ dep: d, index: idx }))
31
+ .filter(({ dep }) => {
32
+ const depEndItem = graph.items.find((item) => item.end === dep.end);
33
+ return (depEndItem && depEndItem.lineno === tokenIndex && dep.rel[1] !== '');
34
+ });
35
+ const updatedDeps = enhancedDeps
36
+ .map(({ dep, index }) => {
37
+ if (index === depIndex) {
38
+ return `${newHeadItem.here}:${dep.rel[1]}`;
39
+ }
40
+ const headItem = graph.items.find((item) => item.end === dep.headpos);
41
+ return headItem ? `${headItem.here}:${dep.rel[1]}` : '';
42
+ })
43
+ .filter(Boolean);
44
+ updatedConllu.tokens[tokenIndex] = {
45
+ ...token,
46
+ deps: updatedDeps.length > 0 ? updatedDeps.join('|') : '_',
47
+ };
48
+ }
49
+ }
50
+ return updatedConllu;
51
+ };
52
+ export default updateDependencyInConllu;
@@ -11,5 +11,6 @@ export { default as UsabilityUtils } from './UsabilityUtils';
11
11
  export { conlluViewerConfigurations } from './conllu/ConlluViewerConstants';
12
12
  export { default as ConlluUtils } from './conllu/ConlluUtils';
13
13
  export { default as ConlluViewerConverter, calculateConlluTextWidth, throwConlluError, } from './conllu/ConlluViewerConverter';
14
+ export { default as updateDependencyInConllu } from './conllu/updateDependencyInConllu';
14
15
  export type { ConlluViewerAnchor, ConlluViewerDependency, ConlluViewerGraph, ConlluViewerItem, ConlluViewerMulti, } from './conllu/ConlluViewerGraph';
15
16
  export { normalizeCorpusFromApi } from './normalizeCorpusFromApi';
@@ -10,4 +10,5 @@ export { default as UsabilityUtils } from './UsabilityUtils';
10
10
  export { conlluViewerConfigurations } from './conllu/ConlluViewerConstants';
11
11
  export { default as ConlluUtils } from './conllu/ConlluUtils';
12
12
  export { default as ConlluViewerConverter, calculateConlluTextWidth, throwConlluError, } from './conllu/ConlluViewerConverter';
13
+ export { default as updateDependencyInConllu } from './conllu/updateDependencyInConllu';
13
14
  export { normalizeCorpusFromApi } from './normalizeCorpusFromApi';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tycho-components",
3
3
  "private": false,
4
- "version": "0.40.7",
4
+ "version": "0.40.9",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
@@ -48,6 +48,8 @@
48
48
  ],
49
49
  "dependencies": {
50
50
  "@hookform/resolvers": "^5.2.2",
51
+ "@svgdotjs/svg.js": "^3.2.4",
52
+ "@svgdotjs/svg.panzoom.js": "^2.1.2",
51
53
  "@tanstack/react-table": "^8.20.6",
52
54
  "classnames": "^2.5.1",
53
55
  "cytoscape": "^3.28.1",