@formicoidea/labre-framework-edgy 0.23.0 → 0.23.2
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 +2 -2
- package/src/element-renderer.ts +208 -208
- package/src/element-view.ts +145 -145
- package/src/label-layout.ts +105 -105
- package/src/node/consts.ts +56 -56
- package/src/node/node-renderer.ts +64 -64
- package/src/node/node-view.ts +33 -33
- package/src/toolbar/config.ts +96 -96
- package/src/toolbar/edgy-menu.ts +242 -242
- package/src/toolbar/edgy-senior-button.ts +102 -102
- package/src/toolbar/node-config.ts +202 -202
- package/src/toolbar/senior-tool.ts +11 -11
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type ElementRenderer,
|
|
3
|
-
ElementRendererExtension,
|
|
4
|
-
} from '@formicoidea/labre-core/blocks/surface';
|
|
5
|
-
import { shape as shapeRenderer } from '@formicoidea/labre-core/gfx/shape';
|
|
6
|
-
import { DefaultTheme, type EdgyNodeElementModel } from '@formicoidea/labre-core/model';
|
|
7
|
-
|
|
8
|
-
import { PERSON_GLYPH_PATHS, PERSON_GLYPH_VIEWBOX } from './consts';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Renderer for an EDGY base-element node. The shape body (rounded rect / rect /
|
|
12
|
-
* chevron polygon / ellipse) is drawn by REUSING the native shape renderer — so
|
|
13
|
-
* stroke width, colors, inner text and theme behave exactly like a native shape.
|
|
14
|
-
* Only `people` is decorated: an inscribed person glyph (the official
|
|
15
|
-
* `Icon-People` paths) drawn on top in the node's (editable) stroke color.
|
|
16
|
-
*/
|
|
17
|
-
export const edgyNode: ElementRenderer<EdgyNodeElementModel> = (
|
|
18
|
-
model,
|
|
19
|
-
ctx,
|
|
20
|
-
matrix,
|
|
21
|
-
renderer,
|
|
22
|
-
rc,
|
|
23
|
-
bound
|
|
24
|
-
) => {
|
|
25
|
-
const [, , w, h] = model.deserializedXYWH;
|
|
26
|
-
const cx = w / 2;
|
|
27
|
-
const cy = h / 2;
|
|
28
|
-
|
|
29
|
-
// Capture the element-local transform BEFORE the shape renderer mutates the
|
|
30
|
-
// matrix, so the glyph can be drawn in the same space afterwards.
|
|
31
|
-
const glyphMatrix = DOMMatrix.fromMatrix(matrix)
|
|
32
|
-
.translateSelf(cx, cy)
|
|
33
|
-
.rotateSelf(model.rotate)
|
|
34
|
-
.translateSelf(-cx, -cy);
|
|
35
|
-
|
|
36
|
-
// Native shape (fill / stroke / inner text / theme handled natively).
|
|
37
|
-
shapeRenderer(model, ctx, matrix, renderer, rc, bound);
|
|
38
|
-
|
|
39
|
-
if (model.kind !== 'people') return;
|
|
40
|
-
|
|
41
|
-
const color = renderer.getColorValue(
|
|
42
|
-
model.strokeColor,
|
|
43
|
-
DefaultTheme.shapeStrokeColor,
|
|
44
|
-
true
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
// ── Person glyph (Icon-People), scaled to the node and centred ──────
|
|
48
|
-
const target = Math.min(w, h) * 0.8;
|
|
49
|
-
const s = target / PERSON_GLYPH_VIEWBOX;
|
|
50
|
-
|
|
51
|
-
ctx.setTransform(glyphMatrix);
|
|
52
|
-
ctx.translate(cx, cy);
|
|
53
|
-
ctx.scale(s, s);
|
|
54
|
-
ctx.translate(-PERSON_GLYPH_VIEWBOX / 2, -PERSON_GLYPH_VIEWBOX / 2);
|
|
55
|
-
ctx.fillStyle = color;
|
|
56
|
-
for (const d of PERSON_GLYPH_PATHS) {
|
|
57
|
-
ctx.fill(new Path2D(d));
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export const EdgyNodeRendererExtension = ElementRendererExtension(
|
|
62
|
-
'edgyNode',
|
|
63
|
-
edgyNode
|
|
64
|
-
);
|
|
1
|
+
import {
|
|
2
|
+
type ElementRenderer,
|
|
3
|
+
ElementRendererExtension,
|
|
4
|
+
} from '@formicoidea/labre-core/blocks/surface';
|
|
5
|
+
import { shape as shapeRenderer } from '@formicoidea/labre-core/gfx/shape';
|
|
6
|
+
import { DefaultTheme, type EdgyNodeElementModel } from '@formicoidea/labre-core/model';
|
|
7
|
+
|
|
8
|
+
import { PERSON_GLYPH_PATHS, PERSON_GLYPH_VIEWBOX } from './consts';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Renderer for an EDGY base-element node. The shape body (rounded rect / rect /
|
|
12
|
+
* chevron polygon / ellipse) is drawn by REUSING the native shape renderer — so
|
|
13
|
+
* stroke width, colors, inner text and theme behave exactly like a native shape.
|
|
14
|
+
* Only `people` is decorated: an inscribed person glyph (the official
|
|
15
|
+
* `Icon-People` paths) drawn on top in the node's (editable) stroke color.
|
|
16
|
+
*/
|
|
17
|
+
export const edgyNode: ElementRenderer<EdgyNodeElementModel> = (
|
|
18
|
+
model,
|
|
19
|
+
ctx,
|
|
20
|
+
matrix,
|
|
21
|
+
renderer,
|
|
22
|
+
rc,
|
|
23
|
+
bound
|
|
24
|
+
) => {
|
|
25
|
+
const [, , w, h] = model.deserializedXYWH;
|
|
26
|
+
const cx = w / 2;
|
|
27
|
+
const cy = h / 2;
|
|
28
|
+
|
|
29
|
+
// Capture the element-local transform BEFORE the shape renderer mutates the
|
|
30
|
+
// matrix, so the glyph can be drawn in the same space afterwards.
|
|
31
|
+
const glyphMatrix = DOMMatrix.fromMatrix(matrix)
|
|
32
|
+
.translateSelf(cx, cy)
|
|
33
|
+
.rotateSelf(model.rotate)
|
|
34
|
+
.translateSelf(-cx, -cy);
|
|
35
|
+
|
|
36
|
+
// Native shape (fill / stroke / inner text / theme handled natively).
|
|
37
|
+
shapeRenderer(model, ctx, matrix, renderer, rc, bound);
|
|
38
|
+
|
|
39
|
+
if (model.kind !== 'people') return;
|
|
40
|
+
|
|
41
|
+
const color = renderer.getColorValue(
|
|
42
|
+
model.strokeColor,
|
|
43
|
+
DefaultTheme.shapeStrokeColor,
|
|
44
|
+
true
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// ── Person glyph (Icon-People), scaled to the node and centred ──────
|
|
48
|
+
const target = Math.min(w, h) * 0.8;
|
|
49
|
+
const s = target / PERSON_GLYPH_VIEWBOX;
|
|
50
|
+
|
|
51
|
+
ctx.setTransform(glyphMatrix);
|
|
52
|
+
ctx.translate(cx, cy);
|
|
53
|
+
ctx.scale(s, s);
|
|
54
|
+
ctx.translate(-PERSON_GLYPH_VIEWBOX / 2, -PERSON_GLYPH_VIEWBOX / 2);
|
|
55
|
+
ctx.fillStyle = color;
|
|
56
|
+
for (const d of PERSON_GLYPH_PATHS) {
|
|
57
|
+
ctx.fill(new Path2D(d));
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const EdgyNodeRendererExtension = ElementRendererExtension(
|
|
62
|
+
'edgyNode',
|
|
63
|
+
edgyNode
|
|
64
|
+
);
|
package/src/node/node-view.ts
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { mountShapeTextEditor } from '@formicoidea/labre-core/gfx/shape';
|
|
2
|
-
import {
|
|
3
|
-
type EdgyNodeElementModel,
|
|
4
|
-
ShapeElementModel,
|
|
5
|
-
} from '@formicoidea/labre-core/model';
|
|
6
|
-
import { GfxElementModelView } from '@formicoidea/labre-core/std/gfx';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* View for an EDGY base-element node. Registering it ensures `gfx.view.get(model)`
|
|
10
|
-
* returns a view (required so move / select / connector interactions work).
|
|
11
|
-
*
|
|
12
|
-
* EDGY nodes are native shapes, so we reuse the shape inner-text editor: a
|
|
13
|
-
* double-click mounts the editable text overlay (`mountShapeTextEditor`), exactly
|
|
14
|
-
* like a native shape. We deliberately do NOT inherit the polygon vertex-editing
|
|
15
|
-
* overlay (the Activity chevron should keep its shape).
|
|
16
|
-
*/
|
|
17
|
-
export class EdgyNodeView extends GfxElementModelView<EdgyNodeElementModel> {
|
|
18
|
-
static override type: string = 'edgyNode';
|
|
19
|
-
|
|
20
|
-
override onCreated(): void {
|
|
21
|
-
super.onCreated();
|
|
22
|
-
this.on('dblclick', () => {
|
|
23
|
-
const edgeless = this.std.view.getBlock(this.std.store.root!.id);
|
|
24
|
-
if (
|
|
25
|
-
edgeless &&
|
|
26
|
-
!this.model.isLocked() &&
|
|
27
|
-
this.model instanceof ShapeElementModel
|
|
28
|
-
) {
|
|
29
|
-
mountShapeTextEditor(this.model, edgeless);
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
}
|
|
1
|
+
import { mountShapeTextEditor } from '@formicoidea/labre-core/gfx/shape';
|
|
2
|
+
import {
|
|
3
|
+
type EdgyNodeElementModel,
|
|
4
|
+
ShapeElementModel,
|
|
5
|
+
} from '@formicoidea/labre-core/model';
|
|
6
|
+
import { GfxElementModelView } from '@formicoidea/labre-core/std/gfx';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* View for an EDGY base-element node. Registering it ensures `gfx.view.get(model)`
|
|
10
|
+
* returns a view (required so move / select / connector interactions work).
|
|
11
|
+
*
|
|
12
|
+
* EDGY nodes are native shapes, so we reuse the shape inner-text editor: a
|
|
13
|
+
* double-click mounts the editable text overlay (`mountShapeTextEditor`), exactly
|
|
14
|
+
* like a native shape. We deliberately do NOT inherit the polygon vertex-editing
|
|
15
|
+
* overlay (the Activity chevron should keep its shape).
|
|
16
|
+
*/
|
|
17
|
+
export class EdgyNodeView extends GfxElementModelView<EdgyNodeElementModel> {
|
|
18
|
+
static override type: string = 'edgyNode';
|
|
19
|
+
|
|
20
|
+
override onCreated(): void {
|
|
21
|
+
super.onCreated();
|
|
22
|
+
this.on('dblclick', () => {
|
|
23
|
+
const edgeless = this.std.view.getBlock(this.std.store.root!.id);
|
|
24
|
+
if (
|
|
25
|
+
edgeless &&
|
|
26
|
+
!this.model.isLocked() &&
|
|
27
|
+
this.model instanceof ShapeElementModel
|
|
28
|
+
) {
|
|
29
|
+
mountShapeTextEditor(this.model, edgeless);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/toolbar/config.ts
CHANGED
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
-
import { EdgyFacetsElementModel } from '@formicoidea/labre-core/model';
|
|
3
|
-
import {
|
|
4
|
-
type ToolbarContext,
|
|
5
|
-
type ToolbarModuleConfig,
|
|
6
|
-
ToolbarModuleExtension,
|
|
7
|
-
} from '@formicoidea/labre-core/shared/services';
|
|
8
|
-
import { BlockFlavourIdentifier } from '@formicoidea/labre-core/std';
|
|
9
|
-
import { html, type TemplateResult } from 'lit';
|
|
10
|
-
|
|
11
|
-
const ResizeIcon = html`<svg
|
|
12
|
-
width="24"
|
|
13
|
-
height="24"
|
|
14
|
-
viewBox="0 0 24 24"
|
|
15
|
-
fill="none"
|
|
16
|
-
stroke="currentColor"
|
|
17
|
-
stroke-width="1.6"
|
|
18
|
-
stroke-linecap="round"
|
|
19
|
-
stroke-linejoin="round"
|
|
20
|
-
>
|
|
21
|
-
<path d="M9 5H5v4M15 19h4v-4" />
|
|
22
|
-
<path d="M5 5l6 6M19 19l-6-6" />
|
|
23
|
-
</svg>`;
|
|
24
|
-
|
|
25
|
-
/** Tag — show / hide the facet name labels. */
|
|
26
|
-
const LabelsIcon = html`<svg
|
|
27
|
-
width="24"
|
|
28
|
-
height="24"
|
|
29
|
-
viewBox="0 0 24 24"
|
|
30
|
-
fill="none"
|
|
31
|
-
stroke="currentColor"
|
|
32
|
-
stroke-width="1.6"
|
|
33
|
-
stroke-linecap="round"
|
|
34
|
-
stroke-linejoin="round"
|
|
35
|
-
>
|
|
36
|
-
<path d="M3 8.5l6-4.5 12 0 0 16-12 0-6-4.5z" />
|
|
37
|
-
<circle cx="8" cy="12" r="1.4" />
|
|
38
|
-
</svg>`;
|
|
39
|
-
|
|
40
|
-
type EdgyToggleProp = 'resizeEnabled' | 'showLabels';
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Build a toolbar toggle that flips a boolean flag on every selected facets
|
|
44
|
-
* diagram: `active` reflects the current state, `run` flips it (with an undo
|
|
45
|
-
* checkpoint).
|
|
46
|
-
*/
|
|
47
|
-
function booleanToggle(
|
|
48
|
-
id: string,
|
|
49
|
-
tooltip: string,
|
|
50
|
-
icon: TemplateResult,
|
|
51
|
-
prop: EdgyToggleProp
|
|
52
|
-
) {
|
|
53
|
-
return {
|
|
54
|
-
id,
|
|
55
|
-
tooltip,
|
|
56
|
-
icon,
|
|
57
|
-
active(ctx: ToolbarContext) {
|
|
58
|
-
const models = ctx.getSurfaceModelsByType(EdgyFacetsElementModel);
|
|
59
|
-
return models.length > 0 && models.every(model => model[prop]);
|
|
60
|
-
},
|
|
61
|
-
run(ctx: ToolbarContext) {
|
|
62
|
-
const models = ctx.getSurfaceModelsByType(EdgyFacetsElementModel);
|
|
63
|
-
if (!models.length) return;
|
|
64
|
-
|
|
65
|
-
const enable = !models.every(model => model[prop]);
|
|
66
|
-
ctx.std.store.captureSync();
|
|
67
|
-
const crud = ctx.std.get(EdgelessCRUDIdentifier);
|
|
68
|
-
for (const model of models) {
|
|
69
|
-
crud.updateElement(model.id, { [prop]: enable });
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export const edgyToolbarConfig = {
|
|
76
|
-
actions: [
|
|
77
|
-
booleanToggle(
|
|
78
|
-
'a.toggle-resize',
|
|
79
|
-
'Enable / lock resizing',
|
|
80
|
-
ResizeIcon,
|
|
81
|
-
'resizeEnabled'
|
|
82
|
-
),
|
|
83
|
-
booleanToggle(
|
|
84
|
-
'b.toggle-labels',
|
|
85
|
-
'Show / hide facet labels',
|
|
86
|
-
LabelsIcon,
|
|
87
|
-
'showLabels'
|
|
88
|
-
),
|
|
89
|
-
],
|
|
90
|
-
when: ctx => ctx.getSurfaceModelsByType(EdgyFacetsElementModel).length > 0,
|
|
91
|
-
} as const satisfies ToolbarModuleConfig;
|
|
92
|
-
|
|
93
|
-
export const edgyToolbarExtension = ToolbarModuleExtension({
|
|
94
|
-
id: BlockFlavourIdentifier('affine:surface:edgy'),
|
|
95
|
-
config: edgyToolbarConfig,
|
|
96
|
-
});
|
|
1
|
+
import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { EdgyFacetsElementModel } from '@formicoidea/labre-core/model';
|
|
3
|
+
import {
|
|
4
|
+
type ToolbarContext,
|
|
5
|
+
type ToolbarModuleConfig,
|
|
6
|
+
ToolbarModuleExtension,
|
|
7
|
+
} from '@formicoidea/labre-core/shared/services';
|
|
8
|
+
import { BlockFlavourIdentifier } from '@formicoidea/labre-core/std';
|
|
9
|
+
import { html, type TemplateResult } from 'lit';
|
|
10
|
+
|
|
11
|
+
const ResizeIcon = html`<svg
|
|
12
|
+
width="24"
|
|
13
|
+
height="24"
|
|
14
|
+
viewBox="0 0 24 24"
|
|
15
|
+
fill="none"
|
|
16
|
+
stroke="currentColor"
|
|
17
|
+
stroke-width="1.6"
|
|
18
|
+
stroke-linecap="round"
|
|
19
|
+
stroke-linejoin="round"
|
|
20
|
+
>
|
|
21
|
+
<path d="M9 5H5v4M15 19h4v-4" />
|
|
22
|
+
<path d="M5 5l6 6M19 19l-6-6" />
|
|
23
|
+
</svg>`;
|
|
24
|
+
|
|
25
|
+
/** Tag — show / hide the facet name labels. */
|
|
26
|
+
const LabelsIcon = html`<svg
|
|
27
|
+
width="24"
|
|
28
|
+
height="24"
|
|
29
|
+
viewBox="0 0 24 24"
|
|
30
|
+
fill="none"
|
|
31
|
+
stroke="currentColor"
|
|
32
|
+
stroke-width="1.6"
|
|
33
|
+
stroke-linecap="round"
|
|
34
|
+
stroke-linejoin="round"
|
|
35
|
+
>
|
|
36
|
+
<path d="M3 8.5l6-4.5 12 0 0 16-12 0-6-4.5z" />
|
|
37
|
+
<circle cx="8" cy="12" r="1.4" />
|
|
38
|
+
</svg>`;
|
|
39
|
+
|
|
40
|
+
type EdgyToggleProp = 'resizeEnabled' | 'showLabels';
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Build a toolbar toggle that flips a boolean flag on every selected facets
|
|
44
|
+
* diagram: `active` reflects the current state, `run` flips it (with an undo
|
|
45
|
+
* checkpoint).
|
|
46
|
+
*/
|
|
47
|
+
function booleanToggle(
|
|
48
|
+
id: string,
|
|
49
|
+
tooltip: string,
|
|
50
|
+
icon: TemplateResult,
|
|
51
|
+
prop: EdgyToggleProp
|
|
52
|
+
) {
|
|
53
|
+
return {
|
|
54
|
+
id,
|
|
55
|
+
tooltip,
|
|
56
|
+
icon,
|
|
57
|
+
active(ctx: ToolbarContext) {
|
|
58
|
+
const models = ctx.getSurfaceModelsByType(EdgyFacetsElementModel);
|
|
59
|
+
return models.length > 0 && models.every(model => model[prop]);
|
|
60
|
+
},
|
|
61
|
+
run(ctx: ToolbarContext) {
|
|
62
|
+
const models = ctx.getSurfaceModelsByType(EdgyFacetsElementModel);
|
|
63
|
+
if (!models.length) return;
|
|
64
|
+
|
|
65
|
+
const enable = !models.every(model => model[prop]);
|
|
66
|
+
ctx.std.store.captureSync();
|
|
67
|
+
const crud = ctx.std.get(EdgelessCRUDIdentifier);
|
|
68
|
+
for (const model of models) {
|
|
69
|
+
crud.updateElement(model.id, { [prop]: enable });
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const edgyToolbarConfig = {
|
|
76
|
+
actions: [
|
|
77
|
+
booleanToggle(
|
|
78
|
+
'a.toggle-resize',
|
|
79
|
+
'Enable / lock resizing',
|
|
80
|
+
ResizeIcon,
|
|
81
|
+
'resizeEnabled'
|
|
82
|
+
),
|
|
83
|
+
booleanToggle(
|
|
84
|
+
'b.toggle-labels',
|
|
85
|
+
'Show / hide facet labels',
|
|
86
|
+
LabelsIcon,
|
|
87
|
+
'showLabels'
|
|
88
|
+
),
|
|
89
|
+
],
|
|
90
|
+
when: ctx => ctx.getSurfaceModelsByType(EdgyFacetsElementModel).length > 0,
|
|
91
|
+
} as const satisfies ToolbarModuleConfig;
|
|
92
|
+
|
|
93
|
+
export const edgyToolbarExtension = ToolbarModuleExtension({
|
|
94
|
+
id: BlockFlavourIdentifier('affine:surface:edgy'),
|
|
95
|
+
config: edgyToolbarConfig,
|
|
96
|
+
});
|