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.
- package/dist/configs/Localization.js +2 -0
- package/dist/configs/localization/ConlluGraphTexts.d.ts +35 -0
- package/dist/configs/localization/ConlluGraphTexts.js +35 -0
- package/dist/features/ConlluGraphViewer/ConlluGraphViewer.d.ts +12 -0
- package/dist/features/ConlluGraphViewer/ConlluGraphViewer.js +115 -0
- package/dist/features/ConlluGraphViewer/ConlluGraphViewerRenderer.d.ts +25 -0
- package/dist/features/ConlluGraphViewer/ConlluGraphViewerRenderer.js +195 -0
- package/dist/features/ConlluGraphViewer/index.d.ts +2 -0
- package/dist/features/ConlluGraphViewer/index.js +1 -0
- package/dist/features/ConlluGraphViewer/style.scss +73 -0
- package/dist/features/index.d.ts +2 -0
- package/dist/features/index.js +1 -0
- package/dist/functions/conllu/updateDependencyInConllu.d.ts +4 -0
- package/dist/functions/conllu/updateDependencyInConllu.js +52 -0
- package/dist/functions/index.d.ts +1 -0
- package/dist/functions/index.js +1 -0
- package/package.json +3 -1
|
@@ -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 @@
|
|
|
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
|
+
}
|
package/dist/features/index.d.ts
CHANGED
|
@@ -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";
|
package/dist/features/index.js
CHANGED
|
@@ -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';
|
package/dist/functions/index.js
CHANGED
|
@@ -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.
|
|
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",
|