@formicoidea/labre-framework-edgy 0.23.1 → 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 +6 -15
- package/{dist/consts.js → src/consts.ts} +50 -46
- package/{dist/descriptor.js → src/descriptor.ts} +1 -1
- package/src/effects.ts +14 -0
- package/src/element-renderer.ts +208 -0
- package/src/element-view.ts +145 -0
- package/src/index.ts +1 -0
- package/src/label-layout.ts +105 -0
- package/{dist/node/consts.js → src/node/consts.ts} +56 -49
- package/src/node/node-renderer.ts +64 -0
- package/{dist/node/node-view.js → src/node/node-view.ts} +33 -27
- package/src/templates/index.ts +254 -0
- package/src/toolbar/config.ts +96 -0
- package/src/toolbar/edgy-menu.ts +242 -0
- package/{dist/toolbar/edgy-senior-button.js → src/toolbar/edgy-senior-button.ts} +102 -97
- package/{dist/toolbar/icons.js → src/toolbar/icons.ts} +25 -20
- package/src/toolbar/node-config.ts +202 -0
- package/{dist/toolbar/senior-tool.js → src/toolbar/senior-tool.ts} +11 -11
- package/src/view.ts +39 -0
- package/dist/consts.d.ts +0 -47
- package/dist/descriptor.d.ts +0 -7
- package/dist/effects.d.ts +0 -10
- package/dist/effects.js +0 -7
- package/dist/element-renderer.d.ts +0 -17
- package/dist/element-renderer.js +0 -177
- package/dist/element-view.d.ts +0 -19
- package/dist/element-view.js +0 -123
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/label-layout.d.ts +0 -46
- package/dist/label-layout.js +0 -69
- package/dist/node/consts.d.ts +0 -34
- package/dist/node/node-renderer.d.ts +0 -14
- package/dist/node/node-renderer.js +0 -40
- package/dist/node/node-view.d.ts +0 -16
- package/dist/templates/index.d.ts +0 -3
- package/dist/templates/index.js +0 -210
- package/dist/toolbar/config.d.ts +0 -20
- package/dist/toolbar/config.js +0 -71
- package/dist/toolbar/edgy-menu.d.ts +0 -31
- package/dist/toolbar/edgy-menu.js +0 -195
- package/dist/toolbar/edgy-senior-button.d.ts +0 -16
- package/dist/toolbar/icons.d.ts +0 -13
- package/dist/toolbar/node-config.d.ts +0 -2
- package/dist/toolbar/node-config.js +0 -162
- package/dist/toolbar/senior-tool.d.ts +0 -2
- package/dist/view.d.ts +0 -7
- package/dist/view.js +0 -36
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import {
|
|
3
|
+
packColor,
|
|
4
|
+
type PickColorEvent,
|
|
5
|
+
} from '@formicoidea/labre-core/components/color-picker';
|
|
6
|
+
import { shapeToolbarConfig } from '@formicoidea/labre-core/gfx/shape';
|
|
7
|
+
import {
|
|
8
|
+
type Color,
|
|
9
|
+
DefaultTheme,
|
|
10
|
+
isTransparent,
|
|
11
|
+
LineWidth,
|
|
12
|
+
type Palette,
|
|
13
|
+
resolveColor,
|
|
14
|
+
ShapeElementModel,
|
|
15
|
+
StrokeStyle,
|
|
16
|
+
} from '@formicoidea/labre-core/model';
|
|
17
|
+
import {
|
|
18
|
+
type ToolbarContext,
|
|
19
|
+
type ToolbarModuleConfig,
|
|
20
|
+
ToolbarModuleExtension,
|
|
21
|
+
} from '@formicoidea/labre-core/shared/services';
|
|
22
|
+
import { getMostCommonValue } from '@formicoidea/labre-core/shared/utils';
|
|
23
|
+
import { BlockFlavourIdentifier } from '@formicoidea/labre-core/std';
|
|
24
|
+
import { html } from 'lit';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The typical EDGY palette, surfaced as ready-made swatches in the EDGY node
|
|
28
|
+
* color picker (facet + intersection colours, saturated then pastel), followed
|
|
29
|
+
* by the default editor palette.
|
|
30
|
+
*/
|
|
31
|
+
const EDGY_PALETTES: Palette[] = [
|
|
32
|
+
{ key: 'Identity', value: '#00ea4e' },
|
|
33
|
+
{ key: 'Architecture', value: '#034cee' },
|
|
34
|
+
{ key: 'Experience', value: '#ff0056' },
|
|
35
|
+
{ key: 'Organisation', value: '#00caf4' },
|
|
36
|
+
{ key: 'Brand', value: '#ffa500' },
|
|
37
|
+
{ key: 'Product', value: '#cf00ff' },
|
|
38
|
+
{ key: 'Identity light', value: '#80ffb7' },
|
|
39
|
+
{ key: 'Architecture light', value: '#a6c0ff' },
|
|
40
|
+
{ key: 'Experience light', value: '#ff99bd' },
|
|
41
|
+
{ key: 'Organisation light', value: '#80eaff' },
|
|
42
|
+
{ key: 'Brand light', value: '#ffd580' },
|
|
43
|
+
{ key: 'Product light', value: '#e599ff' },
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* From the default editor palette we keep ONLY the neutrals (greys, white,
|
|
48
|
+
* black, transparent) — the historical colours are dropped in favour of the
|
|
49
|
+
* EDGY swatches above.
|
|
50
|
+
*/
|
|
51
|
+
const NEUTRAL_KEY = /grey|gray|white|black|transparent/i;
|
|
52
|
+
|
|
53
|
+
const EDGY_PALETTE_LIST: Palette[] = [
|
|
54
|
+
...EDGY_PALETTES,
|
|
55
|
+
...DefaultTheme.Palettes.filter(p => NEUTRAL_KEY.test(p.key)),
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
// Mirror of the shape color action's text-color rule.
|
|
59
|
+
function getTextColor(fillColor: Color) {
|
|
60
|
+
if (fillColor === DefaultTheme.black) return DefaultTheme.white;
|
|
61
|
+
if (fillColor === DefaultTheme.white) return DefaultTheme.black;
|
|
62
|
+
return DefaultTheme.shapeTextColor;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* EDGY fill / stroke colour picker — identical to the shape one but seeded with
|
|
67
|
+
* the EDGY palette swatches (`.palettes`).
|
|
68
|
+
*/
|
|
69
|
+
const edgyColorAction = {
|
|
70
|
+
id: 'e.color',
|
|
71
|
+
when(ctx: ToolbarContext) {
|
|
72
|
+
return ctx.getSurfaceModelsByType(ShapeElementModel).length > 0;
|
|
73
|
+
},
|
|
74
|
+
content(ctx: ToolbarContext) {
|
|
75
|
+
const models = ctx.getSurfaceModelsByType(ShapeElementModel);
|
|
76
|
+
if (!models.length) return null;
|
|
77
|
+
|
|
78
|
+
const enableCustomColor = ctx.features.getFlag('enable_color_picker');
|
|
79
|
+
const theme = ctx.theme.edgeless$.value;
|
|
80
|
+
|
|
81
|
+
const firstModel = models[0];
|
|
82
|
+
const originalFillColor = firstModel.fillColor;
|
|
83
|
+
const originalStrokeColor = firstModel.strokeColor;
|
|
84
|
+
|
|
85
|
+
const mapped = models.map(
|
|
86
|
+
({ filled, fillColor, strokeColor, strokeWidth, strokeStyle }) => ({
|
|
87
|
+
fillColor: filled
|
|
88
|
+
? resolveColor(fillColor, theme)
|
|
89
|
+
: DefaultTheme.transparent,
|
|
90
|
+
strokeColor: resolveColor(strokeColor, theme),
|
|
91
|
+
strokeWidth,
|
|
92
|
+
strokeStyle,
|
|
93
|
+
})
|
|
94
|
+
);
|
|
95
|
+
const fillColor =
|
|
96
|
+
getMostCommonValue(mapped, 'fillColor') ??
|
|
97
|
+
resolveColor(DefaultTheme.shapeFillColor, theme);
|
|
98
|
+
const strokeColor =
|
|
99
|
+
getMostCommonValue(mapped, 'strokeColor') ??
|
|
100
|
+
resolveColor(DefaultTheme.shapeStrokeColor, theme);
|
|
101
|
+
const strokeWidth =
|
|
102
|
+
getMostCommonValue(mapped, 'strokeWidth') ?? LineWidth.Four;
|
|
103
|
+
const strokeStyle =
|
|
104
|
+
getMostCommonValue(mapped, 'strokeStyle') ?? StrokeStyle.Solid;
|
|
105
|
+
|
|
106
|
+
const pickColorWrapper =
|
|
107
|
+
(field: string, pickCallback: (palette: Palette) => void) =>
|
|
108
|
+
(e: CustomEvent<PickColorEvent>) => {
|
|
109
|
+
e.stopPropagation();
|
|
110
|
+
switch (e.detail.type) {
|
|
111
|
+
case 'pick':
|
|
112
|
+
pickCallback(e.detail.detail);
|
|
113
|
+
break;
|
|
114
|
+
case 'start':
|
|
115
|
+
ctx.store.captureSync();
|
|
116
|
+
models.forEach(model => model.stash(field));
|
|
117
|
+
break;
|
|
118
|
+
case 'end':
|
|
119
|
+
ctx.store.transact(() => {
|
|
120
|
+
models.forEach(model => model.pop(field));
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const onPickFillColor = pickColorWrapper('fillColor', palette => {
|
|
126
|
+
const value = palette.value;
|
|
127
|
+
const filled = isTransparent(value);
|
|
128
|
+
const props = packColor('fillColor', value);
|
|
129
|
+
const crud = ctx.std.get(EdgelessCRUDIdentifier);
|
|
130
|
+
models.forEach(model => {
|
|
131
|
+
if (filled && !model.filled) {
|
|
132
|
+
const color = getTextColor(value);
|
|
133
|
+
Object.assign(props, { filled, color });
|
|
134
|
+
}
|
|
135
|
+
crud.updateElement(model.id, props);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const onPickStrokeColor = pickColorWrapper('strokeColor', palette => {
|
|
140
|
+
const props = packColor('strokeColor', palette.value);
|
|
141
|
+
const crud = ctx.std.get(EdgelessCRUDIdentifier);
|
|
142
|
+
models.forEach(model => crud.updateElement(model.id, props));
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const onPickStrokeStyle = (
|
|
146
|
+
e: CustomEvent<{ type: string; value: number & StrokeStyle }>
|
|
147
|
+
) => {
|
|
148
|
+
e.stopPropagation();
|
|
149
|
+
const { type, value } = e.detail;
|
|
150
|
+
const crud = ctx.std.get(EdgelessCRUDIdentifier);
|
|
151
|
+
const props =
|
|
152
|
+
type === 'size'
|
|
153
|
+
? { strokeWidth: value as number }
|
|
154
|
+
: { strokeStyle: value as StrokeStyle };
|
|
155
|
+
for (const model of models) {
|
|
156
|
+
crud.updateElement(model.id, props);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
return html`
|
|
161
|
+
<edgeless-shape-color-picker
|
|
162
|
+
@pickFillColor=${onPickFillColor}
|
|
163
|
+
@pickStrokeColor=${onPickStrokeColor}
|
|
164
|
+
@pickStrokeStyle=${onPickStrokeStyle}
|
|
165
|
+
.palettes=${EDGY_PALETTE_LIST}
|
|
166
|
+
.payload=${{
|
|
167
|
+
fillColor,
|
|
168
|
+
strokeColor,
|
|
169
|
+
strokeWidth,
|
|
170
|
+
strokeStyle,
|
|
171
|
+
originalFillColor,
|
|
172
|
+
originalStrokeColor,
|
|
173
|
+
theme,
|
|
174
|
+
enableCustomColor,
|
|
175
|
+
}}
|
|
176
|
+
>
|
|
177
|
+
</edgeless-shape-color-picker>
|
|
178
|
+
`;
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* EDGY nodes are {@link ShapeElementModel} subclasses, so the shape toolbar's
|
|
184
|
+
* actions operate on them directly. We reuse the line-style + text actions, add
|
|
185
|
+
* the EDGY-seeded color picker, and drop the actions that don't fit an EDGY base
|
|
186
|
+
* shape (switch shape type, edit polygon vertices).
|
|
187
|
+
*/
|
|
188
|
+
const KEEP_FROM_SHAPE = (id: string) =>
|
|
189
|
+
id === 'd.style' || id === 'f.text' || id.startsWith('g.text-');
|
|
190
|
+
|
|
191
|
+
const edgyNodeToolbarConfig = {
|
|
192
|
+
actions: [
|
|
193
|
+
...shapeToolbarConfig.actions.filter(action => KEEP_FROM_SHAPE(action.id)),
|
|
194
|
+
edgyColorAction,
|
|
195
|
+
],
|
|
196
|
+
when: shapeToolbarConfig.when,
|
|
197
|
+
} as ToolbarModuleConfig;
|
|
198
|
+
|
|
199
|
+
export const edgyNodeToolbarExtension = ToolbarModuleExtension({
|
|
200
|
+
id: BlockFlavourIdentifier('affine:surface:edgyNode'),
|
|
201
|
+
config: edgyNodeToolbarConfig,
|
|
202
|
+
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { SeniorToolExtension } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
|
|
2
|
-
import { html } from 'lit';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
1
|
+
import { SeniorToolExtension } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
|
|
4
|
+
export const edgySeniorTool = SeniorToolExtension('edgy', ({ block }) => {
|
|
5
|
+
return {
|
|
6
|
+
name: 'EDGY',
|
|
7
|
+
content: html`<edgeless-edgy-senior-button
|
|
8
|
+
.edgeless=${block}
|
|
9
|
+
></edgeless-edgy-senior-button>`,
|
|
10
|
+
};
|
|
11
|
+
});
|
package/src/view.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ViewExtensionContext,
|
|
3
|
+
ViewExtensionProvider,
|
|
4
|
+
} from '@formicoidea/labre-core/ext-loader';
|
|
5
|
+
import { extendTemplateCategory } from '@formicoidea/labre-core/gfx/template';
|
|
6
|
+
|
|
7
|
+
import { effects } from './effects';
|
|
8
|
+
import { edgyTemplateCategory } from './templates';
|
|
9
|
+
import { EdgyFacetsRendererExtension } from './element-renderer';
|
|
10
|
+
import { EdgyInteraction, EdgyView } from './element-view';
|
|
11
|
+
import { EdgyNodeRendererExtension } from './node/node-renderer';
|
|
12
|
+
import { EdgyNodeView } from './node/node-view';
|
|
13
|
+
import { edgyToolbarExtension } from './toolbar/config';
|
|
14
|
+
import { edgyNodeToolbarExtension } from './toolbar/node-config';
|
|
15
|
+
import { edgySeniorTool } from './toolbar/senior-tool';
|
|
16
|
+
|
|
17
|
+
export class EdgyViewExtension extends ViewExtensionProvider {
|
|
18
|
+
override name = 'affine-edgy-gfx';
|
|
19
|
+
|
|
20
|
+
override effect(): void {
|
|
21
|
+
super.effect();
|
|
22
|
+
effects();
|
|
23
|
+
extendTemplateCategory(edgyTemplateCategory);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
override setup(context: ViewExtensionContext) {
|
|
27
|
+
super.setup(context);
|
|
28
|
+
context.register(EdgyView);
|
|
29
|
+
context.register(EdgyFacetsRendererExtension);
|
|
30
|
+
context.register(EdgyNodeView);
|
|
31
|
+
context.register(EdgyNodeRendererExtension);
|
|
32
|
+
if (this.isEdgeless(context.scope)) {
|
|
33
|
+
context.register(EdgyInteraction);
|
|
34
|
+
context.register(edgySeniorTool);
|
|
35
|
+
context.register(edgyToolbarExtension);
|
|
36
|
+
context.register(edgyNodeToolbarExtension);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
package/dist/consts.d.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Visual constants for the EDGY "Enterprise Design Facets" diagram.
|
|
3
|
-
*
|
|
4
|
-
* The whole diagram is authored in a FIXED reference coordinate space
|
|
5
|
-
* (`REF_W × REF_H`, matching the validated mockup) and the renderer scales it
|
|
6
|
-
* uniformly to the element bounds — so the three circles always stay circular
|
|
7
|
-
* and the pictos/labels keep their relative proportions at any size.
|
|
8
|
-
*/
|
|
9
|
-
/** Reference design size (the validated mockup canvas). */
|
|
10
|
-
export declare const REF_W = 680;
|
|
11
|
-
export declare const REF_H = 400;
|
|
12
|
-
/** Circle geometry in reference coords. */
|
|
13
|
-
export declare const VENN: {
|
|
14
|
-
readonly cx: number;
|
|
15
|
-
readonly cy: 176;
|
|
16
|
-
/** Circle radius. */
|
|
17
|
-
readonly R: 95;
|
|
18
|
-
/** Distance of each circle centre from the diagram centre. */
|
|
19
|
-
readonly r0: 56;
|
|
20
|
-
};
|
|
21
|
-
/** Saturated facet + intersection colours (faithful to the official icons). */
|
|
22
|
-
export declare const COLORS: {
|
|
23
|
-
readonly identity: "#00ea4e";
|
|
24
|
-
readonly architecture: "#034cee";
|
|
25
|
-
readonly experience: "#ff0056";
|
|
26
|
-
readonly organisation: "#00caf4";
|
|
27
|
-
readonly brand: "#ffa500";
|
|
28
|
-
readonly product: "#cf00ff";
|
|
29
|
-
readonly center: "#ffffff";
|
|
30
|
-
readonly separator: "#ffffff";
|
|
31
|
-
readonly picto: "#ffffff";
|
|
32
|
-
};
|
|
33
|
-
/** Fixed picto line width + label font. */
|
|
34
|
-
export declare const PICTO_STROKE = 2.4;
|
|
35
|
-
export declare const LABEL_FONT_SIZE = 15;
|
|
36
|
-
export declare const FONT_FAMILY = "Inter, sans-serif";
|
|
37
|
-
/**
|
|
38
|
-
* Uniform fit of the reference design into an element of size `w × h`: the
|
|
39
|
-
* scale factor plus the centering offsets (letterboxed). Shared by the renderer
|
|
40
|
-
* (to draw) and the view (to map clicks back into reference coords).
|
|
41
|
-
*/
|
|
42
|
-
export declare function refScale(w: number, h: number): {
|
|
43
|
-
s: number;
|
|
44
|
-
ox: number;
|
|
45
|
-
oy: number;
|
|
46
|
-
};
|
|
47
|
-
//# sourceMappingURL=consts.d.ts.map
|
package/dist/descriptor.d.ts
DELETED
package/dist/effects.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { EdgelessEdgyMenu } from './toolbar/edgy-menu';
|
|
2
|
-
import { EdgelessEdgySeniorButton } from './toolbar/edgy-senior-button';
|
|
3
|
-
export declare function effects(): void;
|
|
4
|
-
declare global {
|
|
5
|
-
interface HTMLElementTagNameMap {
|
|
6
|
-
'edgeless-edgy-menu': EdgelessEdgyMenu;
|
|
7
|
-
'edgeless-edgy-senior-button': EdgelessEdgySeniorButton;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
//# sourceMappingURL=effects.d.ts.map
|
package/dist/effects.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { EdgelessEdgyMenu } from './toolbar/edgy-menu';
|
|
2
|
-
import { EdgelessEdgySeniorButton } from './toolbar/edgy-senior-button';
|
|
3
|
-
export function effects() {
|
|
4
|
-
customElements.define('edgeless-edgy-menu', EdgelessEdgyMenu);
|
|
5
|
-
customElements.define('edgeless-edgy-senior-button', EdgelessEdgySeniorButton);
|
|
6
|
-
}
|
|
7
|
-
//# sourceMappingURL=effects.js.map
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { type ElementRenderer } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
-
import type { EdgyFacetsElementModel } from '@formicoidea/labre-core/model';
|
|
3
|
-
/**
|
|
4
|
-
* Canvas renderer for the EDGY Enterprise Design Facets diagram — reproduces the
|
|
5
|
-
* validated mockup: three overlapping circles (Identity / Architecture /
|
|
6
|
-
* Experience), the three pairwise intersection regions (Organisation / Brand /
|
|
7
|
-
* Product) painted by clip-intersection, the white centre, the six white
|
|
8
|
-
* pictograms and the three facet labels placed outside the circles.
|
|
9
|
-
*
|
|
10
|
-
* The whole diagram is drawn in the fixed reference space and scaled uniformly
|
|
11
|
-
* to the element bounds so the circles never distort.
|
|
12
|
-
*/
|
|
13
|
-
export declare const edgy: ElementRenderer<EdgyFacetsElementModel>;
|
|
14
|
-
export declare const EdgyFacetsRendererExtension: import("@formicoidea/labre-core/store").ExtensionType & {
|
|
15
|
-
identifier: import("@formicoidea/labre-core/global/di").ServiceIdentifier<ElementRenderer<EdgyFacetsElementModel>>;
|
|
16
|
-
};
|
|
17
|
-
//# sourceMappingURL=element-renderer.d.ts.map
|
package/dist/element-renderer.js
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
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';
|
|
3
|
-
import { CIRCLE_A, CIRCLE_B, CIRCLE_C, facetLabelAnchors } from './label-layout';
|
|
4
|
-
/**
|
|
5
|
-
* Canvas renderer for the EDGY Enterprise Design Facets diagram — reproduces the
|
|
6
|
-
* validated mockup: three overlapping circles (Identity / Architecture /
|
|
7
|
-
* Experience), the three pairwise intersection regions (Organisation / Brand /
|
|
8
|
-
* Product) painted by clip-intersection, the white centre, the six white
|
|
9
|
-
* pictograms and the three facet labels placed outside the circles.
|
|
10
|
-
*
|
|
11
|
-
* The whole diagram is drawn in the fixed reference space and scaled uniformly
|
|
12
|
-
* to the element bounds so the circles never distort.
|
|
13
|
-
*/
|
|
14
|
-
export const edgy = (model, ctx, matrix) => {
|
|
15
|
-
const [, , w, h] = model.deserializedXYWH;
|
|
16
|
-
const cx = w / 2;
|
|
17
|
-
const cy = h / 2;
|
|
18
|
-
ctx.setTransform(matrix.translateSelf(cx, cy).rotateSelf(model.rotate).translateSelf(-cx, -cy));
|
|
19
|
-
// Uniform fit of the reference design, centered (letterboxed).
|
|
20
|
-
const { s, ox, oy } = refScale(w, h);
|
|
21
|
-
ctx.translate(ox, oy);
|
|
22
|
-
ctx.scale(s, s);
|
|
23
|
-
const A = CIRCLE_A;
|
|
24
|
-
const B = CIRCLE_B;
|
|
25
|
-
const C = CIRCLE_C;
|
|
26
|
-
const R = VENN.R;
|
|
27
|
-
const disc = (c, color) => {
|
|
28
|
-
ctx.beginPath();
|
|
29
|
-
ctx.arc(c.x, c.y, R, 0, Math.PI * 2);
|
|
30
|
-
ctx.fillStyle = color;
|
|
31
|
-
ctx.fill();
|
|
32
|
-
};
|
|
33
|
-
// Paint the region common to every circle in `cs` (clip-intersection).
|
|
34
|
-
const inter = (cs, color) => {
|
|
35
|
-
ctx.save();
|
|
36
|
-
for (const c of cs) {
|
|
37
|
-
ctx.beginPath();
|
|
38
|
-
ctx.arc(c.x, c.y, R, 0, Math.PI * 2);
|
|
39
|
-
ctx.clip();
|
|
40
|
-
}
|
|
41
|
-
ctx.fillStyle = color;
|
|
42
|
-
ctx.fillRect(0, 0, REF_W, REF_H);
|
|
43
|
-
ctx.restore();
|
|
44
|
-
};
|
|
45
|
-
// ── Facets + intersections ──────────────────────────────────────────
|
|
46
|
-
disc(A, COLORS.identity);
|
|
47
|
-
disc(B, COLORS.architecture);
|
|
48
|
-
disc(C, COLORS.experience);
|
|
49
|
-
inter([A, B], COLORS.organisation);
|
|
50
|
-
inter([A, C], COLORS.brand);
|
|
51
|
-
inter([B, C], COLORS.product);
|
|
52
|
-
inter([A, B, C], COLORS.center);
|
|
53
|
-
// White separating outlines.
|
|
54
|
-
ctx.strokeStyle = COLORS.separator;
|
|
55
|
-
ctx.lineWidth = 2.5;
|
|
56
|
-
for (const c of [A, B, C]) {
|
|
57
|
-
ctx.beginPath();
|
|
58
|
-
ctx.arc(c.x, c.y, R, 0, Math.PI * 2);
|
|
59
|
-
ctx.stroke();
|
|
60
|
-
}
|
|
61
|
-
// ── White pictos ────────────────────────────────────────────────────
|
|
62
|
-
const pictoSetup = () => {
|
|
63
|
-
ctx.strokeStyle = COLORS.picto;
|
|
64
|
-
ctx.fillStyle = COLORS.picto;
|
|
65
|
-
ctx.lineWidth = PICTO_STROKE;
|
|
66
|
-
ctx.lineJoin = 'round';
|
|
67
|
-
ctx.lineCap = 'round';
|
|
68
|
-
};
|
|
69
|
-
const lens = (x, y) => {
|
|
70
|
-
pictoSetup();
|
|
71
|
-
ctx.beginPath();
|
|
72
|
-
ctx.arc(x, y, 10, 0, Math.PI * 2);
|
|
73
|
-
ctx.stroke();
|
|
74
|
-
ctx.beginPath();
|
|
75
|
-
ctx.moveTo(x + 7, y + 7);
|
|
76
|
-
ctx.lineTo(x + 15, y + 15);
|
|
77
|
-
ctx.stroke();
|
|
78
|
-
};
|
|
79
|
-
const house = (x, y) => {
|
|
80
|
-
pictoSetup();
|
|
81
|
-
ctx.beginPath();
|
|
82
|
-
ctx.moveTo(x - 11, y + 9);
|
|
83
|
-
ctx.lineTo(x - 11, y - 2);
|
|
84
|
-
ctx.lineTo(x, y - 11);
|
|
85
|
-
ctx.lineTo(x + 11, y - 2);
|
|
86
|
-
ctx.lineTo(x + 11, y + 9);
|
|
87
|
-
ctx.closePath();
|
|
88
|
-
ctx.stroke();
|
|
89
|
-
};
|
|
90
|
-
const heart = (x, y) => {
|
|
91
|
-
pictoSetup();
|
|
92
|
-
ctx.beginPath();
|
|
93
|
-
ctx.moveTo(x, y + 11);
|
|
94
|
-
ctx.bezierCurveTo(x - 14, y - 1, x - 9, y - 12, x, y - 4);
|
|
95
|
-
ctx.bezierCurveTo(x + 9, y - 12, x + 14, y - 1, x, y + 11);
|
|
96
|
-
ctx.closePath();
|
|
97
|
-
ctx.stroke();
|
|
98
|
-
};
|
|
99
|
-
const network = (x, y) => {
|
|
100
|
-
pictoSetup();
|
|
101
|
-
ctx.beginPath();
|
|
102
|
-
ctx.moveTo(x, y - 9);
|
|
103
|
-
ctx.lineTo(x - 9, y + 7);
|
|
104
|
-
ctx.lineTo(x + 9, y + 7);
|
|
105
|
-
ctx.closePath();
|
|
106
|
-
ctx.stroke();
|
|
107
|
-
for (const [px, py] of [
|
|
108
|
-
[x, y - 9],
|
|
109
|
-
[x - 9, y + 7],
|
|
110
|
-
[x + 9, y + 7],
|
|
111
|
-
]) {
|
|
112
|
-
ctx.beginPath();
|
|
113
|
-
ctx.arc(px, py, 2.6, 0, Math.PI * 2);
|
|
114
|
-
ctx.fill();
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
const sun = (x, y) => {
|
|
118
|
-
pictoSetup();
|
|
119
|
-
ctx.beginPath();
|
|
120
|
-
ctx.arc(x, y, 6, 0, Math.PI * 2);
|
|
121
|
-
ctx.stroke();
|
|
122
|
-
for (let i = 0; i < 8; i++) {
|
|
123
|
-
const a = (i * Math.PI) / 4;
|
|
124
|
-
ctx.beginPath();
|
|
125
|
-
ctx.moveTo(x + Math.cos(a) * 9, y + Math.sin(a) * 9);
|
|
126
|
-
ctx.lineTo(x + Math.cos(a) * 12, y + Math.sin(a) * 12);
|
|
127
|
-
ctx.stroke();
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
const cube = (x, y) => {
|
|
131
|
-
pictoSetup();
|
|
132
|
-
const p = [
|
|
133
|
-
[x, y - 11],
|
|
134
|
-
[x + 10, y - 5],
|
|
135
|
-
[x + 10, y + 6],
|
|
136
|
-
[x, y + 12],
|
|
137
|
-
[x - 10, y + 6],
|
|
138
|
-
[x - 10, y - 5],
|
|
139
|
-
];
|
|
140
|
-
ctx.beginPath();
|
|
141
|
-
p.forEach(([qx, qy], i) => (i ? ctx.lineTo(qx, qy) : ctx.moveTo(qx, qy)));
|
|
142
|
-
ctx.closePath();
|
|
143
|
-
ctx.stroke();
|
|
144
|
-
ctx.beginPath();
|
|
145
|
-
ctx.moveTo(x - 10, y - 5);
|
|
146
|
-
ctx.lineTo(x, y + 1);
|
|
147
|
-
ctx.lineTo(x + 10, y - 5);
|
|
148
|
-
ctx.moveTo(x, y + 1);
|
|
149
|
-
ctx.lineTo(x, y + 12);
|
|
150
|
-
ctx.stroke();
|
|
151
|
-
};
|
|
152
|
-
// Single-facet zone pictos.
|
|
153
|
-
lens(A.x - 30, A.y - 22);
|
|
154
|
-
house(B.x + 30, B.y - 22);
|
|
155
|
-
heart(C.x, C.y + 36);
|
|
156
|
-
// Intersection pictos at the validated sweet spots.
|
|
157
|
-
network(VENN.cx, 114);
|
|
158
|
-
sun(VENN.cx - 58, 206);
|
|
159
|
-
cube(VENN.cx + 58, 206);
|
|
160
|
-
// ── Labels (outside the circles) ────────────────────────────────────
|
|
161
|
-
if (model.showLabels) {
|
|
162
|
-
ctx.font = `500 ${LABEL_FONT_SIZE}px ${FONT_FAMILY}`;
|
|
163
|
-
ctx.textBaseline = 'middle';
|
|
164
|
-
const colorByField = {
|
|
165
|
-
identityLabel: COLORS.identity,
|
|
166
|
-
architectureLabel: COLORS.architecture,
|
|
167
|
-
experienceLabel: COLORS.experience,
|
|
168
|
-
};
|
|
169
|
-
for (const { field, text, x, y, align } of facetLabelAnchors(model)) {
|
|
170
|
-
ctx.fillStyle = colorByField[field];
|
|
171
|
-
ctx.textAlign = align;
|
|
172
|
-
ctx.fillText(text, x, y);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
export const EdgyFacetsRendererExtension = ElementRendererExtension('edgy', edgy);
|
|
177
|
-
//# sourceMappingURL=element-renderer.js.map
|
package/dist/element-view.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { EdgyFacetsElementModel } from '@formicoidea/labre-core/model';
|
|
2
|
-
import { GfxElementModelView } from '@formicoidea/labre-core/std/gfx';
|
|
3
|
-
export declare class EdgyView extends GfxElementModelView<EdgyFacetsElementModel> {
|
|
4
|
-
static type: string;
|
|
5
|
-
/** The in-place `<input>` used to edit a label, or null when idle. */
|
|
6
|
-
private _labelEditor;
|
|
7
|
-
onCreated(): void;
|
|
8
|
-
onDestroyed(): void;
|
|
9
|
-
/** Double-click on a label → edit its text in place. */
|
|
10
|
-
private _onDblClick;
|
|
11
|
-
private _openLabelEditor;
|
|
12
|
-
private _closeLabelEditor;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Resize gating: the resize handles are hidden unless `model.resizeEnabled` is
|
|
16
|
-
* true (toggled from the toolbar). Moving/selecting stays available throughout.
|
|
17
|
-
*/
|
|
18
|
-
export declare const EdgyInteraction: import("@formicoidea/labre-core/store").ExtensionType;
|
|
19
|
-
//# sourceMappingURL=element-view.d.ts.map
|
package/dist/element-view.js
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
-
import { rotatePoint } from '@formicoidea/labre-core/global/gfx';
|
|
3
|
-
import { GfxElementModelView, GfxViewInteractionExtension, } from '@formicoidea/labre-core/std/gfx';
|
|
4
|
-
import { refScale } from './consts';
|
|
5
|
-
import { getEdgyLabelHits, hitTestEdgyLabel, } from './label-layout';
|
|
6
|
-
export class EdgyView extends GfxElementModelView {
|
|
7
|
-
constructor() {
|
|
8
|
-
super(...arguments);
|
|
9
|
-
/** The in-place `<input>` used to edit a label, or null when idle. */
|
|
10
|
-
this._labelEditor = null;
|
|
11
|
-
}
|
|
12
|
-
static { this.type = 'edgy'; }
|
|
13
|
-
onCreated() {
|
|
14
|
-
super.onCreated();
|
|
15
|
-
this.on('dblclick', e => this._onDblClick(e));
|
|
16
|
-
}
|
|
17
|
-
onDestroyed() {
|
|
18
|
-
this._closeLabelEditor();
|
|
19
|
-
super.onDestroyed();
|
|
20
|
-
}
|
|
21
|
-
/** Double-click on a label → edit its text in place. */
|
|
22
|
-
_onDblClick(e) {
|
|
23
|
-
if (this.model.isLocked())
|
|
24
|
-
return;
|
|
25
|
-
const [mx, my] = this.gfx.viewport.toModelCoord(e.x, e.y);
|
|
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
|
-
// Map element-local coords into the fixed reference space the labels live in.
|
|
39
|
-
const { s, ox, oy } = refScale(w, h);
|
|
40
|
-
const rx = (lx - ox) / s;
|
|
41
|
-
const ry = (ly - oy) / s;
|
|
42
|
-
const hit = hitTestEdgyLabel(getEdgyLabelHits(this.model), rx, ry);
|
|
43
|
-
if (!hit)
|
|
44
|
-
return;
|
|
45
|
-
this._openLabelEditor(hit.field, e);
|
|
46
|
-
}
|
|
47
|
-
_openLabelEditor(field, e) {
|
|
48
|
-
this._closeLabelEditor();
|
|
49
|
-
const input = document.createElement('input');
|
|
50
|
-
input.value = String(this.model[field] ?? '');
|
|
51
|
-
Object.assign(input.style, {
|
|
52
|
-
position: 'fixed',
|
|
53
|
-
left: `${e.raw.clientX}px`,
|
|
54
|
-
top: `${e.raw.clientY}px`,
|
|
55
|
-
transform: 'translate(-50%, -50%)',
|
|
56
|
-
zIndex: '10000',
|
|
57
|
-
minWidth: '140px',
|
|
58
|
-
padding: '3px 8px',
|
|
59
|
-
font: '14px Inter, sans-serif',
|
|
60
|
-
color: 'var(--affine-text-primary-color, #1f2328)',
|
|
61
|
-
background: 'var(--affine-background-overlay-panel-color, #ffffff)',
|
|
62
|
-
border: '1px solid var(--affine-primary-color, #1e96eb)',
|
|
63
|
-
borderRadius: '6px',
|
|
64
|
-
boxShadow: 'var(--affine-shadow-2, 0 2px 8px rgba(0,0,0,0.18))',
|
|
65
|
-
outline: 'none',
|
|
66
|
-
});
|
|
67
|
-
document.body.append(input);
|
|
68
|
-
this._labelEditor = input;
|
|
69
|
-
// Mark the element as "editing" so the global edgeless key handlers
|
|
70
|
-
// (delete, escape, etc.) don't act on it while the user types.
|
|
71
|
-
this.gfx.selection.set({ elements: [this.model.id], editing: true });
|
|
72
|
-
input.focus();
|
|
73
|
-
input.select();
|
|
74
|
-
const commit = () => {
|
|
75
|
-
if (this._labelEditor !== input)
|
|
76
|
-
return;
|
|
77
|
-
const value = input.value;
|
|
78
|
-
this._closeLabelEditor();
|
|
79
|
-
this.gfx.std.store.captureSync();
|
|
80
|
-
this.gfx.std
|
|
81
|
-
.get(EdgelessCRUDIdentifier)
|
|
82
|
-
.updateElement(this.model.id, { [field]: value });
|
|
83
|
-
};
|
|
84
|
-
input.addEventListener('keydown', ev => {
|
|
85
|
-
ev.stopPropagation();
|
|
86
|
-
if (ev.key === 'Enter') {
|
|
87
|
-
ev.preventDefault();
|
|
88
|
-
commit();
|
|
89
|
-
}
|
|
90
|
-
else if (ev.key === 'Escape') {
|
|
91
|
-
ev.preventDefault();
|
|
92
|
-
this._closeLabelEditor();
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
input.addEventListener('blur', commit);
|
|
96
|
-
}
|
|
97
|
-
_closeLabelEditor() {
|
|
98
|
-
if (!this._labelEditor)
|
|
99
|
-
return;
|
|
100
|
-
const input = this._labelEditor;
|
|
101
|
-
this._labelEditor = null;
|
|
102
|
-
input.remove();
|
|
103
|
-
if (this.isConnected) {
|
|
104
|
-
this.gfx.selection.set({ elements: [this.model.id], editing: false });
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Resize gating: the resize handles are hidden unless `model.resizeEnabled` is
|
|
110
|
-
* true (toggled from the toolbar). Moving/selecting stays available throughout.
|
|
111
|
-
*/
|
|
112
|
-
export const EdgyInteraction = GfxViewInteractionExtension(EdgyView.type, {
|
|
113
|
-
handleResize({ model }) {
|
|
114
|
-
return {
|
|
115
|
-
beforeResize({ set }) {
|
|
116
|
-
if (!model.resizeEnabled) {
|
|
117
|
-
set({ allowedHandlers: [] });
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
},
|
|
122
|
-
});
|
|
123
|
-
//# sourceMappingURL=element-view.js.map
|
package/dist/index.d.ts
DELETED