@formicoidea/labre-framework-edgy 0.31.0 → 0.32.0
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/actions.d.ts +21 -0
- package/dist/actions.js +140 -0
- package/dist/board-renderer.d.ts +12 -0
- package/dist/board-renderer.js +25 -0
- package/dist/board-view.d.ts +11 -0
- package/dist/board-view.js +20 -0
- package/dist/commands.d.ts +4 -0
- package/dist/commands.js +85 -0
- package/dist/consts.d.ts +35 -0
- package/dist/consts.js +46 -0
- package/dist/descriptor.d.ts +8 -3
- package/dist/descriptor.js +6 -3
- package/dist/element-renderer.js +19 -11
- package/dist/element-view.js +9 -3
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/templates/index.d.ts +30 -1
- package/dist/templates/index.js +166 -18
- package/dist/toolbar/config.d.ts +23 -0
- package/dist/toolbar/config.js +44 -14
- package/dist/toolbar/edgy-menu.d.ts +7 -25
- package/dist/toolbar/edgy-menu.js +7 -186
- package/dist/toolbar/icons.d.ts +4 -0
- package/dist/toolbar/icons.js +18 -0
- package/dist/translations.d.ts +14 -0
- package/dist/translations.js +15 -0
- package/dist/view.d.ts +15 -0
- package/dist/view.js +46 -8
- package/package.json +2 -2
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BlockStdScope } from '@formicoidea/labre-core/std';
|
|
2
|
+
export type EdgyBoxKind = 'outcome' | 'object' | 'activity';
|
|
3
|
+
/**
|
|
4
|
+
* Create the Enterprise Design Facets diagram centred on the viewport —
|
|
5
|
+
* cropped to the circles + facet labels (no dead margins around the Venn).
|
|
6
|
+
*/
|
|
7
|
+
export declare function createEdgyFacets(std: BlockStdScope): void;
|
|
8
|
+
/**
|
|
9
|
+
* Insert the "EDGY dynamic" template (facets background + the 12 elements
|
|
10
|
+
* linked by the 24 verbalised relations), then bring it into view.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createEdgyDynamic(std: BlockStdScope): Promise<void>;
|
|
13
|
+
/** Create a blank EDGY board (hover spotlight host) centred on the viewport. */
|
|
14
|
+
export declare function createEdgyBoard(std: BlockStdScope): void;
|
|
15
|
+
/** Create a box base element (outcome / object / activity) with inner text. */
|
|
16
|
+
export declare function createEdgyBox(std: BlockStdScope, kind: EdgyBoxKind): void;
|
|
17
|
+
/**
|
|
18
|
+
* Create the People base element: an (invisible) ellipse decorated with the
|
|
19
|
+
* person glyph by the renderer, plus a native text label below, grouped.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createEdgyPeople(std: BlockStdScope): void;
|
package/dist/actions.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { DefaultTool } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { createGroupCommand } from '@formicoidea/labre-core/gfx/group';
|
|
3
|
+
import { createTemplateJob } from '@formicoidea/labre-core/gfx/template';
|
|
4
|
+
import { FontFamily, ShapeStyle } from '@formicoidea/labre-core/model';
|
|
5
|
+
import { Bound } from '@formicoidea/labre-core/global/gfx';
|
|
6
|
+
import { GfxControllerIdentifier } from '@formicoidea/labre-core/std/gfx';
|
|
7
|
+
import { CROP_LABELED } from './consts.js';
|
|
8
|
+
import { ACTIVITY_VERTICES, INNER_FONT_SIZE, LABEL_FONT_SIZE, LABEL_GAP, NODE_FILL, NODE_LABEL, NODE_SIZE, NODE_STROKE, NODE_STROKE_WIDTH, OUTCOME_RADIUS, } from './node/consts.js';
|
|
9
|
+
import { edgyDynamicTemplate } from './templates/index.js';
|
|
10
|
+
/** Default facets-diagram size (REF aspect, scaled up so it reads on canvas). */
|
|
11
|
+
const FACETS_SCALE = 1.5;
|
|
12
|
+
/** Default blank-board size. */
|
|
13
|
+
const BOARD_W = 1600;
|
|
14
|
+
const BOARD_H = 1000;
|
|
15
|
+
/** Height of the native People free-text label. */
|
|
16
|
+
const LABEL_H = LABEL_FONT_SIZE + 8;
|
|
17
|
+
const gfxOf = (std) => std.get(GfxControllerIdentifier);
|
|
18
|
+
function finish(gfx, id) {
|
|
19
|
+
gfx.doc.captureSync();
|
|
20
|
+
gfx.tool.setTool(DefaultTool);
|
|
21
|
+
gfx.selection.set({ elements: [id], editing: false });
|
|
22
|
+
// Keep the palette open (native sub-menu behaviour).
|
|
23
|
+
}
|
|
24
|
+
/** Shared props for an EDGY node shape. */
|
|
25
|
+
const baseShapeProps = (kind) => ({
|
|
26
|
+
type: 'edgyNode',
|
|
27
|
+
kind,
|
|
28
|
+
filled: true,
|
|
29
|
+
fillColor: NODE_FILL,
|
|
30
|
+
strokeColor: NODE_STROKE,
|
|
31
|
+
shapeStyle: ShapeStyle.General,
|
|
32
|
+
roughness: 0,
|
|
33
|
+
});
|
|
34
|
+
/** Add a native free-text label (Inter), used for the People node. */
|
|
35
|
+
function addLabel(surface, text, x, y) {
|
|
36
|
+
return surface.addElement({
|
|
37
|
+
type: 'text',
|
|
38
|
+
text,
|
|
39
|
+
fontFamily: FontFamily.Inter,
|
|
40
|
+
fontSize: LABEL_FONT_SIZE,
|
|
41
|
+
color: NODE_STROKE,
|
|
42
|
+
textAlign: 'center',
|
|
43
|
+
xywh: new Bound(x, y, 120, LABEL_H).serialize(),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Create the Enterprise Design Facets diagram centred on the viewport —
|
|
48
|
+
* cropped to the circles + facet labels (no dead margins around the Venn).
|
|
49
|
+
*/
|
|
50
|
+
export function createEdgyFacets(std) {
|
|
51
|
+
const gfx = gfxOf(std);
|
|
52
|
+
if (!gfx.surface)
|
|
53
|
+
return;
|
|
54
|
+
const width = CROP_LABELED.w * FACETS_SCALE;
|
|
55
|
+
const height = CROP_LABELED.h * FACETS_SCALE;
|
|
56
|
+
const { centerX, centerY } = gfx.viewport;
|
|
57
|
+
const id = gfx.surface.addElement({
|
|
58
|
+
type: 'edgy',
|
|
59
|
+
cropToCircles: true,
|
|
60
|
+
xywh: new Bound(centerX - width / 2, centerY - height / 2, width, height).serialize(),
|
|
61
|
+
});
|
|
62
|
+
finish(gfx, id);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Insert the "EDGY dynamic" template (facets background + the 12 elements
|
|
66
|
+
* linked by the 24 verbalised relations), then bring it into view.
|
|
67
|
+
*/
|
|
68
|
+
export async function createEdgyDynamic(std) {
|
|
69
|
+
const gfx = gfxOf(std);
|
|
70
|
+
if (!gfx.surface)
|
|
71
|
+
return;
|
|
72
|
+
const job = createTemplateJob(std, 'template');
|
|
73
|
+
const bound = await job.insertTemplate(structuredClone(edgyDynamicTemplate.content));
|
|
74
|
+
if (bound) {
|
|
75
|
+
const padding = 20 / gfx.viewport.zoom;
|
|
76
|
+
gfx.viewport.setViewportByBound(bound, [padding, padding, padding, padding], true);
|
|
77
|
+
}
|
|
78
|
+
gfx.doc.captureSync();
|
|
79
|
+
gfx.tool.setTool(DefaultTool);
|
|
80
|
+
}
|
|
81
|
+
/** Create a blank EDGY board (hover spotlight host) centred on the viewport. */
|
|
82
|
+
export function createEdgyBoard(std) {
|
|
83
|
+
const gfx = gfxOf(std);
|
|
84
|
+
if (!gfx.surface)
|
|
85
|
+
return;
|
|
86
|
+
const { centerX, centerY } = gfx.viewport;
|
|
87
|
+
const id = gfx.surface.addElement({
|
|
88
|
+
type: 'edgyBoard',
|
|
89
|
+
xywh: new Bound(centerX - BOARD_W / 2, centerY - BOARD_H / 2, BOARD_W, BOARD_H).serialize(),
|
|
90
|
+
});
|
|
91
|
+
finish(gfx, id);
|
|
92
|
+
}
|
|
93
|
+
/** Create a box base element (outcome / object / activity) with inner text. */
|
|
94
|
+
export function createEdgyBox(std, kind) {
|
|
95
|
+
const gfx = gfxOf(std);
|
|
96
|
+
const surface = gfx.surface;
|
|
97
|
+
if (!surface)
|
|
98
|
+
return;
|
|
99
|
+
const { w, h } = NODE_SIZE[kind];
|
|
100
|
+
const { centerX: cx, centerY: cy } = gfx.viewport;
|
|
101
|
+
const shapeType = kind === 'activity' ? 'polygon' : 'rect';
|
|
102
|
+
const id = surface.addElement({
|
|
103
|
+
...baseShapeProps(kind),
|
|
104
|
+
shapeType,
|
|
105
|
+
strokeWidth: NODE_STROKE_WIDTH,
|
|
106
|
+
radius: kind === 'outcome' ? OUTCOME_RADIUS : 0,
|
|
107
|
+
vertices: kind === 'activity' ? ACTIVITY_VERTICES : null,
|
|
108
|
+
text: NODE_LABEL[kind],
|
|
109
|
+
color: NODE_STROKE,
|
|
110
|
+
fontFamily: FontFamily.Inter,
|
|
111
|
+
fontSize: INNER_FONT_SIZE,
|
|
112
|
+
textAlign: 'center',
|
|
113
|
+
xywh: new Bound(cx - w / 2, cy - h / 2, w, h).serialize(),
|
|
114
|
+
});
|
|
115
|
+
finish(gfx, id);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Create the People base element: an (invisible) ellipse decorated with the
|
|
119
|
+
* person glyph by the renderer, plus a native text label below, grouped.
|
|
120
|
+
*/
|
|
121
|
+
export function createEdgyPeople(std) {
|
|
122
|
+
const gfx = gfxOf(std);
|
|
123
|
+
const surface = gfx.surface;
|
|
124
|
+
if (!surface)
|
|
125
|
+
return;
|
|
126
|
+
const { w, h } = NODE_SIZE.people;
|
|
127
|
+
const { centerX: cx, centerY: cy } = gfx.viewport;
|
|
128
|
+
const nodeId = surface.addElement({
|
|
129
|
+
...baseShapeProps('people'),
|
|
130
|
+
shapeType: 'ellipse',
|
|
131
|
+
// No visible outline — People is just the glyph; the ellipse is the bound.
|
|
132
|
+
strokeWidth: 0,
|
|
133
|
+
xywh: new Bound(cx - w / 2, cy - h / 2, w, h).serialize(),
|
|
134
|
+
});
|
|
135
|
+
const labelId = addLabel(surface, NODE_LABEL.people, cx - 60, cy + h / 2 + LABEL_GAP);
|
|
136
|
+
const [, result] = std.command.exec(createGroupCommand, {
|
|
137
|
+
elements: [nodeId, labelId],
|
|
138
|
+
});
|
|
139
|
+
finish(gfx, result.groupId || nodeId);
|
|
140
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ElementRenderer } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import type { EdgyBoardElementModel } from '@formicoidea/labre-core/model';
|
|
3
|
+
/**
|
|
4
|
+
* Canvas renderer for the blank EDGY board: a plain white rounded rectangle
|
|
5
|
+
* with a discreet border. The board only exists to host free-form EDGY
|
|
6
|
+
* modelling and grant the spotlight-on-hover behavior to the elements laid on
|
|
7
|
+
* top of it.
|
|
8
|
+
*/
|
|
9
|
+
export declare const edgyBoard: ElementRenderer<EdgyBoardElementModel>;
|
|
10
|
+
export declare const EdgyBoardRendererExtension: import("@formicoidea/labre-core/store").ExtensionType & {
|
|
11
|
+
identifier: import("@formicoidea/labre-core/_pkgs/global/di").ServiceIdentifier<ElementRenderer<EdgyBoardElementModel>>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ElementRendererExtension, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
const BOARD_FILL = '#ffffff';
|
|
3
|
+
const BOARD_BORDER = '#e0e0e0';
|
|
4
|
+
const BOARD_RADIUS = 16;
|
|
5
|
+
const BOARD_BORDER_WIDTH = 2;
|
|
6
|
+
/**
|
|
7
|
+
* Canvas renderer for the blank EDGY board: a plain white rounded rectangle
|
|
8
|
+
* with a discreet border. The board only exists to host free-form EDGY
|
|
9
|
+
* modelling and grant the spotlight-on-hover behavior to the elements laid on
|
|
10
|
+
* top of it.
|
|
11
|
+
*/
|
|
12
|
+
export const edgyBoard = (model, ctx, matrix) => {
|
|
13
|
+
const [, , w, h] = model.deserializedXYWH;
|
|
14
|
+
const cx = w / 2;
|
|
15
|
+
const cy = h / 2;
|
|
16
|
+
ctx.setTransform(matrix.translateSelf(cx, cy).rotateSelf(model.rotate).translateSelf(-cx, -cy));
|
|
17
|
+
ctx.beginPath();
|
|
18
|
+
ctx.roundRect(0, 0, w, h, BOARD_RADIUS);
|
|
19
|
+
ctx.fillStyle = BOARD_FILL;
|
|
20
|
+
ctx.fill();
|
|
21
|
+
ctx.strokeStyle = BOARD_BORDER;
|
|
22
|
+
ctx.lineWidth = BOARD_BORDER_WIDTH;
|
|
23
|
+
ctx.stroke();
|
|
24
|
+
};
|
|
25
|
+
export const EdgyBoardRendererExtension = ElementRendererExtension('edgyBoard', edgyBoard);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EdgyBoardElementModel } from '@formicoidea/labre-core/model';
|
|
2
|
+
import { GfxElementModelView } from '@formicoidea/labre-core/std/gfx';
|
|
3
|
+
export declare class EdgyBoardView extends GfxElementModelView<EdgyBoardElementModel> {
|
|
4
|
+
static type: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Resize gating: the resize handles are hidden unless `model.resizeEnabled` is
|
|
8
|
+
* true. Moving/selecting stays available throughout (same as the facets
|
|
9
|
+
* diagram).
|
|
10
|
+
*/
|
|
11
|
+
export declare const EdgyBoardInteraction: import("@formicoidea/labre-core/store").ExtensionType;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { GfxElementModelView, GfxViewInteractionExtension, } from '@formicoidea/labre-core/std/gfx';
|
|
2
|
+
export class EdgyBoardView extends GfxElementModelView {
|
|
3
|
+
static { this.type = 'edgyBoard'; }
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Resize gating: the resize handles are hidden unless `model.resizeEnabled` is
|
|
7
|
+
* true. Moving/selecting stays available throughout (same as the facets
|
|
8
|
+
* diagram).
|
|
9
|
+
*/
|
|
10
|
+
export const EdgyBoardInteraction = GfxViewInteractionExtension(EdgyBoardView.type, {
|
|
11
|
+
handleResize({ model }) {
|
|
12
|
+
return {
|
|
13
|
+
beforeResize({ set }) {
|
|
14
|
+
if (!model.resizeEnabled) {
|
|
15
|
+
set({ allowedHandlers: [] });
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
});
|
package/dist/commands.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { createEdgyBoard, createEdgyBox, createEdgyDynamic, createEdgyFacets, createEdgyPeople, } from './actions.js';
|
|
2
|
+
import { edgyActivityIcon, edgyBoardIcon, edgyDynamicIcon, edgyFacetsIcon, edgyObjectIcon, edgyOutcomeIcon, edgyPeopleIcon, } from './toolbar/icons.js';
|
|
3
|
+
const SPECS = [
|
|
4
|
+
{
|
|
5
|
+
id: 'addFacets',
|
|
6
|
+
label: 'Enterprise Design facets',
|
|
7
|
+
iconKey: 'edgy.facets',
|
|
8
|
+
category: 'diagrams',
|
|
9
|
+
element: 'facets',
|
|
10
|
+
run: createEdgyFacets,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: 'insertDynamic',
|
|
14
|
+
label: 'EDGY dynamic (elements & relations)',
|
|
15
|
+
iconKey: 'edgy.dynamic',
|
|
16
|
+
category: 'diagrams',
|
|
17
|
+
element: 'template:dynamic',
|
|
18
|
+
run: createEdgyDynamic,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 'addBoard',
|
|
22
|
+
label: 'EDGY board (hover spotlight)',
|
|
23
|
+
iconKey: 'edgy.board',
|
|
24
|
+
category: 'diagrams',
|
|
25
|
+
element: 'board',
|
|
26
|
+
run: createEdgyBoard,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'addPeople',
|
|
30
|
+
label: 'People',
|
|
31
|
+
iconKey: 'edgy.people',
|
|
32
|
+
category: 'elements',
|
|
33
|
+
element: 'node:people',
|
|
34
|
+
run: createEdgyPeople,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'addOutcome',
|
|
38
|
+
label: 'Outcome',
|
|
39
|
+
iconKey: 'edgy.outcome',
|
|
40
|
+
category: 'elements',
|
|
41
|
+
element: 'node:outcome',
|
|
42
|
+
run: std => createEdgyBox(std, 'outcome'),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'addObject',
|
|
46
|
+
label: 'Object',
|
|
47
|
+
iconKey: 'edgy.object',
|
|
48
|
+
category: 'elements',
|
|
49
|
+
element: 'node:object',
|
|
50
|
+
run: std => createEdgyBox(std, 'object'),
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'addActivity',
|
|
54
|
+
label: 'Activity',
|
|
55
|
+
iconKey: 'edgy.activity',
|
|
56
|
+
category: 'elements',
|
|
57
|
+
element: 'node:activity',
|
|
58
|
+
run: std => createEdgyBox(std, 'activity'),
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
export const edgyCommands = SPECS.map((spec, order) => ({
|
|
62
|
+
id: `edgy.${spec.id}`,
|
|
63
|
+
owner: 'edgy',
|
|
64
|
+
kind: 'artefact',
|
|
65
|
+
labelKey: `com.labre.commands.edgy.${spec.id}`,
|
|
66
|
+
labelFallback: spec.label,
|
|
67
|
+
category: spec.category,
|
|
68
|
+
iconKey: spec.iconKey,
|
|
69
|
+
surfaces: ['senior-menu', 'catalogue', 'palette', 'agent'],
|
|
70
|
+
order,
|
|
71
|
+
scope: 'edgeless',
|
|
72
|
+
defaultKeys: { mac: [], other: [] },
|
|
73
|
+
availability: 'always',
|
|
74
|
+
run: spec.run,
|
|
75
|
+
telemetry: { framework: 'edgy', element: spec.element },
|
|
76
|
+
}));
|
|
77
|
+
export const edgyCommandIcons = {
|
|
78
|
+
'edgy.facets': edgyFacetsIcon,
|
|
79
|
+
'edgy.dynamic': edgyDynamicIcon,
|
|
80
|
+
'edgy.board': edgyBoardIcon,
|
|
81
|
+
'edgy.people': edgyPeopleIcon,
|
|
82
|
+
'edgy.outcome': edgyOutcomeIcon,
|
|
83
|
+
'edgy.object': edgyObjectIcon,
|
|
84
|
+
'edgy.activity': edgyActivityIcon,
|
|
85
|
+
};
|
package/dist/consts.d.ts
CHANGED
|
@@ -44,3 +44,38 @@ export declare function refScale(w: number, h: number): {
|
|
|
44
44
|
ox: number;
|
|
45
45
|
oy: number;
|
|
46
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* Bounding box of the three circles in reference coords (small padding). The
|
|
49
|
+
* REF margins around it only exist for the facet name labels — when a diagram
|
|
50
|
+
* hides them (`cropToCircles`), the renderer fits THIS box into the element
|
|
51
|
+
* bounds instead, so the background hugs the Venn.
|
|
52
|
+
*/
|
|
53
|
+
export declare const CROP: {
|
|
54
|
+
x: number;
|
|
55
|
+
y: number;
|
|
56
|
+
w: number;
|
|
57
|
+
h: number;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Crop box KEEPING room for the three facet name labels (drawn outside the
|
|
61
|
+
* circles): the circles box plus a fixed text allowance left/right and below.
|
|
62
|
+
* Used when a cropped diagram still shows its labels.
|
|
63
|
+
*/
|
|
64
|
+
export declare const CROP_LABELED: {
|
|
65
|
+
x: number;
|
|
66
|
+
y: number;
|
|
67
|
+
w: number;
|
|
68
|
+
h: number;
|
|
69
|
+
};
|
|
70
|
+
/** `refScale` variant fitting the cropped circles box (see {@link CROP}). */
|
|
71
|
+
export declare const cropScale: (w: number, h: number) => {
|
|
72
|
+
s: number;
|
|
73
|
+
ox: number;
|
|
74
|
+
oy: number;
|
|
75
|
+
};
|
|
76
|
+
/** `refScale` variant fitting the circles + facet labels box. */
|
|
77
|
+
export declare const cropLabeledScale: (w: number, h: number) => {
|
|
78
|
+
s: number;
|
|
79
|
+
ox: number;
|
|
80
|
+
oy: number;
|
|
81
|
+
};
|
package/dist/consts.js
CHANGED
|
@@ -43,3 +43,49 @@ export function refScale(w, h) {
|
|
|
43
43
|
const s = Math.min(w / REF_W, h / REF_H);
|
|
44
44
|
return { s, ox: (w - REF_W * s) / 2, oy: (h - REF_H * s) / 2 };
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Bounding box of the three circles in reference coords (small padding). The
|
|
48
|
+
* REF margins around it only exist for the facet name labels — when a diagram
|
|
49
|
+
* hides them (`cropToCircles`), the renderer fits THIS box into the element
|
|
50
|
+
* bounds instead, so the background hugs the Venn.
|
|
51
|
+
*/
|
|
52
|
+
export const CROP = (() => {
|
|
53
|
+
const pad = 8;
|
|
54
|
+
const ax = VENN.cx - 0.866 * VENN.r0; // Identity / Architecture centres
|
|
55
|
+
const abY = VENN.cy - 0.5 * VENN.r0;
|
|
56
|
+
const cY = VENN.cy + VENN.r0; // Experience centre
|
|
57
|
+
const minX = ax - VENN.R - pad;
|
|
58
|
+
const maxX = VENN.cx + 0.866 * VENN.r0 + VENN.R + pad;
|
|
59
|
+
const minY = abY - VENN.R - pad;
|
|
60
|
+
const maxY = cY + VENN.R + pad;
|
|
61
|
+
return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
|
|
62
|
+
})();
|
|
63
|
+
/**
|
|
64
|
+
* Crop box KEEPING room for the three facet name labels (drawn outside the
|
|
65
|
+
* circles): the circles box plus a fixed text allowance left/right and below.
|
|
66
|
+
* Used when a cropped diagram still shows its labels.
|
|
67
|
+
*/
|
|
68
|
+
export const CROP_LABELED = (() => {
|
|
69
|
+
const sideAllowance = 140; // label offset (10) + text width headroom
|
|
70
|
+
const bottomAllowance = 35; // experience label below the bottom circle
|
|
71
|
+
return {
|
|
72
|
+
x: CROP.x - sideAllowance,
|
|
73
|
+
y: CROP.y,
|
|
74
|
+
w: CROP.w + 2 * sideAllowance,
|
|
75
|
+
h: CROP.h + bottomAllowance,
|
|
76
|
+
};
|
|
77
|
+
})();
|
|
78
|
+
function fitScale(box) {
|
|
79
|
+
return (w, h) => {
|
|
80
|
+
const s = Math.min(w / box.w, h / box.h);
|
|
81
|
+
return {
|
|
82
|
+
s,
|
|
83
|
+
ox: (w - box.w * s) / 2 - box.x * s,
|
|
84
|
+
oy: (h - box.h * s) / 2 - box.y * s,
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** `refScale` variant fitting the cropped circles box (see {@link CROP}). */
|
|
89
|
+
export const cropScale = fitScale(CROP);
|
|
90
|
+
/** `refScale` variant fitting the circles + facet labels box. */
|
|
91
|
+
export const cropLabeledScale = fitScale(CROP_LABELED);
|
package/dist/descriptor.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { EdgyViewExtension } from './view.js';
|
|
1
|
+
import { EdgyRenderViewExtension, EdgyViewExtension } from './view.js';
|
|
2
2
|
/** Host wiring for the edgy framework. */
|
|
3
3
|
export declare const edgyFramework: {
|
|
4
4
|
readonly flag: "edgy";
|
|
5
|
-
readonly
|
|
6
|
-
readonly
|
|
5
|
+
readonly telemetryKey: "edgy";
|
|
6
|
+
readonly extensions: readonly [{
|
|
7
|
+
readonly viewExtension: typeof EdgyRenderViewExtension;
|
|
8
|
+
}, {
|
|
9
|
+
readonly flag: "edgy";
|
|
10
|
+
readonly viewExtension: typeof EdgyViewExtension;
|
|
11
|
+
}];
|
|
7
12
|
};
|
package/dist/descriptor.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { EdgyViewExtension } from './view.js';
|
|
1
|
+
import { EdgyRenderViewExtension, EdgyViewExtension } from './view.js';
|
|
2
2
|
/** Host wiring for the edgy framework. */
|
|
3
3
|
export const edgyFramework = {
|
|
4
4
|
flag: 'edgy',
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
telemetryKey: 'edgy',
|
|
6
|
+
extensions: [
|
|
7
|
+
{ viewExtension: EdgyRenderViewExtension },
|
|
8
|
+
{ flag: 'edgy', viewExtension: EdgyViewExtension },
|
|
9
|
+
],
|
|
7
10
|
};
|
package/dist/element-renderer.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ElementRendererExtension, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
-
import { COLORS, FONT_FAMILY, LABEL_FONT_SIZE, PICTO_STROKE, REF_H, REF_W, refScale, VENN, } from './consts.js';
|
|
2
|
+
import { COLORS, cropLabeledScale, cropScale, FONT_FAMILY, LABEL_FONT_SIZE, PICTO_STROKE, REF_H, REF_W, refScale, VENN, } from './consts.js';
|
|
3
3
|
import { CIRCLE_A, CIRCLE_B, CIRCLE_C, facetLabelAnchors } from './label-layout.js';
|
|
4
4
|
/**
|
|
5
5
|
* Canvas renderer for the EDGY Enterprise Design Facets diagram — reproduces the
|
|
@@ -16,8 +16,14 @@ export const edgy = (model, ctx, matrix) => {
|
|
|
16
16
|
const cx = w / 2;
|
|
17
17
|
const cy = h / 2;
|
|
18
18
|
ctx.setTransform(matrix.translateSelf(cx, cy).rotateSelf(model.rotate).translateSelf(-cx, -cy));
|
|
19
|
-
// Uniform fit of the reference design, centered (letterboxed)
|
|
20
|
-
|
|
19
|
+
// Uniform fit of the reference design, centered (letterboxed) — or, when
|
|
20
|
+
// the diagram is cropped, of the circles' bounding box (plus the facet
|
|
21
|
+
// label allowance if the labels are shown).
|
|
22
|
+
const { s, ox, oy } = model.cropToCircles
|
|
23
|
+
? model.showLabels
|
|
24
|
+
? cropLabeledScale(w, h)
|
|
25
|
+
: cropScale(w, h)
|
|
26
|
+
: refScale(w, h);
|
|
21
27
|
ctx.translate(ox, oy);
|
|
22
28
|
ctx.scale(s, s);
|
|
23
29
|
const A = CIRCLE_A;
|
|
@@ -149,14 +155,16 @@ export const edgy = (model, ctx, matrix) => {
|
|
|
149
155
|
ctx.lineTo(x, y + 12);
|
|
150
156
|
ctx.stroke();
|
|
151
157
|
};
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
if (model.showPictos) {
|
|
159
|
+
// Single-facet zone pictos.
|
|
160
|
+
lens(A.x - 30, A.y - 22);
|
|
161
|
+
house(B.x + 30, B.y - 22);
|
|
162
|
+
heart(C.x, C.y + 36);
|
|
163
|
+
// Intersection pictos at the validated sweet spots.
|
|
164
|
+
network(VENN.cx, 114);
|
|
165
|
+
sun(VENN.cx - 58, 206);
|
|
166
|
+
cube(VENN.cx + 58, 206);
|
|
167
|
+
}
|
|
160
168
|
// ── Labels (outside the circles) ────────────────────────────────────
|
|
161
169
|
if (model.showLabels) {
|
|
162
170
|
ctx.font = `500 ${LABEL_FONT_SIZE}px ${FONT_FAMILY}`;
|
package/dist/element-view.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { rotatePoint } from '@formicoidea/labre-core/global/gfx';
|
|
3
3
|
import { GfxElementModelView, GfxViewInteractionExtension, } from '@formicoidea/labre-core/std/gfx';
|
|
4
|
-
import { refScale } from './consts.js';
|
|
4
|
+
import { cropLabeledScale, refScale } from './consts.js';
|
|
5
5
|
import { getEdgyLabelHits, hitTestEdgyLabel, } from './label-layout.js';
|
|
6
6
|
export class EdgyView extends GfxElementModelView {
|
|
7
7
|
constructor() {
|
|
@@ -22,6 +22,9 @@ export class EdgyView extends GfxElementModelView {
|
|
|
22
22
|
_onDblClick(e) {
|
|
23
23
|
if (this.model.isLocked())
|
|
24
24
|
return;
|
|
25
|
+
// No labels shown → nothing to edit (and the cropped mapping differs).
|
|
26
|
+
if (!this.model.showLabels)
|
|
27
|
+
return;
|
|
25
28
|
const [mx, my] = this.gfx.viewport.toModelCoord(e.x, e.y);
|
|
26
29
|
const [bx, by, w, h] = this.model.deserializedXYWH;
|
|
27
30
|
// Convert the model-space point into element-local coordinates, undoing the
|
|
@@ -35,8 +38,11 @@ export class EdgyView extends GfxElementModelView {
|
|
|
35
38
|
lx = ux - bx;
|
|
36
39
|
ly = uy - by;
|
|
37
40
|
}
|
|
38
|
-
// Map element-local coords into the fixed reference space the labels live
|
|
39
|
-
|
|
41
|
+
// Map element-local coords into the fixed reference space the labels live
|
|
42
|
+
// in — with the SAME fit the renderer used (cropped or letterboxed).
|
|
43
|
+
const { s, ox, oy } = this.model.cropToCircles
|
|
44
|
+
? cropLabeledScale(w, h)
|
|
45
|
+
: refScale(w, h);
|
|
40
46
|
const rx = (lx - ox) / s;
|
|
41
47
|
const ry = (ly - oy) / s;
|
|
42
48
|
const hit = hitTestEdgyLabel(getEdgyLabelHits(this.model), rx, ry);
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export {};
|
|
1
|
+
export { edgyCommandIcons, edgyCommands } from './commands.js';
|
|
2
|
+
export { edgyTranslationEntries } from './translations.js';
|
package/dist/index.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export {};
|
|
1
|
+
export { edgyCommandIcons, edgyCommands } from './commands.js';
|
|
2
|
+
export { edgyTranslationEntries } from './translations.js';
|
|
@@ -1,2 +1,31 @@
|
|
|
1
|
-
import { type TemplateCategory } from '@formicoidea/labre-core/gfx/template';
|
|
1
|
+
import { type Template, type TemplateCategory } from '@formicoidea/labre-core/gfx/template';
|
|
2
|
+
/**
|
|
3
|
+
* Background scale (from REF coords to model coords): large enough that the
|
|
4
|
+
* default-size nodes breathe inside each zone. The background is cropped to
|
|
5
|
+
* the circles (`cropToCircles`), so its xywh covers CROP × DYN_SCALE.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DYN_SCALE = 4.8;
|
|
8
|
+
/**
|
|
9
|
+
* The 24 canonical EDGY relations (source, target, verb, label position along
|
|
10
|
+
* the link) — exported for the unit tests. 7 per facet + 3 between the
|
|
11
|
+
* intersections. The optional 4th member mirrors the reference diagram's
|
|
12
|
+
* placements: verbs of intersection-outgoing links sit near the far element
|
|
13
|
+
* (`labelOffset.distance` ≈ .75), short peer links keep the middle.
|
|
14
|
+
*/
|
|
15
|
+
export declare const EDGY_DYNAMIC_RELATIONS: [string, string, string, number?][];
|
|
16
|
+
export declare const EDGY_DYNAMIC_NODES: Record<string, {
|
|
17
|
+
kind: 'outcome' | 'object' | 'activity';
|
|
18
|
+
cx: number;
|
|
19
|
+
cy: number;
|
|
20
|
+
w?: number;
|
|
21
|
+
fill: string;
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* Reference coords → template model coords: the background element sits at
|
|
25
|
+
* (0,0) and renders the cropped circles box, so a reference point maps to
|
|
26
|
+
* `(p - CROP.origin) × DYN_SCALE`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function dynToModel(refX: number, refY: number): [number, number];
|
|
29
|
+
/** Exported for direct insertion from the EDGY senior toolbar menu. */
|
|
30
|
+
export declare const edgyDynamicTemplate: Template;
|
|
2
31
|
export declare const edgyTemplateCategory: TemplateCategory;
|