@grafloria/element 0.4.36 → 0.4.37
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/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -79,7 +79,7 @@ export type { JoinColumn, JoinEnd, MatchTier, JoinGuidanceApi, JoinGuidanceOptio
|
|
|
79
79
|
export type { ErColumn, ErEntitySpec, ErRelationshipSpec, ErCardinality, ErSide, ErDiagramOptions, UmlClassSpec, UmlRelationshipSpec, UmlRelationKind, UmlSide, UmlDiagramOptions, ErEntityDelta, UmlClassDelta, CardEditingHandle, } from './lib/diagram-kit/index.js';
|
|
80
80
|
export { erTable, umlClass, erTables, umlClasses, CardHandle, ErTable, ErField, ErColumnList, UmlClass, UmlMemberList, } from './lib/diagram-kit/index.js';
|
|
81
81
|
export type { HandleApi } from './lib/diagram-kit/index.js';
|
|
82
|
-
export { bindDashboardGrid, paintTabStrip, tabStripReserve, tabStripKey, TAB_STRIP_HEIGHT, TAB_DRAG_THRESHOLD, normalizeCaption, captionReserve, captionBandHeight, paintCaptionBand, CAPTION_HEIGHT, CAPTION_HEIGHT_SUBTITLE, CAPTION_HEIGHT_TIGHT, CAPTION_PASS_THROUGH, rowHeightFor, boardHeightFor, columnUnitFor, cellToRect, pointToCell, sizeToSpan, gridItemFromCell, cellFromGridItem, buildCommitCommands, ensureDashboardKitStyles, DASHBOARD_KIT_STYLE_ID, dashboard, defaultWidgetRenderer, renderKpiWidget, renderLineWidget, renderBarWidget, renderDonutWidget, renderFunnelWidget, renderTableWidget, BUILT_IN_WIDGET_KINDS, } from './lib/dashboard-kit/index.js';
|
|
82
|
+
export { bindDashboardGrid, paintTabStrip, tabStripReserve, tabStripKey, TAB_STRIP_HEIGHT, TAB_DRAG_THRESHOLD, normalizeCaption, captionReserve, captionBandHeight, paintCaptionBand, CAPTION_HEIGHT, CAPTION_HEIGHT_SUBTITLE, CAPTION_HEIGHT_TIGHT, CAPTION_PASS_THROUGH, rowHeightFor, boardHeightFor, columnUnitFor, cellToRect, pointToCell, sizeToSpan, gridItemFromCell, cellFromGridItem, buildCommitCommands, ensureDashboardKitStyles, DASHBOARD_KIT_STYLE_ID, DASHBOARD_KIT_CSS, dashboard, defaultWidgetRenderer, renderKpiWidget, renderLineWidget, renderBarWidget, renderDonutWidget, renderFunnelWidget, renderTableWidget, BUILT_IN_WIDGET_KINDS, } from './lib/dashboard-kit/index.js';
|
|
83
83
|
export type { DashboardGridApi, DashboardGridOptions, DashboardGridHandle, CellRect, WorldRect, DashboardGridGeometry, TileDelta, DashboardOptions, DashboardSpec, DashboardSnapshot, DragHandleOption, DragGripOptions, DashboardHandle, DashboardViewSpec, DashboardWidgetSpec, SectionCaption, SectionCaptionOptions, TabsOptions, TabPage, SectionCaptionAction, SectionCaptionFont, WidgetHandle, WidgetRenderer, KpiWidgetData, LineWidgetData, LineSeries, BarWidgetData, DonutWidgetData, FunnelWidgetData, TableWidgetData, } from './lib/dashboard-kit/index.js';
|
|
84
84
|
export { bindStencilPalette, bindShapeDataPanel, ensureStencilKitStyles } from './lib/stencil-kit/index.js';
|
|
85
85
|
export type { StencilPaletteApi, StencilPaletteOptions, StencilPaletteHandle, ShapeDataPanelApi, ShapeDataPanelOptions, ShapeDataPanelHandle, } from './lib/stencil-kit/index.js';
|
package/src/index.js
CHANGED
|
@@ -230,7 +230,7 @@ export { erTable, umlClass, erTables, umlClasses, CardHandle, ErTable, ErField,
|
|
|
230
230
|
// GridItemConfig. Element-local code, so the Phase-0 wholesale re-export
|
|
231
231
|
// below does NOT cover it — this curated block is its only door.
|
|
232
232
|
// ===========================================================================
|
|
233
|
-
export { bindDashboardGrid, paintTabStrip, tabStripReserve, tabStripKey, TAB_STRIP_HEIGHT, TAB_DRAG_THRESHOLD, normalizeCaption, captionReserve, captionBandHeight, paintCaptionBand, CAPTION_HEIGHT, CAPTION_HEIGHT_SUBTITLE, CAPTION_HEIGHT_TIGHT, CAPTION_PASS_THROUGH, rowHeightFor, boardHeightFor, columnUnitFor, cellToRect, pointToCell, sizeToSpan, gridItemFromCell, cellFromGridItem, buildCommitCommands, ensureDashboardKitStyles, DASHBOARD_KIT_STYLE_ID,
|
|
233
|
+
export { bindDashboardGrid, paintTabStrip, tabStripReserve, tabStripKey, TAB_STRIP_HEIGHT, TAB_DRAG_THRESHOLD, normalizeCaption, captionReserve, captionBandHeight, paintCaptionBand, CAPTION_HEIGHT, CAPTION_HEIGHT_SUBTITLE, CAPTION_HEIGHT_TIGHT, CAPTION_PASS_THROUGH, rowHeightFor, boardHeightFor, columnUnitFor, cellToRect, pointToCell, sizeToSpan, gridItemFromCell, cellFromGridItem, buildCommitCommands, ensureDashboardKitStyles, DASHBOARD_KIT_STYLE_ID, DASHBOARD_KIT_CSS,
|
|
234
234
|
// The DATA-FIRST authoring API — the erDiagram()/umlDiagram() equivalent.
|
|
235
235
|
dashboard,
|
|
236
236
|
// …and the built-in renderers behind `kind`, so a dashboard is useful with
|
|
@@ -2,7 +2,7 @@ export { bindDashboardGrid, type DashboardGridApi, type DashboardGridOptions, ty
|
|
|
2
2
|
export { bindDashboardSplit, SetSplitTreeCommand, SPLIT_TREE_KEY, type DashboardSplitOptions, type DashboardSplitHandle, } from './split-binder.js';
|
|
3
3
|
export { projectSplit, dividersOf, addSplitLeaf, removeSplitLeaf, insertSplitLeaf, moveSplitDivider, splitFromCells, cellsFromSplit, splitLeaves, type SplitNode, type SplitGroup, type SplitLeaf, type SplitDir, type SplitSide, type SplitDivider, } from './split-layout.js';
|
|
4
4
|
export { rowHeightFor, boardHeightFor, columnUnitFor, cellToRect, pointToCell, sizeToSpan, spanWidthPx, gridItemFromCell, cellFromGridItem, buildCommitCommands, type CellRect, type WorldRect, type DashboardGridGeometry, type TileDelta, } from './grid-mapping.js';
|
|
5
|
-
export { ensureDashboardKitStyles, DASHBOARD_KIT_STYLE_ID } from './styles.js';
|
|
5
|
+
export { ensureDashboardKitStyles, DASHBOARD_KIT_STYLE_ID, DASHBOARD_KIT_CSS } from './styles.js';
|
|
6
6
|
export { paintTabStrip, tabStripReserve, tabStripKey, TAB_STRIP_HEIGHT, TAB_DRAG_THRESHOLD, type TabsOptions, type TabPage } from './tabs.js';
|
|
7
7
|
export { normalizeCaption, captionReserve, captionBandHeight, paintCaptionBand, CAPTION_HEIGHT, CAPTION_HEIGHT_SUBTITLE, CAPTION_HEIGHT_TIGHT, CAPTION_PASS_THROUGH, type SectionCaption, type SectionCaptionOptions, type SectionCaptionAction, type SectionCaptionFont, } from './caption.js';
|
|
8
8
|
export { dashboard, type DashboardOptions, type DashboardSpec, type DashboardSnapshot, type DashboardHandle, type DashboardViewSpec, type DashboardWidgetSpec, type WidgetHandle, } from './dashboard.js';
|
|
@@ -2,7 +2,7 @@ export { bindDashboardGrid, normalizeDragHandle, } from './grid-binder.js';
|
|
|
2
2
|
export { bindDashboardSplit, SetSplitTreeCommand, SPLIT_TREE_KEY, } from './split-binder.js';
|
|
3
3
|
export { projectSplit, dividersOf, addSplitLeaf, removeSplitLeaf, insertSplitLeaf, moveSplitDivider, splitFromCells, cellsFromSplit, splitLeaves, } from './split-layout.js';
|
|
4
4
|
export { rowHeightFor, boardHeightFor, columnUnitFor, cellToRect, pointToCell, sizeToSpan, spanWidthPx, gridItemFromCell, cellFromGridItem, buildCommitCommands, } from './grid-mapping.js';
|
|
5
|
-
export { ensureDashboardKitStyles, DASHBOARD_KIT_STYLE_ID } from './styles.js';
|
|
5
|
+
export { ensureDashboardKitStyles, DASHBOARD_KIT_STYLE_ID, DASHBOARD_KIT_CSS } from './styles.js';
|
|
6
6
|
export { paintTabStrip, tabStripReserve, tabStripKey, TAB_STRIP_HEIGHT, TAB_DRAG_THRESHOLD } from './tabs.js';
|
|
7
7
|
export { normalizeCaption, captionReserve, captionBandHeight, paintCaptionBand, CAPTION_HEIGHT, CAPTION_HEIGHT_SUBTITLE, CAPTION_HEIGHT_TIGHT, CAPTION_PASS_THROUGH, } from './caption.js';
|
|
8
8
|
// The DATA-FIRST authoring API (the erDiagram/umlDiagram equivalent).
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* the authored size.
|
|
24
24
|
*/
|
|
25
25
|
import { __awaiter } from "tslib";
|
|
26
|
-
import { Command } from '@grafloria/engine';
|
|
26
|
+
import { BatchCommand, Command } from '@grafloria/engine';
|
|
27
27
|
import { LiveRegionController, registerTool } from '@grafloria/renderer';
|
|
28
28
|
import { anyEdge, clearOtherSelections, dragHandleSelector, gripHostOf, gripOf, normalizeDragHandle, ownsPress, parentPeerOf, pressOnDragHandle, registerBoardPeer, syncGrip, DRAG_HANDLE_CLASS, EDGE_GRIP } from './grid-binder.js';
|
|
29
29
|
import { cellFromGridItem } from './grid-mapping.js';
|
|
@@ -779,6 +779,192 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
779
779
|
},
|
|
780
780
|
};
|
|
781
781
|
const unregisterTool = registerTool(tool);
|
|
782
|
+
// -- a tab torn out of a container on THIS board (0.4.37) --------------------
|
|
783
|
+
// The split board REFUSED every tear-out ("no cells to drop a page into"),
|
|
784
|
+
// so on a split board a tab press was a dead click — no chip, no reorder,
|
|
785
|
+
// no join. The board has a drop model of its own: a widget dropped on a
|
|
786
|
+
// pane's edge inserts there. A torn-out page uses it — the page becomes a
|
|
787
|
+
// one-tab group that IS a pane — and keeps the strip rules of the grid
|
|
788
|
+
// board: its own strip reorders, another container's strip or the centre of
|
|
789
|
+
// its body joins, the centre of its own body is home, off the canvas is
|
|
790
|
+
// nothing. One history step, like every other drop.
|
|
791
|
+
let tearing = false;
|
|
792
|
+
const EDGE_GRACE = 60;
|
|
793
|
+
const execute = (name, commands) => {
|
|
794
|
+
if (commands.length === 0)
|
|
795
|
+
return false;
|
|
796
|
+
void execCommand(new BatchCommand(name, commands));
|
|
797
|
+
return true;
|
|
798
|
+
};
|
|
799
|
+
const beginTearOut = (pageId, fromGroupId, ev, plan) => {
|
|
800
|
+
var _a;
|
|
801
|
+
if (disposed || isStatic || gesture || tearing)
|
|
802
|
+
return false;
|
|
803
|
+
const from = diagram.getGroup(fromGroupId);
|
|
804
|
+
if (!from)
|
|
805
|
+
return false;
|
|
806
|
+
const tree0 = readTree();
|
|
807
|
+
tearing = true;
|
|
808
|
+
const doc = (_a = api.container.ownerDocument) !== null && _a !== void 0 ? _a : document;
|
|
809
|
+
const chip = doc.createElement('div');
|
|
810
|
+
chip.className = 'axdb-drag-chip axdb-tab-chip';
|
|
811
|
+
chip.textContent = plan.label;
|
|
812
|
+
doc.body.appendChild(chip);
|
|
813
|
+
const moveChip = (cx, cy) => {
|
|
814
|
+
chip.style.left = `${cx + 6}px`;
|
|
815
|
+
chip.style.top = `${cy + 6}px`;
|
|
816
|
+
};
|
|
817
|
+
moveChip(ev.clientX, ev.clientY);
|
|
818
|
+
const layer = htmlLayer();
|
|
819
|
+
let joinEl = null;
|
|
820
|
+
const showJoin = (r) => {
|
|
821
|
+
if (!layer)
|
|
822
|
+
return;
|
|
823
|
+
if (!joinEl) {
|
|
824
|
+
joinEl = doc.createElement('div');
|
|
825
|
+
joinEl.className = 'axdb-join';
|
|
826
|
+
layer.prepend(joinEl);
|
|
827
|
+
}
|
|
828
|
+
joinEl.style.left = `${r.x}px`;
|
|
829
|
+
joinEl.style.top = `${r.y}px`;
|
|
830
|
+
joinEl.style.width = `${r.width}px`;
|
|
831
|
+
joinEl.style.height = `${r.height}px`;
|
|
832
|
+
};
|
|
833
|
+
const hideJoin = () => {
|
|
834
|
+
joinEl === null || joinEl === void 0 ? void 0 : joinEl.remove();
|
|
835
|
+
joinEl = null;
|
|
836
|
+
};
|
|
837
|
+
const frameOfGroup = (g) => { var _a, _b, _c, _d; return ({ x: g.position.x, y: g.position.y, width: (_b = (_a = g.size) === null || _a === void 0 ? void 0 : _a.width) !== null && _b !== void 0 ? _b : 0, height: (_d = (_c = g.size) === null || _c === void 0 ? void 0 : _c.height) !== null && _d !== void 0 ? _d : 0 }); };
|
|
838
|
+
const inRect = (r, x, y) => x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height;
|
|
839
|
+
const targets = plan.joinTargets
|
|
840
|
+
.map((id) => diagram.getGroup(id))
|
|
841
|
+
.filter((g) => !!g && g.id !== fromGroupId && !plan.ownBoards.includes(g.id));
|
|
842
|
+
/** Inside the visible canvas plus a grace band; an unmeasurable container (no layout) is never off. */
|
|
843
|
+
const clientInsideCanvasGrace = (cx, cy) => {
|
|
844
|
+
const rect = api.container.getBoundingClientRect();
|
|
845
|
+
if (rect.width <= 0 || rect.height <= 0)
|
|
846
|
+
return true;
|
|
847
|
+
return cx >= rect.left - EDGE_GRACE && cx <= rect.right + EDGE_GRACE && cy >= rect.top - EDGE_GRACE && cy <= rect.bottom + EDGE_GRACE;
|
|
848
|
+
};
|
|
849
|
+
/** The centre third of a container's BODY (below its strip), both axes — the grid board's join zone. */
|
|
850
|
+
const centreThird = (g, w) => {
|
|
851
|
+
const f = frameOfGroup(g);
|
|
852
|
+
if (!inRect(f, w.x, w.y))
|
|
853
|
+
return false;
|
|
854
|
+
const top = f.y + plan.stripHeight(g.id);
|
|
855
|
+
const h = Math.max(0, f.y + f.height - top);
|
|
856
|
+
return w.x >= f.x + f.width / 3 && w.x <= f.x + (2 * f.width) / 3 && w.y >= top + h / 3 && w.y <= top + (2 * h) / 3;
|
|
857
|
+
};
|
|
858
|
+
const zoneAt = (cx, cy, w) => {
|
|
859
|
+
if (!clientInsideCanvasGrace(cx, cy))
|
|
860
|
+
return { kind: 'off' };
|
|
861
|
+
const own = plan.stripIndex(fromGroupId, cx, cy);
|
|
862
|
+
if (own !== null)
|
|
863
|
+
return { kind: 'reorder', index: own };
|
|
864
|
+
for (const t of targets) {
|
|
865
|
+
const i = plan.stripIndex(t.id, cx, cy);
|
|
866
|
+
if (i !== null)
|
|
867
|
+
return { kind: 'strip', targetId: t.id, index: i };
|
|
868
|
+
}
|
|
869
|
+
for (const t of targets)
|
|
870
|
+
if (centreThird(t, w))
|
|
871
|
+
return { kind: 'join', targetId: t.id };
|
|
872
|
+
if (centreThird(from, w))
|
|
873
|
+
return { kind: 'home' };
|
|
874
|
+
if (worldInsideBoard(w.x, w.y)) {
|
|
875
|
+
const t = dropTargetAt(tree0, w.x, w.y);
|
|
876
|
+
if (t)
|
|
877
|
+
return { kind: 'pane', target: t };
|
|
878
|
+
}
|
|
879
|
+
return { kind: 'home' };
|
|
880
|
+
};
|
|
881
|
+
const apply = (z) => {
|
|
882
|
+
plan.markDrop(z.kind === 'reorder' ? fromGroupId : z.kind === 'strip' ? z.targetId : null, z.kind === 'reorder' || z.kind === 'strip' ? z.index : null);
|
|
883
|
+
const jt = z.kind === 'join' ? diagram.getGroup(z.targetId) : undefined;
|
|
884
|
+
if (jt)
|
|
885
|
+
showJoin(frameOfGroup(jt));
|
|
886
|
+
else
|
|
887
|
+
hideJoin();
|
|
888
|
+
showInsertion(z.kind === 'pane' ? insertionRect(z.target.rect, z.target.side) : null);
|
|
889
|
+
chip.classList.toggle('axdb-out', z.kind === 'home' || z.kind === 'off');
|
|
890
|
+
};
|
|
891
|
+
let last = { x: ev.clientX, y: ev.clientY };
|
|
892
|
+
const detach = () => {
|
|
893
|
+
window.removeEventListener('pointermove', onMove, true);
|
|
894
|
+
window.removeEventListener('pointerup', onUp, true);
|
|
895
|
+
window.removeEventListener('pointercancel', onCancel, true);
|
|
896
|
+
window.removeEventListener('keydown', onKey, true);
|
|
897
|
+
};
|
|
898
|
+
const done = (changed, kind) => {
|
|
899
|
+
tearing = false;
|
|
900
|
+
project(readTree());
|
|
901
|
+
api.renderNow();
|
|
902
|
+
fire({ type: kind, kind: 'move', nodeId: pageId, changed });
|
|
903
|
+
};
|
|
904
|
+
const finish = (commit) => {
|
|
905
|
+
detach();
|
|
906
|
+
chip.remove();
|
|
907
|
+
hideJoin();
|
|
908
|
+
showInsertion(null);
|
|
909
|
+
plan.markDrop(null, null);
|
|
910
|
+
if (disposed) {
|
|
911
|
+
tearing = false;
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
const z = commit ? zoneAt(last.x, last.y, toWorld(last.x, last.y)) : { kind: 'home' };
|
|
915
|
+
if (z.kind === 'home' || z.kind === 'off') {
|
|
916
|
+
done(false, 'cancel');
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
if (z.kind === 'reorder') {
|
|
920
|
+
const cmds = plan.reorder(z.index);
|
|
921
|
+
const changed = cmds.length > 0 && execute('Reorder tab', cmds);
|
|
922
|
+
done(changed, changed ? 'commit' : 'cancel');
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
if (z.kind === 'strip' || z.kind === 'join') {
|
|
926
|
+
const index = z.kind === 'strip' ? z.index : plan.dropIndex(z.targetId, last.x, last.y);
|
|
927
|
+
const planned = plan.join(z.targetId, index, group.id);
|
|
928
|
+
done(execute('Move tab', [...planned.move, ...planned.collapse]), 'commit');
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
// A PANE: the page's new group takes the named side of the target — the
|
|
932
|
+
// widget drop's own rule — and the tree swap rides in the same step as
|
|
933
|
+
// the group's birth, so one undo removes both.
|
|
934
|
+
const side = rtl && (z.target.side === 'left' || z.target.side === 'right') ? (z.target.side === 'left' ? 'right' : 'left') : z.target.side;
|
|
935
|
+
const after = normalizeSplit(insertSplitLeaf(tree0, plan.arrivingId, targetRef(z.target), side));
|
|
936
|
+
const rect = rectsOf(after).get(plan.arrivingId);
|
|
937
|
+
const cell = cellsFromSplit(after, columns, rowsGuess()).get(plan.arrivingId);
|
|
938
|
+
if (!rect || !cell) {
|
|
939
|
+
done(false, 'cancel');
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
const planned = plan.commands(cell, rect, group.id);
|
|
943
|
+
done(execute('Move tab out', [...planned.move, new SetSplitTreeCommand(group.id, tree0, after), ...planned.collapse]), 'commit');
|
|
944
|
+
live.announce(`${plan.label} is a pane ${side === 'left' || side === 'top' ? 'before' : 'after'} ${targetName(z.target)}`, 'polite', true);
|
|
945
|
+
};
|
|
946
|
+
const onMove = (e) => {
|
|
947
|
+
if (disposed)
|
|
948
|
+
return finish(false);
|
|
949
|
+
last = { x: e.clientX, y: e.clientY };
|
|
950
|
+
moveChip(e.clientX, e.clientY);
|
|
951
|
+
apply(zoneAt(e.clientX, e.clientY, toWorld(e.clientX, e.clientY)));
|
|
952
|
+
api.render();
|
|
953
|
+
};
|
|
954
|
+
const onUp = () => finish(true);
|
|
955
|
+
const onCancel = () => finish(false);
|
|
956
|
+
const onKey = (e) => {
|
|
957
|
+
if (e.key === 'Escape')
|
|
958
|
+
finish(false);
|
|
959
|
+
};
|
|
960
|
+
window.addEventListener('pointermove', onMove, true);
|
|
961
|
+
window.addEventListener('pointerup', onUp, true);
|
|
962
|
+
window.addEventListener('pointercancel', onCancel, true);
|
|
963
|
+
window.addEventListener('keydown', onKey, true);
|
|
964
|
+
apply(zoneAt(ev.clientX, ev.clientY, toWorld(ev.clientX, ev.clientY)));
|
|
965
|
+
api.render();
|
|
966
|
+
return true;
|
|
967
|
+
};
|
|
782
968
|
// -- palette drag-in --------------------------------------------------------
|
|
783
969
|
const beginPaletteDrag = (node, spec, event) => {
|
|
784
970
|
var _a;
|
|
@@ -1251,9 +1437,8 @@ export function bindDashboardSplit(api, group, options = {}) {
|
|
|
1251
1437
|
hasItem: (id) => { var _a; return ((_a = group.members) !== null && _a !== void 0 ? _a : new Set()).has(id); },
|
|
1252
1438
|
memberCell: (id) => handle.cellOf(id),
|
|
1253
1439
|
resizeMemberBy: () => ({ changed: false }),
|
|
1254
|
-
// A
|
|
1255
|
-
|
|
1256
|
-
tearOutMember: (_pageId, _fromGroupId, _ev, _plan) => false,
|
|
1440
|
+
// A torn-out page becomes a PANE (0.4.37) — see beginTearOut.
|
|
1441
|
+
tearOutMember: (pageId, fromGroupId, ev, plan) => beginTearOut(pageId, fromGroupId, ev, plan),
|
|
1257
1442
|
// …and it has no cells to move a section across: a strip press stays a selection.
|
|
1258
1443
|
dragMember: () => false,
|
|
1259
1444
|
containsWorld: (x, y) => worldInsideBoard(x, y),
|