@formicoidea/labre-framework-wardley 0.23.0 → 0.23.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/dist/consts.d.ts +72 -0
- package/{src/consts.ts → dist/consts.js} +63 -72
- package/dist/descriptor.d.ts +7 -0
- package/{src/descriptor.ts → dist/descriptor.js} +1 -1
- package/dist/effects.d.ts +10 -0
- package/dist/effects.js +7 -0
- package/dist/element-renderer.d.ts +15 -0
- package/dist/element-renderer.js +160 -0
- package/dist/element-view.d.ts +21 -0
- package/dist/element-view.js +122 -0
- package/dist/gradient.d.ts +18 -0
- package/dist/gradient.js +112 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/label-layout.d.ts +21 -0
- package/dist/label-layout.js +73 -0
- package/dist/legend.d.ts +12 -0
- package/dist/legend.js +333 -0
- package/dist/node/consts.d.ts +107 -0
- package/{src/node/consts.ts → dist/node/consts.js} +12 -20
- package/dist/node/label-editor.d.ts +28 -0
- package/dist/node/label-editor.js +216 -0
- package/dist/node/node-renderer.d.ts +17 -0
- package/dist/node/node-renderer.js +106 -0
- package/{src/node/node-view.ts → dist/node/node-view.d.ts} +3 -3
- package/dist/node/node-view.js +10 -0
- package/dist/templates/index.d.ts +3 -0
- package/dist/templates/index.js +172 -0
- package/dist/templates/maps.d.ts +3 -0
- package/dist/templates/maps.js +247 -0
- package/dist/toolbar/config.d.ts +75 -0
- package/dist/toolbar/config.js +206 -0
- package/dist/toolbar/icons.d.ts +31 -0
- package/{src/toolbar/icons.ts → dist/toolbar/icons.js} +51 -66
- package/dist/toolbar/node-config.d.ts +2 -0
- package/{src/toolbar/node-config.ts → dist/toolbar/node-config.js} +7 -14
- package/dist/toolbar/senior-tool.d.ts +2 -0
- package/{src/toolbar/senior-tool.ts → dist/toolbar/senior-tool.js} +5 -5
- package/dist/toolbar/wardley-menu.d.ts +53 -0
- package/dist/toolbar/wardley-menu.js +408 -0
- package/dist/toolbar/wardley-senior-button.d.ts +18 -0
- package/dist/toolbar/wardley-senior-button.js +146 -0
- package/dist/toolbar/wardley-tool-button.d.ts +10 -0
- package/dist/toolbar/wardley-tool-button.js +123 -0
- package/dist/view.d.ts +7 -0
- package/dist/view.js +36 -0
- package/package.json +15 -6
- package/src/effects.ts +0 -17
- package/src/element-renderer.ts +0 -242
- package/src/element-view.ts +0 -143
- package/src/gradient.ts +0 -137
- package/src/index.ts +0 -1
- package/src/label-layout.ts +0 -126
- package/src/legend.ts +0 -438
- package/src/node/node-renderer.ts +0 -142
- package/src/templates/index.ts +0 -236
- package/src/templates/maps.ts +0 -283
- package/src/toolbar/config.ts +0 -280
- package/src/toolbar/wardley-menu.ts +0 -552
- package/src/toolbar/wardley-senior-button.ts +0 -154
- package/src/view.ts +0 -39
|
@@ -7,33 +7,29 @@
|
|
|
7
7
|
* pre-formatted defaults at creation. The person glyph (anchor) is expressed as
|
|
8
8
|
* ratios of the circle radius R so it stays proportional at any size.
|
|
9
9
|
*/
|
|
10
|
-
|
|
11
10
|
/** Default node diameter (= the map label font size, 18). White fill, thin border. */
|
|
12
11
|
export const NODE_SIZE = 18;
|
|
13
12
|
export const NODE_FILL = '#ffffff';
|
|
14
13
|
export const NODE_STROKE = '#1f2328';
|
|
15
14
|
/** Thin border, matching the link / silhouette line weight. */
|
|
16
15
|
export const NODE_STROKE_WIDTH = 1;
|
|
17
|
-
|
|
18
16
|
/**
|
|
19
17
|
* Person glyph (`kind: 'anchor'`) ratios of R, mirroring the validated icon
|
|
20
18
|
* ANCH-B (circle r6 → head r1.8 at cy-2.6 ; shoulders cubic touching the
|
|
21
19
|
* border at ±(4.6, 3.9) with controls at ±(3.8, 0.1)).
|
|
22
20
|
*/
|
|
23
21
|
export const ANCHOR = {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
headR: 0.3,
|
|
23
|
+
headCY: -0.433,
|
|
24
|
+
shoulderEndX: 0.767,
|
|
25
|
+
shoulderEndY: 0.65,
|
|
26
|
+
shoulderCtrlX: 0.633,
|
|
27
|
+
shoulderCtrlY: 0.017,
|
|
30
28
|
};
|
|
31
|
-
|
|
32
29
|
/** Native text label, same family as the axis labels, size 18. */
|
|
33
30
|
export const LABEL_FONT_SIZE = 18;
|
|
34
31
|
export const LABEL_GAP = 8;
|
|
35
32
|
export const LABEL_DEFAULT = { component: 'Component', anchor: 'Anchor' };
|
|
36
|
-
|
|
37
33
|
/**
|
|
38
34
|
* Pipeline defaults. The body is a wide, thin native rect (≈ 1.4× the node
|
|
39
35
|
* diameter tall) with a white slightly-transparent fill and a node-weight
|
|
@@ -48,14 +44,12 @@ export const PIPELINE_FILL = '#ffffff99';
|
|
|
48
44
|
/** Handle square = node diameter. */
|
|
49
45
|
export const HANDLE_SIZE = NODE_SIZE;
|
|
50
46
|
export const PIPELINE_LABEL = 'Pipeline';
|
|
51
|
-
|
|
52
47
|
/**
|
|
53
48
|
* Connector line width. Connectors are constrained to the LineWidth enum
|
|
54
49
|
* {2,4,6,8,10,12}, so the thinnest available (2) is used — as close as possible
|
|
55
50
|
* to the 1px node border.
|
|
56
51
|
*/
|
|
57
52
|
export const LINK_STROKE_WIDTH = 2;
|
|
58
|
-
|
|
59
53
|
/** Wardley red ("future"/evolution) — matches the validated arrow icon. */
|
|
60
54
|
export const WARDLEY_RED = '#d6455d';
|
|
61
55
|
/** Dependency link grey. */
|
|
@@ -63,7 +57,6 @@ export const LINK_GREY = '#666666';
|
|
|
63
57
|
/** Inertia bar color + size. */
|
|
64
58
|
export const INERTIA_COLOR = '#1f2328';
|
|
65
59
|
export const INERTIA_SIZE = { w: 8, h: 44 };
|
|
66
|
-
|
|
67
60
|
/**
|
|
68
61
|
* Market node (composite): a large thin-bordered circle containing 3 small
|
|
69
62
|
* thick-bordered component nodes wired into a triangle by native connectors.
|
|
@@ -78,7 +71,6 @@ export const MARKET_DOT_STROKE_WIDTH = 2;
|
|
|
78
71
|
export const MARKET_LINK_WIDTH = 1;
|
|
79
72
|
export const MARKET_LINK_COLOR = NODE_STROKE;
|
|
80
73
|
export const MARKET_LABEL = 'Market';
|
|
81
|
-
|
|
82
74
|
/**
|
|
83
75
|
* Ecosystem node: a single connectable circle drawn as a GLYPH — a double border
|
|
84
76
|
* at the rim (outer circle + a 2nd inscribed circle with a thin blank band
|
|
@@ -88,13 +80,12 @@ export const MARKET_LABEL = 'Market';
|
|
|
88
80
|
*/
|
|
89
81
|
export const ECOSYSTEM_SIZE = 40;
|
|
90
82
|
export const ECOSYSTEM = {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
83
|
+
secondBorderRatio: 0.88, // 2nd (inscribed) border
|
|
84
|
+
centerRatio: 0.43, // central hollow circle
|
|
85
|
+
hatchOuterRatio: 0.86, // hatch stays just inside the 2nd border
|
|
86
|
+
hatchSpacingRatio: 0.15, // gap between hatch lines
|
|
95
87
|
};
|
|
96
88
|
export const ECOSYSTEM_LABEL = 'Ecosystem';
|
|
97
|
-
|
|
98
89
|
/**
|
|
99
90
|
* Method node: a component inscribed in a slightly larger outer circle whose
|
|
100
91
|
* FILL color encodes the chosen method (editable via the toolbar). Glyph = the
|
|
@@ -104,6 +95,7 @@ export const ECOSYSTEM_LABEL = 'Ecosystem';
|
|
|
104
95
|
export const METHOD_SIZE = 35;
|
|
105
96
|
export const METHOD_FILL = '#d9d9d9';
|
|
106
97
|
export const METHOD = {
|
|
107
|
-
|
|
98
|
+
centerRatio: 0.5, // inner white component radius / R
|
|
108
99
|
};
|
|
109
100
|
export const METHOD_LABEL = 'Component';
|
|
101
|
+
//# sourceMappingURL=consts.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { WardleyNodeElementModel } from '@blocksuite/affine-model';
|
|
2
|
+
import type { RichText } from '@blocksuite/affine-rich-text';
|
|
3
|
+
import { type BlockComponent, type BlockStdScope, ShadowlessElement } from '@blocksuite/std';
|
|
4
|
+
import { nothing } from 'lit';
|
|
5
|
+
/**
|
|
6
|
+
* Mount an inline editor over a Wardley node's label. A trimmed clone of
|
|
7
|
+
* `EdgelessShapeTextEditor`, bound to {@link WardleyNodeElementModel.text} and
|
|
8
|
+
* positioned at the label spot (right of the circle + `labelOffset`).
|
|
9
|
+
*/
|
|
10
|
+
export declare function mountWardleyNodeLabelEditor(node: WardleyNodeElementModel, edgeless: BlockComponent): void;
|
|
11
|
+
declare const EdgelessWardleyNodeLabelEditor_base: typeof ShadowlessElement & import("@blocksuite/global/utils").Constructor<import("@blocksuite/global/lit").DisposableClass>;
|
|
12
|
+
export declare class EdgelessWardleyNodeLabelEditor extends EdgelessWardleyNodeLabelEditor_base {
|
|
13
|
+
get inlineEditor(): import("@blocksuite/affine-shared/types").AffineInlineEditor | null;
|
|
14
|
+
get crud(): import("@blocksuite/affine-block-surface").EdgelessCRUDExtension;
|
|
15
|
+
get gfx(): import("@blocksuite/std/gfx").GfxController;
|
|
16
|
+
get selection(): import("@blocksuite/std/gfx").GfxSelectionManager;
|
|
17
|
+
get inlineEditorContainer(): import("@blocksuite/std/inline").InlineRootElement<import("@blocksuite/affine-shared/types").AffineTextAttributes> | null | undefined;
|
|
18
|
+
private _unmount;
|
|
19
|
+
connectedCallback(): void;
|
|
20
|
+
firstUpdated(): void;
|
|
21
|
+
getUpdateComplete(): Promise<boolean>;
|
|
22
|
+
render(): typeof nothing | import("lit-html").TemplateResult<1>;
|
|
23
|
+
accessor element: WardleyNodeElementModel;
|
|
24
|
+
accessor std: BlockStdScope;
|
|
25
|
+
accessor richText: RichText;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=label-editor.d.ts.map
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
var __esDecorate = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
2
|
+
function accept(f) {
|
|
3
|
+
if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
|
|
4
|
+
return f;
|
|
5
|
+
}
|
|
6
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
7
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
8
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
9
|
+
var _, done = false;
|
|
10
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
11
|
+
var context = {};
|
|
12
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
13
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
14
|
+
context.addInitializer = function(f) {
|
|
15
|
+
if (done) throw new TypeError("Cannot add initializers after decoration has completed");
|
|
16
|
+
extraInitializers.push(accept(f || null));
|
|
17
|
+
};
|
|
18
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
19
|
+
if (kind === "accessor") {
|
|
20
|
+
if (result === void 0) continue;
|
|
21
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
22
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
23
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
24
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
25
|
+
} else if (_ = accept(result)) {
|
|
26
|
+
if (kind === "field") initializers.unshift(_);
|
|
27
|
+
else descriptor[key] = _;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
31
|
+
done = true;
|
|
32
|
+
};
|
|
33
|
+
var __runInitializers = function(thisArg, initializers, value) {
|
|
34
|
+
var useValue = arguments.length > 2;
|
|
35
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
36
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
37
|
+
}
|
|
38
|
+
return useValue ? value : void 0;
|
|
39
|
+
};
|
|
40
|
+
import { DefaultTool, EdgelessCRUDIdentifier, TextUtils } from "@blocksuite/affine-block-surface";
|
|
41
|
+
import { WardleyNodeElementModel } from "@blocksuite/affine-model";
|
|
42
|
+
import { BlockSuiteError, ErrorCode } from "@blocksuite/global/exceptions";
|
|
43
|
+
import { WithDisposable } from "@blocksuite/global/lit";
|
|
44
|
+
import { ShadowlessElement, stdContext } from "@blocksuite/std";
|
|
45
|
+
import { GfxControllerIdentifier } from "@blocksuite/std/gfx";
|
|
46
|
+
import { RANGE_SYNC_EXCLUDE_ATTR } from "@blocksuite/std/inline";
|
|
47
|
+
import { consume } from "@lit/context";
|
|
48
|
+
import { html, nothing } from "lit";
|
|
49
|
+
import { property, query } from "lit/decorators.js";
|
|
50
|
+
import { styleMap } from "lit/directives/style-map.js";
|
|
51
|
+
import * as Y from "yjs";
|
|
52
|
+
import { LABEL } from "./consts";
|
|
53
|
+
export function mountWardleyNodeLabelEditor(node, edgeless) {
|
|
54
|
+
const mountElm = edgeless.querySelector(".edgeless-mount-point");
|
|
55
|
+
if (!mountElm) {
|
|
56
|
+
throw new BlockSuiteError(ErrorCode.ValueNotExists, "edgeless block's mount point does not exist");
|
|
57
|
+
}
|
|
58
|
+
const gfx = edgeless.std.get(GfxControllerIdentifier);
|
|
59
|
+
const crud = edgeless.std.get(EdgelessCRUDIdentifier);
|
|
60
|
+
const updated = crud.getElementById(node.id);
|
|
61
|
+
if (!(updated instanceof WardleyNodeElementModel)) {
|
|
62
|
+
console.error("Cannot mount label editor on a non-wardley-node element");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
gfx.tool.setTool(DefaultTool);
|
|
66
|
+
gfx.selection.set({ elements: [node.id], editing: true });
|
|
67
|
+
if (!node.text) {
|
|
68
|
+
crud.updateElement(node.id, { text: new Y.Text() });
|
|
69
|
+
}
|
|
70
|
+
const editor = new EdgelessWardleyNodeLabelEditor();
|
|
71
|
+
editor.element = crud.getElementById(node.id);
|
|
72
|
+
mountElm.append(editor);
|
|
73
|
+
}
|
|
74
|
+
let EdgelessWardleyNodeLabelEditor = (() => {
|
|
75
|
+
let _classSuper = WithDisposable(ShadowlessElement);
|
|
76
|
+
let _element_decorators;
|
|
77
|
+
let _element_initializers = [];
|
|
78
|
+
let _element_extraInitializers = [];
|
|
79
|
+
let _std_decorators;
|
|
80
|
+
let _std_initializers = [];
|
|
81
|
+
let _std_extraInitializers = [];
|
|
82
|
+
let _richText_decorators;
|
|
83
|
+
let _richText_initializers = [];
|
|
84
|
+
let _richText_extraInitializers = [];
|
|
85
|
+
return class EdgelessWardleyNodeLabelEditor extends _classSuper {
|
|
86
|
+
static {
|
|
87
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
88
|
+
_element_decorators = [property({ attribute: false })];
|
|
89
|
+
_std_decorators = [consume({ context: stdContext })];
|
|
90
|
+
_richText_decorators = [query("rich-text")];
|
|
91
|
+
__esDecorate(this, null, _element_decorators, { kind: "accessor", name: "element", static: false, private: false, access: { has: (obj) => "element" in obj, get: (obj) => obj.element, set: (obj, value) => {
|
|
92
|
+
obj.element = value;
|
|
93
|
+
} }, metadata: _metadata }, _element_initializers, _element_extraInitializers);
|
|
94
|
+
__esDecorate(this, null, _std_decorators, { kind: "accessor", name: "std", static: false, private: false, access: { has: (obj) => "std" in obj, get: (obj) => obj.std, set: (obj, value) => {
|
|
95
|
+
obj.std = value;
|
|
96
|
+
} }, metadata: _metadata }, _std_initializers, _std_extraInitializers);
|
|
97
|
+
__esDecorate(this, null, _richText_decorators, { kind: "accessor", name: "richText", static: false, private: false, access: { has: (obj) => "richText" in obj, get: (obj) => obj.richText, set: (obj, value) => {
|
|
98
|
+
obj.richText = value;
|
|
99
|
+
} }, metadata: _metadata }, _richText_initializers, _richText_extraInitializers);
|
|
100
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
101
|
+
}
|
|
102
|
+
get inlineEditor() {
|
|
103
|
+
return this.richText.inlineEditor;
|
|
104
|
+
}
|
|
105
|
+
get crud() {
|
|
106
|
+
return this.std.get(EdgelessCRUDIdentifier);
|
|
107
|
+
}
|
|
108
|
+
get gfx() {
|
|
109
|
+
return this.std.get(GfxControllerIdentifier);
|
|
110
|
+
}
|
|
111
|
+
get selection() {
|
|
112
|
+
return this.gfx.selection;
|
|
113
|
+
}
|
|
114
|
+
get inlineEditorContainer() {
|
|
115
|
+
return this.inlineEditor?.rootElement;
|
|
116
|
+
}
|
|
117
|
+
_unmount() {
|
|
118
|
+
if (this.element.text) {
|
|
119
|
+
const text = this.element.text.toString();
|
|
120
|
+
const trimmed = text.trim();
|
|
121
|
+
if (trimmed.length === 0) {
|
|
122
|
+
this.crud.updateElement(this.element.id, { text: void 0 });
|
|
123
|
+
} else if (trimmed.length < text.length) {
|
|
124
|
+
this.crud.updateElement(this.element.id, {
|
|
125
|
+
text: new Y.Text(trimmed)
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
this.remove();
|
|
130
|
+
this.selection.set({ elements: [], editing: false });
|
|
131
|
+
}
|
|
132
|
+
connectedCallback() {
|
|
133
|
+
super.connectedCallback();
|
|
134
|
+
this.setAttribute(RANGE_SYNC_EXCLUDE_ATTR, "true");
|
|
135
|
+
}
|
|
136
|
+
firstUpdated() {
|
|
137
|
+
this.disposables.add(this.gfx.viewport.viewportUpdated.subscribe(() => this.requestUpdate()));
|
|
138
|
+
this.updateComplete.then(() => {
|
|
139
|
+
if (!this.inlineEditor)
|
|
140
|
+
return;
|
|
141
|
+
this.inlineEditor.focusEnd();
|
|
142
|
+
if (!this.inlineEditorContainer)
|
|
143
|
+
return;
|
|
144
|
+
this.disposables.addFromEvent(this.inlineEditorContainer, "blur", () => this._unmount());
|
|
145
|
+
}).catch(console.error);
|
|
146
|
+
this.disposables.addFromEvent(this, "keydown", (evt) => {
|
|
147
|
+
if (evt.key === "Escape") {
|
|
148
|
+
this.ownerDocument.activeElement?.blur();
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
async getUpdateComplete() {
|
|
153
|
+
const result = await super.getUpdateComplete();
|
|
154
|
+
await this.richText?.updateComplete;
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
render() {
|
|
158
|
+
if (!this.element.text)
|
|
159
|
+
return nothing;
|
|
160
|
+
const [, , w, h] = this.element.deserializedXYWH;
|
|
161
|
+
const R = Math.min(w, h) / 2;
|
|
162
|
+
const [ox, oy] = this.element.labelOffset ?? [0, 0];
|
|
163
|
+
const modelX = this.element.x + w / 2 + R + LABEL.gap + ox;
|
|
164
|
+
const modelY = this.element.y + h / 2 + oy - LABEL.font / 2;
|
|
165
|
+
const [x, y] = this.gfx.viewport.toViewCoord(modelX, modelY);
|
|
166
|
+
const zoom = this.gfx.viewport.zoom;
|
|
167
|
+
const style = styleMap({
|
|
168
|
+
position: "absolute",
|
|
169
|
+
left: `${x}px`,
|
|
170
|
+
top: `${y}px`,
|
|
171
|
+
minWidth: "8px",
|
|
172
|
+
fontSize: `${LABEL.font}px`,
|
|
173
|
+
fontFamily: TextUtils.wrapFontFamily(LABEL.family),
|
|
174
|
+
lineHeight: "normal",
|
|
175
|
+
color: LABEL.color,
|
|
176
|
+
outline: "none",
|
|
177
|
+
whiteSpace: "nowrap",
|
|
178
|
+
transform: `scale(${zoom}, ${zoom})`,
|
|
179
|
+
transformOrigin: "top left",
|
|
180
|
+
zIndex: "1"
|
|
181
|
+
});
|
|
182
|
+
return html`<rich-text
|
|
183
|
+
.yText=${this.element.text}
|
|
184
|
+
.enableFormat=${false}
|
|
185
|
+
.enableAutoScrollHorizontally=${false}
|
|
186
|
+
style=${style}
|
|
187
|
+
></rich-text>`;
|
|
188
|
+
}
|
|
189
|
+
#element = __runInitializers(this, _element_initializers, void 0);
|
|
190
|
+
get element() {
|
|
191
|
+
return this.#element;
|
|
192
|
+
}
|
|
193
|
+
set element(_) {
|
|
194
|
+
this.#element = _;
|
|
195
|
+
}
|
|
196
|
+
#std = (__runInitializers(this, _element_extraInitializers), __runInitializers(this, _std_initializers, void 0));
|
|
197
|
+
get std() {
|
|
198
|
+
return this.#std;
|
|
199
|
+
}
|
|
200
|
+
set std(_) {
|
|
201
|
+
this.#std = _;
|
|
202
|
+
}
|
|
203
|
+
#richText = (__runInitializers(this, _std_extraInitializers), __runInitializers(this, _richText_initializers, void 0));
|
|
204
|
+
get richText() {
|
|
205
|
+
return this.#richText;
|
|
206
|
+
}
|
|
207
|
+
set richText(_) {
|
|
208
|
+
this.#richText = _;
|
|
209
|
+
}
|
|
210
|
+
constructor() {
|
|
211
|
+
super(...arguments);
|
|
212
|
+
__runInitializers(this, _richText_extraInitializers);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
})();
|
|
216
|
+
export { EdgelessWardleyNodeLabelEditor };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ElementRenderer } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { type WardleyNodeElementModel } from '@formicoidea/labre-core/model';
|
|
3
|
+
/**
|
|
4
|
+
* Renderer for a Wardley node. The circle is drawn by REUSING the native shape
|
|
5
|
+
* renderer (so stroke width, colors and theme behave exactly like a native
|
|
6
|
+
* ellipse). On top of it, a glyph is drawn for two kinds:
|
|
7
|
+
* - `anchor` → an inscribed person (head + shoulders), clipped to the circle.
|
|
8
|
+
* - `ecosystem` → a double border at the rim + diagonal hatching confined to the
|
|
9
|
+
* inner donut + a hollow central circle.
|
|
10
|
+
* All glyph strokes use the model's (editable) stroke color; the white band and
|
|
11
|
+
* the hollow center come from the base fill, so colors stay editable.
|
|
12
|
+
*/
|
|
13
|
+
export declare const wardleyNode: ElementRenderer<WardleyNodeElementModel>;
|
|
14
|
+
export declare const WardleyNodeRendererExtension: import("@formicoidea/labre-core/store").ExtensionType & {
|
|
15
|
+
identifier: import("@formicoidea/labre-core/global/di").ServiceIdentifier<ElementRenderer<WardleyNodeElementModel>>;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=node-renderer.d.ts.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { ElementRendererExtension, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { shape as shapeRenderer } from '@formicoidea/labre-core/gfx/shape';
|
|
3
|
+
import { DefaultTheme } from '@formicoidea/labre-core/model';
|
|
4
|
+
import { ANCHOR, ECOSYSTEM, METHOD, NODE_FILL } from './consts';
|
|
5
|
+
/**
|
|
6
|
+
* Renderer for a Wardley node. The circle is drawn by REUSING the native shape
|
|
7
|
+
* renderer (so stroke width, colors and theme behave exactly like a native
|
|
8
|
+
* ellipse). On top of it, a glyph is drawn for two kinds:
|
|
9
|
+
* - `anchor` → an inscribed person (head + shoulders), clipped to the circle.
|
|
10
|
+
* - `ecosystem` → a double border at the rim + diagonal hatching confined to the
|
|
11
|
+
* inner donut + a hollow central circle.
|
|
12
|
+
* All glyph strokes use the model's (editable) stroke color; the white band and
|
|
13
|
+
* the hollow center come from the base fill, so colors stay editable.
|
|
14
|
+
*/
|
|
15
|
+
export const wardleyNode = (model, ctx, matrix, renderer, rc, bound) => {
|
|
16
|
+
const [, , w, h] = model.deserializedXYWH;
|
|
17
|
+
const cx = w / 2;
|
|
18
|
+
const cy = h / 2;
|
|
19
|
+
// Capture the element-local transform BEFORE the shape renderer mutates the
|
|
20
|
+
// matrix, so the glyph can be drawn in the same space afterwards.
|
|
21
|
+
const glyphMatrix = DOMMatrix.fromMatrix(matrix)
|
|
22
|
+
.translateSelf(cx, cy)
|
|
23
|
+
.rotateSelf(model.rotate)
|
|
24
|
+
.translateSelf(-cx, -cy);
|
|
25
|
+
// Native ellipse (fill / stroke / theme handled natively).
|
|
26
|
+
shapeRenderer(model, ctx, matrix, renderer, rc, bound);
|
|
27
|
+
if (model.kind !== 'anchor' &&
|
|
28
|
+
model.kind !== 'ecosystem' &&
|
|
29
|
+
model.kind !== 'method')
|
|
30
|
+
return;
|
|
31
|
+
const strokeWidth = model.strokeWidth || 1;
|
|
32
|
+
const R = Math.min(w, h) / 2 - strokeWidth / 2;
|
|
33
|
+
const color = renderer.getColorValue(model.strokeColor, DefaultTheme.shapeStrokeColor, true);
|
|
34
|
+
ctx.setTransform(glyphMatrix);
|
|
35
|
+
// ── Anchor: person glyph (clipped to the circle) ────────────────────
|
|
36
|
+
if (model.kind === 'anchor') {
|
|
37
|
+
ctx.save();
|
|
38
|
+
ctx.beginPath();
|
|
39
|
+
ctx.arc(cx, cy, R - strokeWidth / 2, 0, Math.PI * 2);
|
|
40
|
+
ctx.clip();
|
|
41
|
+
ctx.lineWidth = strokeWidth;
|
|
42
|
+
ctx.strokeStyle = color;
|
|
43
|
+
ctx.lineCap = 'round';
|
|
44
|
+
ctx.lineJoin = 'round';
|
|
45
|
+
// head
|
|
46
|
+
ctx.beginPath();
|
|
47
|
+
ctx.arc(cx, cy + ANCHOR.headCY * R, ANCHOR.headR * R, 0, Math.PI * 2);
|
|
48
|
+
ctx.stroke();
|
|
49
|
+
// rounded shoulders (extremities sit on the circle border)
|
|
50
|
+
const sx = ANCHOR.shoulderEndX * R;
|
|
51
|
+
const sy = ANCHOR.shoulderEndY * R;
|
|
52
|
+
const kx = ANCHOR.shoulderCtrlX * R;
|
|
53
|
+
const ky = ANCHOR.shoulderCtrlY * R;
|
|
54
|
+
ctx.beginPath();
|
|
55
|
+
ctx.moveTo(cx - sx, cy + sy);
|
|
56
|
+
ctx.bezierCurveTo(cx - kx, cy + ky, cx + kx, cy + ky, cx + sx, cy + sy);
|
|
57
|
+
ctx.stroke();
|
|
58
|
+
ctx.restore();
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// ── Method: a white component inscribed in the colored outer circle ──
|
|
62
|
+
if (model.kind === 'method') {
|
|
63
|
+
const rInner = R * METHOD.centerRatio;
|
|
64
|
+
ctx.fillStyle = NODE_FILL;
|
|
65
|
+
ctx.lineWidth = strokeWidth;
|
|
66
|
+
ctx.strokeStyle = color;
|
|
67
|
+
ctx.beginPath();
|
|
68
|
+
ctx.arc(cx, cy, rInner, 0, Math.PI * 2);
|
|
69
|
+
ctx.fill();
|
|
70
|
+
ctx.stroke();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// ── Ecosystem: double border + hatched inner donut + hollow center ──
|
|
74
|
+
const rBorder2 = R * ECOSYSTEM.secondBorderRatio;
|
|
75
|
+
const rCenter = R * ECOSYSTEM.centerRatio;
|
|
76
|
+
const rHatch = R * ECOSYSTEM.hatchOuterRatio;
|
|
77
|
+
// Hatch confined to the donut [rCenter, rHatch] (even-odd clip = annulus).
|
|
78
|
+
ctx.save();
|
|
79
|
+
ctx.beginPath();
|
|
80
|
+
ctx.arc(cx, cy, rHatch, 0, Math.PI * 2);
|
|
81
|
+
ctx.moveTo(cx + rCenter, cy);
|
|
82
|
+
ctx.arc(cx, cy, rCenter, 0, Math.PI * 2);
|
|
83
|
+
ctx.clip('evenodd');
|
|
84
|
+
ctx.lineWidth = Math.max(0.5, strokeWidth * 0.6);
|
|
85
|
+
ctx.strokeStyle = color;
|
|
86
|
+
const step = R * ECOSYSTEM.hatchSpacingRatio;
|
|
87
|
+
for (let d = -2 * R; d <= 2 * R; d += step) {
|
|
88
|
+
ctx.beginPath();
|
|
89
|
+
ctx.moveTo(cx - R, cy - R + d);
|
|
90
|
+
ctx.lineTo(cx + R, cy + R + d);
|
|
91
|
+
ctx.stroke();
|
|
92
|
+
}
|
|
93
|
+
ctx.restore();
|
|
94
|
+
// Double border (2nd inscribed circle) + central hole border. No fill: the
|
|
95
|
+
// white band and hollow center come from the base ellipse fill.
|
|
96
|
+
ctx.lineWidth = strokeWidth;
|
|
97
|
+
ctx.strokeStyle = color;
|
|
98
|
+
ctx.beginPath();
|
|
99
|
+
ctx.arc(cx, cy, rBorder2, 0, Math.PI * 2);
|
|
100
|
+
ctx.stroke();
|
|
101
|
+
ctx.beginPath();
|
|
102
|
+
ctx.arc(cx, cy, rCenter, 0, Math.PI * 2);
|
|
103
|
+
ctx.stroke();
|
|
104
|
+
};
|
|
105
|
+
export const WardleyNodeRendererExtension = ElementRendererExtension('wardleyNode', wardleyNode);
|
|
106
|
+
//# sourceMappingURL=node-renderer.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { WardleyNodeElementModel } from '@formicoidea/labre-core/model';
|
|
2
2
|
import { GfxElementModelView } from '@formicoidea/labre-core/std/gfx';
|
|
3
|
-
|
|
4
3
|
/**
|
|
5
4
|
* View for a Wardley node. Registering it ensures `gfx.view.get(model)` returns
|
|
6
5
|
* a view (required so move / select / connector interactions work). The label
|
|
7
6
|
* is a separate native text element, so no custom editing is needed here.
|
|
8
7
|
*/
|
|
9
|
-
export class WardleyNodeView extends GfxElementModelView<WardleyNodeElementModel> {
|
|
10
|
-
|
|
8
|
+
export declare class WardleyNodeView extends GfxElementModelView<WardleyNodeElementModel> {
|
|
9
|
+
static type: string;
|
|
11
10
|
}
|
|
11
|
+
//# sourceMappingURL=node-view.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { GfxElementModelView } from '@formicoidea/labre-core/std/gfx';
|
|
2
|
+
/**
|
|
3
|
+
* View for a Wardley node. Registering it ensures `gfx.view.get(model)` returns
|
|
4
|
+
* a view (required so move / select / connector interactions work). The label
|
|
5
|
+
* is a separate native text element, so no custom editing is needed here.
|
|
6
|
+
*/
|
|
7
|
+
export class WardleyNodeView extends GfxElementModelView {
|
|
8
|
+
static { this.type = 'wardleyNode'; }
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=node-view.js.map
|