@formicoidea/labre-framework-edgy 0.41.0 → 0.43.1
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/LICENSE +384 -0
- package/dist/commands.d.ts +3 -0
- package/dist/commands.js +114 -1
- package/dist/consts.d.ts +28 -11
- package/dist/consts.js +97 -23
- package/dist/element-view.d.ts +20 -22
- package/dist/element-view.js +27 -116
- package/dist/node/node-renderer.js +2 -2
- package/dist/templates/dynamic.js +5 -1
- package/dist/templates/index.js +1 -1
- package/dist/toolbar/config.d.ts +5 -11
- package/dist/toolbar/config.js +54 -39
- package/dist/toolbar/edgy-senior-button.d.ts +2 -0
- package/dist/toolbar/edgy-senior-button.js +6 -2
- package/dist/toolbar/node-config.d.ts +14 -0
- package/dist/toolbar/node-config.js +12 -1
- package/dist/translations.js +1 -2
- package/dist/view.js +21 -21
- package/package.json +2 -3
- package/dist/legend.d.ts +0 -11
- package/dist/legend.js +0 -133
package/dist/consts.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* uniformly to the element bounds — so the three circles always stay circular
|
|
7
7
|
* and the pictos/labels keep their relative proportions at any size.
|
|
8
8
|
*/
|
|
9
|
+
import { refScale as fitReference } from '@formicoidea/labre-core/blocks/surface';
|
|
9
10
|
/** Reference design size (the validated mockup canvas). */
|
|
10
11
|
export const REF_W = 680;
|
|
11
12
|
export const REF_H = 400;
|
|
@@ -38,43 +39,116 @@ export const FONT_FAMILY = 'Inter, sans-serif';
|
|
|
38
39
|
* Uniform fit of the reference design into an element of size `w × h`: the
|
|
39
40
|
* scale factor plus the centering offsets (letterboxed). Shared by the renderer
|
|
40
41
|
* (to draw) and the view (to map clicks back into reference coords).
|
|
42
|
+
*
|
|
43
|
+
* The fit itself is the library's ({@link fitReference}, R37): only the
|
|
44
|
+
* reference size is this framework's business.
|
|
41
45
|
*/
|
|
42
46
|
export function refScale(w, h) {
|
|
43
|
-
|
|
44
|
-
return { s, ox: (w - REF_W * s) / 2, oy: (h - REF_H * s) / 2 };
|
|
47
|
+
return fitReference(w, h, REF_W, REF_H);
|
|
45
48
|
}
|
|
46
49
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* bounds instead, so the background hugs the Venn.
|
|
50
|
+
* The three circles, and nothing else: their exact bounding box in reference
|
|
51
|
+
* coords. Both crop boxes below are this box plus what each of them has to make
|
|
52
|
+
* room for, so the two can never disagree about where the Venn is.
|
|
51
53
|
*/
|
|
52
|
-
|
|
53
|
-
const pad = 8;
|
|
54
|
+
const CIRCLES = (() => {
|
|
54
55
|
const ax = VENN.cx - 0.866 * VENN.r0; // Identity / Architecture centres
|
|
55
56
|
const abY = VENN.cy - 0.5 * VENN.r0;
|
|
56
57
|
const cY = VENN.cy + VENN.r0; // Experience centre
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
return {
|
|
59
|
+
minX: ax - VENN.R,
|
|
60
|
+
maxX: VENN.cx + 0.866 * VENN.r0 + VENN.R,
|
|
61
|
+
minY: abY - VENN.R,
|
|
62
|
+
maxY: cY + VENN.R,
|
|
63
|
+
};
|
|
62
64
|
})();
|
|
63
65
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
66
|
+
* Bounding box of the three circles in reference coords, with just enough
|
|
67
|
+
* padding to see their outline. When a diagram hides its facet names
|
|
68
|
+
* (`cropToCircles` without `showLabels`) the renderer fits THIS box into the
|
|
69
|
+
* element bounds, so the background hugs the Venn.
|
|
70
|
+
*
|
|
71
|
+
* ## Why the padding is 2.5 and not the 8 it was
|
|
72
|
+
*
|
|
73
|
+
* The padding is the ONLY empty band such a board has, and it is spent at the
|
|
74
|
+
* element's scale, not at the reference one. The EDGY dynamic template draws
|
|
75
|
+
* its board at `DYN_SCALE = 4.8`, so eight reference units of padding were
|
|
76
|
+
* **32.4 model units** of nothing on each of the four sides — the board a user
|
|
77
|
+
* has to catch by its border (R37) sat a good inch away from the circles, which
|
|
78
|
+
* is what the recette of 23/09/2026 reported. 2.5 leaves 1.25 past the circles'
|
|
79
|
+
* own 2.5-wide outline, i.e. 6 model units at the dynamic board's scale and
|
|
80
|
+
* under 2 at a facets board's.
|
|
81
|
+
*
|
|
82
|
+
* `templates/dynamic.ts` places every element of the dynamic board in
|
|
83
|
+
* coordinates relative to this box (`dynToModel`) and sizes the board from it,
|
|
84
|
+
* so the template follows in the same breath: both are derived here, and a
|
|
85
|
+
* freshly inserted template is aligned by construction. A board built from the
|
|
86
|
+
* OLD template keeps its `xywh` and sees the Venn grow 3.8 % inside it — see
|
|
87
|
+
* the changeset.
|
|
67
88
|
*/
|
|
68
|
-
export const
|
|
69
|
-
const
|
|
70
|
-
const bottomAllowance = 35; // experience label below the bottom circle
|
|
89
|
+
export const CROP = (() => {
|
|
90
|
+
const pad = 2.5;
|
|
71
91
|
return {
|
|
72
|
-
x:
|
|
73
|
-
y:
|
|
74
|
-
w:
|
|
75
|
-
h:
|
|
92
|
+
x: CIRCLES.minX - pad,
|
|
93
|
+
y: CIRCLES.minY - pad,
|
|
94
|
+
w: CIRCLES.maxX - CIRCLES.minX + 2 * pad,
|
|
95
|
+
h: CIRCLES.maxY - CIRCLES.minY + 2 * pad,
|
|
76
96
|
};
|
|
77
97
|
})();
|
|
98
|
+
/**
|
|
99
|
+
* How far past the circles each side of the labelled box has to reach, in REF
|
|
100
|
+
* units. MEASURED on the canvas, in the shipped wording, at
|
|
101
|
+
* {@link LABEL_FONT_SIZE}; each number is that reach plus 4 or 5 units of
|
|
102
|
+
* gutter.
|
|
103
|
+
*
|
|
104
|
+
* It used to be one symmetric number — `sideAllowance = 140` either side of the
|
|
105
|
+
* padded box, `bottomAllowance = 35` under it — and the three words are not
|
|
106
|
+
* symmetric at all. "Identity" is written right-aligned 10 left of the left
|
|
107
|
+
* circle and reaches 63.6 past the circles; "Architecture" is written
|
|
108
|
+
* left-aligned 10 right of the right circle and reaches 98.1; "Experience"
|
|
109
|
+
* hangs 29.2 below the bottom one (a `middle` baseline plus the descender of
|
|
110
|
+
* its "p"); and above them there is no word at all, only the 1.25 of circle
|
|
111
|
+
* stroke. So a facets diagram was born inside 126.6 model units of nothing on
|
|
112
|
+
* its left and 74.8 on its right — and a framework background is caught by its
|
|
113
|
+
* border and by nothing else
|
|
114
|
+
* (`model/src/elements/framework-background/hit-test.ts`), which put the
|
|
115
|
+
* catchable edge a seventh of the board away from anything drawn.
|
|
116
|
+
*
|
|
117
|
+
* ## Why constants rather than a measurement at paint time
|
|
118
|
+
*
|
|
119
|
+
* Because the three names are EDITABLE props of the element (`identityLabel` &
|
|
120
|
+
* co) and `cropLabeledScale` is not only what the renderer draws through —
|
|
121
|
+
* `element-view.ts` maps every click back through it too. Deriving the box from
|
|
122
|
+
* the current text would rescale and re-centre the whole Venn on each
|
|
123
|
+
* keystroke, and move the click mapping under the user's finger while they
|
|
124
|
+
* type; two boards of the same size would also draw their circles at two
|
|
125
|
+
* different sizes. The allowance is furniture: it is measured once, here.
|
|
126
|
+
*
|
|
127
|
+
* A name rewritten to something much longer than the vocabulary therefore
|
|
128
|
+
* reaches past the element's edge. It paints (the surface clips nothing); only
|
|
129
|
+
* the SVG export, which crops to the `xywh`, would cut it.
|
|
130
|
+
*/
|
|
131
|
+
const LABEL_ALLOWANCE = {
|
|
132
|
+
/** No word up there: the circle's own stroke, and room to see it. */
|
|
133
|
+
top: 5,
|
|
134
|
+
/** "Identity", right-aligned, reaching left of the circles. */
|
|
135
|
+
left: 68,
|
|
136
|
+
/** "Architecture", left-aligned, reaching right of them. */
|
|
137
|
+
right: 103,
|
|
138
|
+
/** "Experience", under the bottom circle, descender included. */
|
|
139
|
+
bottom: 32,
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Crop box KEEPING room for the three facet name labels (drawn outside the
|
|
143
|
+
* circles): the circles plus {@link LABEL_ALLOWANCE}, which is asymmetric
|
|
144
|
+
* because the words are. Used when a cropped diagram still shows its labels.
|
|
145
|
+
*/
|
|
146
|
+
export const CROP_LABELED = {
|
|
147
|
+
x: CIRCLES.minX - LABEL_ALLOWANCE.left,
|
|
148
|
+
y: CIRCLES.minY - LABEL_ALLOWANCE.top,
|
|
149
|
+
w: CIRCLES.maxX - CIRCLES.minX + LABEL_ALLOWANCE.left + LABEL_ALLOWANCE.right,
|
|
150
|
+
h: CIRCLES.maxY - CIRCLES.minY + LABEL_ALLOWANCE.top + LABEL_ALLOWANCE.bottom,
|
|
151
|
+
};
|
|
78
152
|
function fitScale(box) {
|
|
79
153
|
return (w, h) => {
|
|
80
154
|
const s = Math.min(w / box.w, h / box.h);
|
package/dist/element-view.d.ts
CHANGED
|
@@ -1,29 +1,27 @@
|
|
|
1
|
+
import type { EditableBackgroundLabel } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { FrameworkBackgroundView } from '@formicoidea/labre-core/blocks/surface';
|
|
1
3
|
import type { EdgyFacetsElementModel } from '@formicoidea/labre-core/model';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* The EDGY facets diagram's view: the shared background gesture, aimed by this
|
|
6
|
+
* framework's own label layout.
|
|
7
|
+
*
|
|
8
|
+
* The three facet names are NOT declared in a `FrameworkBackgroundDef` — the
|
|
9
|
+
* diagram is a Venn figure fitted into a reference space, and its labels hang
|
|
10
|
+
* off the circles rather than off a plot — so the hit test stays here, mapped
|
|
11
|
+
* with the SAME fit the renderer used. Everything above it (where a
|
|
12
|
+
* double-click is allowed to land, the in-place `<input>`, the commit that
|
|
13
|
+
* refuses to write an untouched value) comes from
|
|
14
|
+
* {@link FrameworkBackgroundView}, which is where this diagram's own copy of it
|
|
15
|
+
* went — see issue #355.
|
|
16
|
+
*/
|
|
17
|
+
export declare class EdgyView extends FrameworkBackgroundView<EdgyFacetsElementModel> {
|
|
6
18
|
static type: string;
|
|
7
|
-
/** The in-place `<input>` used to edit a label, or null when idle. */
|
|
8
|
-
private _labelEditor;
|
|
9
|
-
onCreated(): void;
|
|
10
|
-
onDestroyed(): void;
|
|
11
|
-
/** The editable facet label under a MODEL-space point, or null. */
|
|
12
|
-
private _labelAt;
|
|
13
19
|
/**
|
|
14
|
-
*
|
|
15
|
-
* three facet labels
|
|
16
|
-
*
|
|
17
|
-
* Same seam Wardley uses, for the same reason: the pointer router asks the
|
|
18
|
-
* VIEW, so a framework declares its own gesture zones next to the code that
|
|
19
|
-
* draws them, and the model layer keeps a hit test that only says what a
|
|
20
|
-
* click SELECTS.
|
|
20
|
+
* A diagram is SELECTED by its border (`EdgyFacetsElementModel`), and the
|
|
21
|
+
* three facet labels widen that for the pointer — the base class' doing; this
|
|
22
|
+
* method only says where they are.
|
|
21
23
|
*/
|
|
22
|
-
|
|
23
|
-
/** Double-click on a label → edit its text in place. */
|
|
24
|
-
private _onDblClick;
|
|
25
|
-
private _openLabelEditor;
|
|
26
|
-
private _closeLabelEditor;
|
|
24
|
+
protected labelAt(lx: number, ly: number, w: number, h: number): EditableBackgroundLabel | null;
|
|
27
25
|
}
|
|
28
26
|
/**
|
|
29
27
|
* Resize gating: the resize handles are hidden unless `model.resizeEnabled` is
|
package/dist/element-view.js
CHANGED
|
@@ -1,40 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { GfxElementModelView, GfxViewInteractionExtension, } from '@formicoidea/labre-core/std/gfx';
|
|
1
|
+
import { FrameworkBackgroundView } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { GfxViewInteractionExtension } from '@formicoidea/labre-core/std/gfx';
|
|
4
3
|
import { cropLabeledScale, refScale } from './consts.js';
|
|
5
|
-
import { getEdgyLabelHits, hitTestEdgyLabel
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
import { getEdgyLabelHits, hitTestEdgyLabel } from './label-layout.js';
|
|
5
|
+
/**
|
|
6
|
+
* The EDGY facets diagram's view: the shared background gesture, aimed by this
|
|
7
|
+
* framework's own label layout.
|
|
8
|
+
*
|
|
9
|
+
* The three facet names are NOT declared in a `FrameworkBackgroundDef` — the
|
|
10
|
+
* diagram is a Venn figure fitted into a reference space, and its labels hang
|
|
11
|
+
* off the circles rather than off a plot — so the hit test stays here, mapped
|
|
12
|
+
* with the SAME fit the renderer used. Everything above it (where a
|
|
13
|
+
* double-click is allowed to land, the in-place `<input>`, the commit that
|
|
14
|
+
* refuses to write an untouched value) comes from
|
|
15
|
+
* {@link FrameworkBackgroundView}, which is where this diagram's own copy of it
|
|
16
|
+
* went — see issue #355.
|
|
17
|
+
*/
|
|
18
|
+
export class EdgyView extends FrameworkBackgroundView {
|
|
12
19
|
static { this.type = 'edgy'; }
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
super.onDestroyed();
|
|
20
|
-
}
|
|
21
|
-
/** The editable facet label under a MODEL-space point, or null. */
|
|
22
|
-
_labelAt(mx, my) {
|
|
20
|
+
/**
|
|
21
|
+
* A diagram is SELECTED by its border (`EdgyFacetsElementModel`), and the
|
|
22
|
+
* three facet labels widen that for the pointer — the base class' doing; this
|
|
23
|
+
* method only says where they are.
|
|
24
|
+
*/
|
|
25
|
+
labelAt(lx, ly, w, h) {
|
|
23
26
|
// No labels shown → nothing to edit (and the cropped mapping differs).
|
|
24
27
|
if (!this.model.showLabels)
|
|
25
28
|
return null;
|
|
26
|
-
const [bx, by, w, h] = this.model.deserializedXYWH;
|
|
27
|
-
// Convert the model-space point into element-local coordinates, undoing the
|
|
28
|
-
// element rotation around its center.
|
|
29
|
-
let lx = mx - bx;
|
|
30
|
-
let ly = my - by;
|
|
31
|
-
const rot = this.model.rotate ?? 0;
|
|
32
|
-
if (rot) {
|
|
33
|
-
const center = [bx + w / 2, by + h / 2];
|
|
34
|
-
const [ux, uy] = rotatePoint([mx, my], center, -rot);
|
|
35
|
-
lx = ux - bx;
|
|
36
|
-
ly = uy - by;
|
|
37
|
-
}
|
|
38
29
|
// Map element-local coords into the fixed reference space the labels live
|
|
39
30
|
// in — with the SAME fit the renderer used (cropped or letterboxed).
|
|
40
31
|
const { s, ox, oy } = this.model.cropToCircles
|
|
@@ -42,91 +33,11 @@ export class EdgyView extends GfxElementModelView {
|
|
|
42
33
|
: refScale(w, h);
|
|
43
34
|
const rx = (lx - ox) / s;
|
|
44
35
|
const ry = (ly - oy) / s;
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* The diagram is SELECTED by its border (`EdgyFacetsElementModel`), but its
|
|
49
|
-
* three facet labels must still receive the double-click that renames them.
|
|
50
|
-
*
|
|
51
|
-
* Same seam Wardley uses, for the same reason: the pointer router asks the
|
|
52
|
-
* VIEW, so a framework declares its own gesture zones next to the code that
|
|
53
|
-
* draws them, and the model layer keeps a hit test that only says what a
|
|
54
|
-
* click SELECTS.
|
|
55
|
-
*/
|
|
56
|
-
includesPoint(x, y, options, host) {
|
|
57
|
-
if (super.includesPoint(x, y, options, host))
|
|
58
|
-
return true;
|
|
59
|
-
return this._labelAt(x, y) !== null;
|
|
60
|
-
}
|
|
61
|
-
/** Double-click on a label → edit its text in place. */
|
|
62
|
-
_onDblClick(e) {
|
|
63
|
-
if (this.model.isLocked())
|
|
64
|
-
return;
|
|
65
|
-
const [mx, my] = this.gfx.viewport.toModelCoord(e.x, e.y);
|
|
66
|
-
const hit = this._labelAt(mx, my);
|
|
36
|
+
const hit = hitTestEdgyLabel(getEdgyLabelHits(this.model), rx, ry);
|
|
67
37
|
if (!hit)
|
|
68
|
-
return;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
_openLabelEditor(field, e) {
|
|
72
|
-
this._closeLabelEditor();
|
|
73
|
-
const input = document.createElement('input');
|
|
74
|
-
input.value = String(this.model[field] ?? '');
|
|
75
|
-
Object.assign(input.style, {
|
|
76
|
-
position: 'fixed',
|
|
77
|
-
left: `${e.raw.clientX}px`,
|
|
78
|
-
top: `${e.raw.clientY}px`,
|
|
79
|
-
transform: 'translate(-50%, -50%)',
|
|
80
|
-
zIndex: '10000',
|
|
81
|
-
minWidth: '140px',
|
|
82
|
-
padding: '3px 8px',
|
|
83
|
-
font: '14px Inter, sans-serif',
|
|
84
|
-
color: 'var(--affine-text-primary-color, #1f2328)',
|
|
85
|
-
background: 'var(--affine-background-overlay-panel-color, #ffffff)',
|
|
86
|
-
border: '1px solid var(--affine-primary-color, #1e96eb)',
|
|
87
|
-
borderRadius: '6px',
|
|
88
|
-
boxShadow: 'var(--affine-shadow-2, 0 2px 8px rgba(0,0,0,0.18))',
|
|
89
|
-
outline: 'none',
|
|
90
|
-
});
|
|
91
|
-
document.body.append(input);
|
|
92
|
-
this._labelEditor = input;
|
|
93
|
-
// Mark the element as "editing" so the global edgeless key handlers
|
|
94
|
-
// (delete, escape, etc.) don't act on it while the user types.
|
|
95
|
-
this.gfx.selection.set({ elements: [this.model.id], editing: true });
|
|
96
|
-
input.focus();
|
|
97
|
-
input.select();
|
|
98
|
-
const commit = () => {
|
|
99
|
-
if (this._labelEditor !== input)
|
|
100
|
-
return;
|
|
101
|
-
const value = input.value;
|
|
102
|
-
this._closeLabelEditor();
|
|
103
|
-
this.gfx.std.store.captureSync();
|
|
104
|
-
this.gfx.std
|
|
105
|
-
.get(EdgelessCRUDIdentifier)
|
|
106
|
-
.updateElement(this.model.id, { [field]: value });
|
|
107
|
-
};
|
|
108
|
-
input.addEventListener('keydown', ev => {
|
|
109
|
-
ev.stopPropagation();
|
|
110
|
-
if (ev.key === 'Enter') {
|
|
111
|
-
ev.preventDefault();
|
|
112
|
-
commit();
|
|
113
|
-
}
|
|
114
|
-
else if (ev.key === 'Escape') {
|
|
115
|
-
ev.preventDefault();
|
|
116
|
-
this._closeLabelEditor();
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
input.addEventListener('blur', commit);
|
|
120
|
-
}
|
|
121
|
-
_closeLabelEditor() {
|
|
122
|
-
if (!this._labelEditor)
|
|
123
|
-
return;
|
|
124
|
-
const input = this._labelEditor;
|
|
125
|
-
this._labelEditor = null;
|
|
126
|
-
input.remove();
|
|
127
|
-
if (this.isConnected) {
|
|
128
|
-
this.gfx.selection.set({ elements: [this.model.id], editing: false });
|
|
129
|
-
}
|
|
38
|
+
return null;
|
|
39
|
+
// The facet names carry a hard default, so the drawn words ARE the prop.
|
|
40
|
+
return { prop: hit.field, text: String(this.model[hit.field] ?? '') };
|
|
130
41
|
}
|
|
131
42
|
}
|
|
132
43
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ElementRendererExtension, } from '@formicoidea/labre-core/blocks/surface';
|
|
1
|
+
import { ElementRendererExtension, taggedPath2D, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { shape as shapeRenderer } from '@formicoidea/labre-core/gfx/shape';
|
|
3
3
|
import { DefaultTheme } from '@formicoidea/labre-core/model';
|
|
4
4
|
import { PERSON_GLYPH_PATHS, PERSON_GLYPH_VIEWBOX } from './consts.js';
|
|
@@ -33,7 +33,7 @@ export const edgyNode = (model, ctx, matrix, renderer, rc, bound) => {
|
|
|
33
33
|
ctx.translate(-PERSON_GLYPH_VIEWBOX / 2, -PERSON_GLYPH_VIEWBOX / 2);
|
|
34
34
|
ctx.fillStyle = color;
|
|
35
35
|
for (const d of PERSON_GLYPH_PATHS) {
|
|
36
|
-
ctx.fill(
|
|
36
|
+
ctx.fill(taggedPath2D(d));
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
39
|
export const EdgyNodeRendererExtension = ElementRendererExtension('edgyNode', edgyNode);
|
|
@@ -111,7 +111,11 @@ const NAME = 'EDGY dynamic';
|
|
|
111
111
|
export const edgyDynamicTemplate = {
|
|
112
112
|
name: NAME,
|
|
113
113
|
type: 'template',
|
|
114
|
-
|
|
114
|
+
// The thumbnail follows the board: the circles are drawn where the cropped
|
|
115
|
+
// {@link CROP} box puts them once it is fitted into the 135 × 80 tile
|
|
116
|
+
// (height-bound, `s = 80 / CROP.h`), so the tile shows the same framing the
|
|
117
|
+
// template actually inserts rather than a Venn floating in a tile of its own.
|
|
118
|
+
preview: `<svg width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www.w3.org/2000/svg"><circle cx="53.6" cy="28" r="27.2" fill="#00ea4e" opacity="0.9"/><circle cx="81.4" cy="28" r="27.2" fill="#034cee" opacity="0.9"/><circle cx="67.5" cy="52" r="27.2" fill="#ff0056" opacity="0.9"/><path d="M53.6 28 H81.4 M55.1 30.6 L66 49.4 M79.9 30.6 L69 49.4" stroke="#fff" stroke-width="3"/><rect x="48.6" y="23" width="10" height="10" fill="#fff"/><circle cx="81.4" cy="28" r="5" fill="#fff"/><path d="M63 48 h7 l4 4 -4 4 h-7 z" fill="#fff"/></svg>`,
|
|
115
119
|
// Stamped by hand, and it is the one `commandId` in this pack that is a
|
|
116
120
|
// literal rather than a derivation: importing the registry here would close
|
|
117
121
|
// the very loop this module exists to avoid. The coverage test pairs the two
|
package/dist/templates/index.js
CHANGED
|
@@ -518,7 +518,7 @@ const ATTRS = 'width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www
|
|
|
518
518
|
* `build` takes the OPTIONAL inserting editor: called with none, at module
|
|
519
519
|
* load, for the English `content`; called again with the real `std` as
|
|
520
520
|
* {@link Template.localize}, so the two builds read the very same layout and
|
|
521
|
-
* differ only in the words a seed resolves to (`docs/adr/
|
|
521
|
+
* differ only in the words a seed resolves to (`docs/adr/0023`).
|
|
522
522
|
*/
|
|
523
523
|
function tpl(name, preview, build, nameKey) {
|
|
524
524
|
return {
|
package/dist/toolbar/config.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ChromeWording, type ToolbarContext } from '@formicoidea/labre-core/shared/services';
|
|
1
|
+
import { type ChromeWording, type ToolbarContext, type ToolbarModuleConfig } from '@formicoidea/labre-core/shared/services';
|
|
2
2
|
import { type TemplateResult } from 'lit';
|
|
3
3
|
/** Every wording above, for `translations.ts`'s manifest contribution. */
|
|
4
4
|
export declare const EDGY_TOOLBAR_WORDINGS: readonly ChromeWording[];
|
|
@@ -17,11 +17,6 @@ export declare const edgyToolbarConfig: {
|
|
|
17
17
|
icon: TemplateResult;
|
|
18
18
|
active(ctx: ToolbarContext): boolean;
|
|
19
19
|
run(ctx: ToolbarContext): void;
|
|
20
|
-
}, {
|
|
21
|
-
id: string;
|
|
22
|
-
tooltipWording: ChromeWording;
|
|
23
|
-
icon: TemplateResult<1>;
|
|
24
|
-
run(ctx: ToolbarContext): void;
|
|
25
20
|
}];
|
|
26
21
|
readonly when: (ctx: ToolbarContext) => boolean;
|
|
27
22
|
};
|
|
@@ -41,12 +36,11 @@ export declare const edgyBoardToolbarConfig: {
|
|
|
41
36
|
icon: TemplateResult;
|
|
42
37
|
active(ctx: ToolbarContext): boolean;
|
|
43
38
|
run(ctx: ToolbarContext): void;
|
|
44
|
-
}, {
|
|
45
|
-
id: string;
|
|
46
|
-
tooltipWording: ChromeWording;
|
|
47
|
-
icon: TemplateResult<1>;
|
|
48
|
-
run(ctx: ToolbarContext): void;
|
|
49
39
|
}];
|
|
50
40
|
readonly when: (ctx: ToolbarContext) => boolean;
|
|
51
41
|
};
|
|
52
42
|
export declare const edgyBoardToolbarExtension: import("@formicoidea/labre-core/store").ExtensionType;
|
|
43
|
+
export declare const edgyToolingToolbarConfig: ToolbarModuleConfig;
|
|
44
|
+
export declare const edgyBoardToolingToolbarConfig: ToolbarModuleConfig;
|
|
45
|
+
export declare const edgyToolingToolbarExtension: import("@formicoidea/labre-core/store").ExtensionType;
|
|
46
|
+
export declare const edgyBoardToolingToolbarExtension: import("@formicoidea/labre-core/store").ExtensionType;
|
package/dist/toolbar/config.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
-
import { createAutoLegend, dddLegendIcon } from '@formicoidea/labre-ddd-shared';
|
|
1
|
+
import { EdgelessCRUDIdentifier, legendToolbarAction, validationToolbarConfig, } from '@formicoidea/labre-core/blocks/surface';
|
|
3
2
|
import { EdgyBoardElementModel, EdgyFacetsElementModel, } from '@formicoidea/labre-core/model';
|
|
4
|
-
import {
|
|
3
|
+
import { BOARD_RESIZE_TOGGLE, ToolbarModuleExtension, } from '@formicoidea/labre-core/shared/services';
|
|
5
4
|
import { BlockFlavourIdentifier } from '@formicoidea/labre-core/std';
|
|
6
5
|
import { html } from 'lit';
|
|
7
|
-
import { EDGY_AUTO_LEGEND } from '../legend.js';
|
|
8
6
|
/** The two toolbar tooltips this file used to hard-code as English literals. */
|
|
9
7
|
const FACET_LABELS_TOGGLE = [
|
|
10
8
|
'com.labre.edgy.toolbar.facet-labels',
|
|
@@ -96,46 +94,12 @@ function booleanToggle(Model, id, tooltip, icon, prop) {
|
|
|
96
94
|
},
|
|
97
95
|
};
|
|
98
96
|
}
|
|
99
|
-
/**
|
|
100
|
-
* The legend action, shared by the two EDGY backgrounds: both frame the same
|
|
101
|
-
* notation, so both answer the same question — "what is actually drawn here" —
|
|
102
|
-
* with the same table ({@link EDGY_AUTO_LEGEND}) and the same box.
|
|
103
|
-
*
|
|
104
|
-
* Registered ALWAYS-ON (`EdgyRenderViewExtension`) with the toggles it sits
|
|
105
|
-
* next to: a legend is real, editable elements written into the document, so
|
|
106
|
-
* generating one is authoring, not tooling a flag may take away
|
|
107
|
-
* (`docs/adr/0009`).
|
|
108
|
-
*/
|
|
109
|
-
function legendAction(Model, id) {
|
|
110
|
-
return {
|
|
111
|
-
id,
|
|
112
|
-
tooltipWording: BOARD_LEGEND_NOTATION,
|
|
113
|
-
icon: dddLegendIcon,
|
|
114
|
-
run(ctx) {
|
|
115
|
-
const background = ctx.getSurfaceModelsByType(Model)[0];
|
|
116
|
-
if (!background)
|
|
117
|
-
return;
|
|
118
|
-
createAutoLegend(ctx.std, background, EDGY_AUTO_LEGEND);
|
|
119
|
-
ctx.std.getOptional(TelemetryProvider)?.track('FrameworkLegendCreated', {
|
|
120
|
-
// The WIRE value from `frameworks.ts` (`telemetryKey`), which for EDGY
|
|
121
|
-
// happens to be the module id itself. Same field set as Wardley's and
|
|
122
|
-
// the three DDD legend buttons, so the four are comparable.
|
|
123
|
-
framework: 'edgy',
|
|
124
|
-
element: 'legend',
|
|
125
|
-
page: 'whiteboard editor',
|
|
126
|
-
segment: 'element toolbar',
|
|
127
|
-
module: 'edgy toolbar',
|
|
128
|
-
});
|
|
129
|
-
},
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
97
|
export const edgyToolbarConfig = {
|
|
133
98
|
actions: [
|
|
134
99
|
booleanToggle(EdgyFacetsElementModel, 'a.toggle-resize', BOARD_RESIZE_TOGGLE, ResizeIcon, 'resizeEnabled'),
|
|
135
100
|
booleanToggle(EdgyFacetsElementModel, 'b.toggle-labels', FACET_LABELS_TOGGLE, LabelsIcon, 'showLabels'),
|
|
136
101
|
// No spotlight toggle here: the hover spotlight is BOARD logic and lives on
|
|
137
102
|
// the EDGY board toolbar below. The Venn only carries APPEARANCE (#195).
|
|
138
|
-
legendAction(EdgyFacetsElementModel, 'c.legend'),
|
|
139
103
|
],
|
|
140
104
|
when: ctx => ctx.getSurfaceModelsByType(EdgyFacetsElementModel).length > 0,
|
|
141
105
|
};
|
|
@@ -147,7 +111,6 @@ export const edgyBoardToolbarConfig = {
|
|
|
147
111
|
actions: [
|
|
148
112
|
booleanToggle(EdgyBoardElementModel, 'a.toggle-resize', BOARD_RESIZE_TOGGLE, ResizeIcon, 'resizeEnabled'),
|
|
149
113
|
booleanToggle(EdgyBoardElementModel, 'b.toggle-spotlight', HOVER_SPOTLIGHT_TOGGLE, SpotlightIcon, 'spotlightEnabled'),
|
|
150
|
-
legendAction(EdgyBoardElementModel, 'c.legend'),
|
|
151
114
|
],
|
|
152
115
|
when: ctx => ctx.getSurfaceModelsByType(EdgyBoardElementModel).length > 0,
|
|
153
116
|
};
|
|
@@ -155,3 +118,55 @@ export const edgyBoardToolbarExtension = ToolbarModuleExtension({
|
|
|
155
118
|
id: BlockFlavourIdentifier('affine:surface:edgyBoard'),
|
|
156
119
|
config: edgyBoardToolbarConfig,
|
|
157
120
|
});
|
|
121
|
+
/**
|
|
122
|
+
* A background's flag-gated row, WHOLE: the legend button and the Validation
|
|
123
|
+
* dropdown, in ONE module — registered by `EdgyViewExtension` on the `custom:`
|
|
124
|
+
* flavour slot of each of the two frames.
|
|
125
|
+
*
|
|
126
|
+
* ## Why one module and not two
|
|
127
|
+
*
|
|
128
|
+
* `renderToolbar` merges exactly four slots per element and
|
|
129
|
+
* `ToolbarModuleExtension` binds by DI variant, so a second module claiming
|
|
130
|
+
* `custom:affine:surface:edgy` throws `DuplicateServiceDefinitionError` before
|
|
131
|
+
* the editor finishes setting up. The always-on module above holds the
|
|
132
|
+
* APPEARANCE toggles a stored board must keep; these two entries are gated by
|
|
133
|
+
* the same flag and appear together or not at all.
|
|
134
|
+
*
|
|
135
|
+
* ## Why the legend moved out of the always-on half
|
|
136
|
+
*
|
|
137
|
+
* It used to sit with the toggles, argued as authoring. `docs/adr/0026` (which
|
|
138
|
+
* amends `docs/adr/0009`) reverses that reading: GENERATING a legend is
|
|
139
|
+
* tooling — it is the framework telling you what its notation means — while the
|
|
140
|
+
* legend it wrote is content, made of plain shapes, text and this pack's own
|
|
141
|
+
* always-on renderers, and keeps being painted with the flag off.
|
|
142
|
+
*
|
|
143
|
+
* `c.legend` and not the factory's default `b.`: the sort is lexicographic and
|
|
144
|
+
* both frames already spend `a.` and `b.` on their toggles.
|
|
145
|
+
*/
|
|
146
|
+
function toolingConfig(Model) {
|
|
147
|
+
return {
|
|
148
|
+
actions: [
|
|
149
|
+
legendToolbarAction({
|
|
150
|
+
id: 'c.legend',
|
|
151
|
+
Model,
|
|
152
|
+
owner: 'edgy',
|
|
153
|
+
framework: 'edgy',
|
|
154
|
+
}),
|
|
155
|
+
// The generic dropdown, not an EDGY variant of it: the config names no
|
|
156
|
+
// framework — it reads the registered rules and profiles — so this is the
|
|
157
|
+
// very same object wardley, bpmn, the context map and C4 register.
|
|
158
|
+
...validationToolbarConfig.actions,
|
|
159
|
+
],
|
|
160
|
+
when: (ctx) => ctx.getSurfaceModelsByType(Model).length > 0,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
export const edgyToolingToolbarConfig = toolingConfig(EdgyFacetsElementModel);
|
|
164
|
+
export const edgyBoardToolingToolbarConfig = toolingConfig(EdgyBoardElementModel);
|
|
165
|
+
export const edgyToolingToolbarExtension = ToolbarModuleExtension({
|
|
166
|
+
id: BlockFlavourIdentifier('custom:affine:surface:edgy'),
|
|
167
|
+
config: edgyToolingToolbarConfig,
|
|
168
|
+
});
|
|
169
|
+
export const edgyBoardToolingToolbarExtension = ToolbarModuleExtension({
|
|
170
|
+
id: BlockFlavourIdentifier('custom:affine:surface:edgyBoard'),
|
|
171
|
+
config: edgyBoardToolingToolbarConfig,
|
|
172
|
+
});
|
|
@@ -10,6 +10,8 @@ export declare class EdgelessEdgySeniorButton extends EdgelessEdgySeniorButton_b
|
|
|
10
10
|
enableActiveBackground: boolean;
|
|
11
11
|
type: typeof EmptyTool;
|
|
12
12
|
private _toggleMenu;
|
|
13
|
+
/** Lit while one of THIS framework's artefacts is armed. See the Wardley one. */
|
|
14
|
+
private get _armed();
|
|
13
15
|
render(): import("lit-html").TemplateResult<1>;
|
|
14
16
|
}
|
|
15
17
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DefaultTool } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
|
|
3
3
|
import { translateKey } from '@formicoidea/labre-core/shared/services';
|
|
4
|
-
import { EdgelessToolbarToolMixin } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
|
|
4
|
+
import { armedArtefact, EdgelessToolbarToolMixin, } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
|
|
5
5
|
import { SignalWatcher } from '@formicoidea/labre-core/global/lit';
|
|
6
6
|
import { css, html, LitElement } from 'lit';
|
|
7
7
|
import { edgyToolbarIcon } from './icons.js';
|
|
@@ -72,6 +72,10 @@ export class EdgelessEdgySeniorButton extends EdgelessToolbarToolMixin(SignalWat
|
|
|
72
72
|
const menu = this.createPopper('edgeless-edgy-menu', this);
|
|
73
73
|
menu.element.edgeless = this.edgeless;
|
|
74
74
|
}
|
|
75
|
+
/** Lit while one of THIS framework's artefacts is armed. See the Wardley one. */
|
|
76
|
+
get _armed() {
|
|
77
|
+
return armedArtefact(this.gfx)?.owner === 'edgy';
|
|
78
|
+
}
|
|
75
79
|
render() {
|
|
76
80
|
return html `<edgeless-toolbar-button
|
|
77
81
|
class="edgy-button"
|
|
@@ -79,7 +83,7 @@ export class EdgelessEdgySeniorButton extends EdgelessToolbarToolMixin(SignalWat
|
|
|
79
83
|
? ''
|
|
80
84
|
: translateKey(this.edgeless.std, 'com.labre.framework.edgy', 'EDGY')}
|
|
81
85
|
.tooltipOffset=${4}
|
|
82
|
-
.active=${!!this.popper}
|
|
86
|
+
.active=${!!this.popper || this._armed}
|
|
83
87
|
@click=${this._toggleMenu}
|
|
84
88
|
>
|
|
85
89
|
<div class="edgy-root">
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { FrameworkPalette, NamedPalette } from '@formicoidea/labre-core/components/color-picker';
|
|
1
2
|
import { type ChromeWording } from '@formicoidea/labre-core/shared/services';
|
|
2
3
|
/**
|
|
3
4
|
* The twelve EDGY swatches' own names, resolved by `resolvePaletteLabel`
|
|
@@ -23,4 +24,17 @@ export declare const EDGY_PALETTE_WORDING_ORGANISATION_LIGHT: ChromeWording;
|
|
|
23
24
|
export declare const EDGY_PALETTE_WORDING_BRAND_LIGHT: ChromeWording;
|
|
24
25
|
export declare const EDGY_PALETTE_WORDING_PRODUCT_LIGHT: ChromeWording;
|
|
25
26
|
export declare const EDGY_PALETTE_WORDINGS: readonly ChromeWording[];
|
|
27
|
+
/**
|
|
28
|
+
* From the default editor palette we keep ONLY the neutrals (greys, white,
|
|
29
|
+
* black, transparent) — the historical colours are dropped in favour of the
|
|
30
|
+
* EDGY swatches above.
|
|
31
|
+
*/
|
|
32
|
+
export declare const EDGY_PALETTE_LIST: NamedPalette[];
|
|
33
|
+
/**
|
|
34
|
+
* EDGY's page of the colour pickers' carousel (`docs/adr/0027`) — the same
|
|
35
|
+
* list its node picker is seeded with, offered on every other picker too.
|
|
36
|
+
* Registered from the FLAG-GATED view extension: offering hues is tooling
|
|
37
|
+
* (`docs/adr/0009`).
|
|
38
|
+
*/
|
|
39
|
+
export declare const EDGY_FRAMEWORK_PALETTE: FrameworkPalette;
|
|
26
40
|
export declare const edgyNodeToolbarExtension: import("@formicoidea/labre-core/store").ExtensionType;
|
|
@@ -142,10 +142,21 @@ const EDGY_PALETTES = [
|
|
|
142
142
|
* black, transparent) — the historical colours are dropped in favour of the
|
|
143
143
|
* EDGY swatches above.
|
|
144
144
|
*/
|
|
145
|
-
const EDGY_PALETTE_LIST = [
|
|
145
|
+
export const EDGY_PALETTE_LIST = [
|
|
146
146
|
...EDGY_PALETTES,
|
|
147
147
|
...neutralPalettes(),
|
|
148
148
|
];
|
|
149
|
+
/**
|
|
150
|
+
* EDGY's page of the colour pickers' carousel (`docs/adr/0027`) — the same
|
|
151
|
+
* list its node picker is seeded with, offered on every other picker too.
|
|
152
|
+
* Registered from the FLAG-GATED view extension: offering hues is tooling
|
|
153
|
+
* (`docs/adr/0009`).
|
|
154
|
+
*/
|
|
155
|
+
export const EDGY_FRAMEWORK_PALETTE = {
|
|
156
|
+
framework: 'edgy',
|
|
157
|
+
labelWording: ['com.labre.framework.edgy', 'EDGY'],
|
|
158
|
+
palettes: EDGY_PALETTE_LIST,
|
|
159
|
+
};
|
|
149
160
|
/**
|
|
150
161
|
* EDGY fill / stroke colour picker — identical to the shape one but seeded with
|
|
151
162
|
* the EDGY palette swatches (`.palettes`).
|