@formicoidea/labre-framework-edgy 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.
Files changed (48) hide show
  1. package/dist/consts.d.ts +47 -0
  2. package/{src/consts.ts → dist/consts.js} +46 -50
  3. package/dist/descriptor.d.ts +7 -0
  4. package/{src/descriptor.ts → dist/descriptor.js} +1 -1
  5. package/dist/effects.d.ts +10 -0
  6. package/dist/effects.js +7 -0
  7. package/dist/element-renderer.d.ts +17 -0
  8. package/dist/element-renderer.js +177 -0
  9. package/dist/element-view.d.ts +19 -0
  10. package/dist/element-view.js +123 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.js +2 -0
  13. package/dist/label-layout.d.ts +46 -0
  14. package/dist/label-layout.js +69 -0
  15. package/dist/node/consts.d.ts +34 -0
  16. package/{src/node/consts.ts → dist/node/consts.js} +19 -26
  17. package/dist/node/node-renderer.d.ts +14 -0
  18. package/dist/node/node-renderer.js +40 -0
  19. package/dist/node/node-view.d.ts +16 -0
  20. package/{src/node/node-view.ts → dist/node/node-view.js} +15 -21
  21. package/dist/templates/index.d.ts +3 -0
  22. package/dist/templates/index.js +210 -0
  23. package/dist/toolbar/config.d.ts +20 -0
  24. package/dist/toolbar/config.js +71 -0
  25. package/dist/toolbar/edgy-menu.d.ts +31 -0
  26. package/dist/toolbar/edgy-menu.js +195 -0
  27. package/dist/toolbar/edgy-senior-button.d.ts +16 -0
  28. package/{src/toolbar/edgy-senior-button.ts → dist/toolbar/edgy-senior-button.js} +33 -38
  29. package/dist/toolbar/icons.d.ts +13 -0
  30. package/{src/toolbar/icons.ts → dist/toolbar/icons.js} +20 -25
  31. package/dist/toolbar/node-config.d.ts +2 -0
  32. package/dist/toolbar/node-config.js +162 -0
  33. package/dist/toolbar/senior-tool.d.ts +2 -0
  34. package/{src/toolbar/senior-tool.ts → dist/toolbar/senior-tool.js} +5 -5
  35. package/dist/view.d.ts +7 -0
  36. package/dist/view.js +36 -0
  37. package/package.json +15 -6
  38. package/src/effects.ts +0 -14
  39. package/src/element-renderer.ts +0 -208
  40. package/src/element-view.ts +0 -145
  41. package/src/index.ts +0 -1
  42. package/src/label-layout.ts +0 -105
  43. package/src/node/node-renderer.ts +0 -64
  44. package/src/templates/index.ts +0 -254
  45. package/src/toolbar/config.ts +0 -96
  46. package/src/toolbar/edgy-menu.ts +0 -242
  47. package/src/toolbar/node-config.ts +0 -202
  48. package/src/view.ts +0 -39
@@ -0,0 +1,195 @@
1
+ import { DefaultTool } from '@formicoidea/labre-core/blocks/surface';
2
+ import { createGroupCommand } from '@formicoidea/labre-core/gfx/group';
3
+ import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
4
+ import { FontFamily, ShapeStyle } from '@formicoidea/labre-core/model';
5
+ import { TelemetryProvider } from '@formicoidea/labre-core/shared/services';
6
+ import { EdgelessToolbarToolMixin } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
7
+ import { Bound } from '@formicoidea/labre-core/global/gfx';
8
+ import { css, html, LitElement } from 'lit';
9
+ import { REF_H, REF_W } from '../consts';
10
+ import { ACTIVITY_VERTICES, INNER_FONT_SIZE, LABEL_FONT_SIZE, LABEL_GAP, NODE_FILL, NODE_LABEL, NODE_SIZE, NODE_STROKE, NODE_STROKE_WIDTH, OUTCOME_RADIUS, } from '../node/consts';
11
+ import { edgyActivityIcon, edgyFacetsIcon, edgyObjectIcon, edgyOutcomeIcon, edgyPeopleIcon, } from './icons';
12
+ /** Default facets-diagram size (REF aspect, scaled up so it reads on canvas). */
13
+ const FACETS_SCALE = 1.5;
14
+ /** Height of the native People free-text label. */
15
+ const LABEL_H = LABEL_FONT_SIZE + 8;
16
+ /**
17
+ * The popover above the toolbar for the EDGY toolbox. Items create the facets
18
+ * diagram (the on-click Venn) and the four base-element prefab shapes — all
19
+ * native shapes so they stay editable.
20
+ */
21
+ export class EdgelessEdgyMenu extends EdgelessToolbarToolMixin(LitElement) {
22
+ constructor() {
23
+ super(...arguments);
24
+ this.type = EmptyTool;
25
+ }
26
+ static { this.styles = css `
27
+ :host {
28
+ position: absolute;
29
+ display: flex;
30
+ z-index: -1;
31
+ }
32
+ .menu-content {
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ }
37
+ .button-group-container {
38
+ display: flex;
39
+ align-items: center;
40
+ gap: 14px;
41
+ fill: var(--affine-icon-color);
42
+ }
43
+ .button-group-container svg {
44
+ width: 24px;
45
+ height: 24px;
46
+ }
47
+ `; }
48
+ /** Create the Enterprise Design Facets diagram centred on the viewport. */
49
+ _createFacets() {
50
+ const { gfx } = this;
51
+ if (!gfx.surface)
52
+ return;
53
+ const width = REF_W * FACETS_SCALE;
54
+ const height = REF_H * FACETS_SCALE;
55
+ const { centerX, centerY } = gfx.viewport;
56
+ const id = gfx.surface.addElement({
57
+ type: 'edgy',
58
+ xywh: new Bound(centerX - width / 2, centerY - height / 2, width, height).serialize(),
59
+ });
60
+ this._track('facets');
61
+ this._finish(id);
62
+ }
63
+ /** Add a native free-text label (Inter), used for the People node. */
64
+ _addLabel(surface, text, x, y) {
65
+ return surface.addElement({
66
+ type: 'text',
67
+ text,
68
+ fontFamily: FontFamily.Inter,
69
+ fontSize: LABEL_FONT_SIZE,
70
+ color: NODE_STROKE,
71
+ textAlign: 'center',
72
+ xywh: new Bound(x, y, 120, LABEL_H).serialize(),
73
+ });
74
+ }
75
+ _group(ids) {
76
+ const [, result] = this.edgeless.std.command.exec(createGroupCommand, {
77
+ elements: ids,
78
+ });
79
+ return result.groupId || ids[0];
80
+ }
81
+ /** Shared props for an EDGY node shape. */
82
+ _baseShapeProps(kind) {
83
+ return {
84
+ type: 'edgyNode',
85
+ kind,
86
+ filled: true,
87
+ fillColor: NODE_FILL,
88
+ strokeColor: NODE_STROKE,
89
+ shapeStyle: ShapeStyle.General,
90
+ roughness: 0,
91
+ };
92
+ }
93
+ /** Create a box base element (outcome / object / activity) with inner text. */
94
+ _createBox(kind) {
95
+ const surface = this.gfx.surface;
96
+ if (!surface)
97
+ return;
98
+ const { w, h } = NODE_SIZE[kind];
99
+ const { centerX: cx, centerY: cy } = this.gfx.viewport;
100
+ const shapeType = kind === 'activity' ? 'polygon' : 'rect';
101
+ const id = surface.addElement({
102
+ ...this._baseShapeProps(kind),
103
+ shapeType,
104
+ strokeWidth: NODE_STROKE_WIDTH,
105
+ radius: kind === 'outcome' ? OUTCOME_RADIUS : 0,
106
+ vertices: kind === 'activity' ? ACTIVITY_VERTICES : null,
107
+ text: NODE_LABEL[kind],
108
+ color: NODE_STROKE,
109
+ fontFamily: FontFamily.Inter,
110
+ fontSize: INNER_FONT_SIZE,
111
+ textAlign: 'center',
112
+ xywh: new Bound(cx - w / 2, cy - h / 2, w, h).serialize(),
113
+ });
114
+ this._track(`node:${kind}`);
115
+ this._finish(id);
116
+ }
117
+ /**
118
+ * Create the People base element: an (invisible) ellipse decorated with the
119
+ * person glyph by the renderer, plus a native text label below, grouped.
120
+ */
121
+ _createPeople() {
122
+ const surface = this.gfx.surface;
123
+ if (!surface)
124
+ return;
125
+ const { w, h } = NODE_SIZE.people;
126
+ const { centerX: cx, centerY: cy } = this.gfx.viewport;
127
+ const nodeId = surface.addElement({
128
+ ...this._baseShapeProps('people'),
129
+ shapeType: 'ellipse',
130
+ // No visible outline — People is just the glyph; the ellipse is the bound.
131
+ strokeWidth: 0,
132
+ xywh: new Bound(cx - w / 2, cy - h / 2, w, h).serialize(),
133
+ });
134
+ const labelId = this._addLabel(surface, NODE_LABEL.people, cx - 60, cy + h / 2 + LABEL_GAP);
135
+ this._track('node:people');
136
+ this._finish(this._group([nodeId, labelId]));
137
+ }
138
+ _finish(id) {
139
+ const { gfx } = this;
140
+ gfx.doc.captureSync();
141
+ gfx.tool.setTool(DefaultTool);
142
+ gfx.selection.set({ elements: [id], editing: false });
143
+ // Keep the palette open (native sub-menu behaviour).
144
+ }
145
+ _track(element) {
146
+ this.edgeless.std.getOptional(TelemetryProvider)?.track('FrameworkElementAdded', {
147
+ framework: 'edgy',
148
+ element,
149
+ page: 'whiteboard editor',
150
+ segment: 'edgy toolbox',
151
+ module: 'edgy menu',
152
+ });
153
+ }
154
+ render() {
155
+ return html `
156
+ <edgeless-slide-menu>
157
+ <div class="menu-content">
158
+ <div class="button-group-container">
159
+ <edgeless-tool-icon-button
160
+ .tooltip=${'Enterprise Design facets'}
161
+ @click=${this._createFacets}
162
+ >
163
+ ${edgyFacetsIcon}
164
+ </edgeless-tool-icon-button>
165
+ <edgeless-tool-icon-button
166
+ .tooltip=${'People'}
167
+ @click=${this._createPeople}
168
+ >
169
+ ${edgyPeopleIcon}
170
+ </edgeless-tool-icon-button>
171
+ <edgeless-tool-icon-button
172
+ .tooltip=${'Outcome'}
173
+ @click=${() => this._createBox('outcome')}
174
+ >
175
+ ${edgyOutcomeIcon}
176
+ </edgeless-tool-icon-button>
177
+ <edgeless-tool-icon-button
178
+ .tooltip=${'Object'}
179
+ @click=${() => this._createBox('object')}
180
+ >
181
+ ${edgyObjectIcon}
182
+ </edgeless-tool-icon-button>
183
+ <edgeless-tool-icon-button
184
+ .tooltip=${'Activity'}
185
+ @click=${() => this._createBox('activity')}
186
+ >
187
+ ${edgyActivityIcon}
188
+ </edgeless-tool-icon-button>
189
+ </div>
190
+ </div>
191
+ </edgeless-slide-menu>
192
+ `;
193
+ }
194
+ }
195
+ //# sourceMappingURL=edgy-menu.js.map
@@ -0,0 +1,16 @@
1
+ import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
2
+ import { LitElement } from 'lit';
3
+ declare const EdgelessEdgySeniorButton_base: typeof LitElement & import("@formicoidea/labre-core/global/utils").Constructor<import("@formicoidea/labre-core/widgets/edgeless-toolbar").EdgelessToolbarToolClass>;
4
+ /**
5
+ * Main toolbar button (colored facets glyph) that opens the EDGY toolbox
6
+ * sub-menu above the toolbar. Mirrors the Wardley senior button.
7
+ */
8
+ export declare class EdgelessEdgySeniorButton extends EdgelessEdgySeniorButton_base {
9
+ static styles: import("lit").CSSResult;
10
+ enableActiveBackground: boolean;
11
+ type: typeof EmptyTool;
12
+ private _toggleMenu;
13
+ render(): import("lit-html").TemplateResult<1>;
14
+ }
15
+ export {};
16
+ //# sourceMappingURL=edgy-senior-button.d.ts.map
@@ -3,17 +3,18 @@ import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
3
3
  import { EdgelessToolbarToolMixin } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
4
4
  import { SignalWatcher } from '@formicoidea/labre-core/global/lit';
5
5
  import { css, html, LitElement } from 'lit';
6
-
7
6
  import { edgyToolbarIcon } from './icons';
8
-
9
7
  /**
10
8
  * Main toolbar button (colored facets glyph) that opens the EDGY toolbox
11
9
  * sub-menu above the toolbar. Mirrors the Wardley senior button.
12
10
  */
13
- export class EdgelessEdgySeniorButton extends EdgelessToolbarToolMixin(
14
- SignalWatcher(LitElement)
15
- ) {
16
- static override styles = css`
11
+ export class EdgelessEdgySeniorButton extends EdgelessToolbarToolMixin(SignalWatcher(LitElement)) {
12
+ constructor() {
13
+ super(...arguments);
14
+ this.enableActiveBackground = true;
15
+ this.type = EmptyTool;
16
+ }
17
+ static { this.styles = css `
17
18
  :host,
18
19
  .edgy-button {
19
20
  display: block;
@@ -56,38 +57,31 @@ export class EdgelessEdgySeniorButton extends EdgelessToolbarToolMixin(
56
57
  --y: -10px;
57
58
  --s: 1.07;
58
59
  }
59
- `;
60
-
61
- override enableActiveBackground = true;
62
-
63
- override type = EmptyTool;
64
-
65
- private _toggleMenu() {
66
- if (this.popper) {
67
- this.popper.dispose();
68
- this.popper = null;
69
- return;
70
- }
71
- this.setEdgelessTool(DefaultTool);
72
- const menu = this.createPopper('edgeless-edgy-menu', this);
73
- menu.element.edgeless = this.edgeless;
74
-
75
- const el = menu.element as HTMLElement;
76
- const wrap = el.parentElement;
77
- if (wrap) {
78
- wrap.style.overflow = 'visible';
79
- wrap.style.justifyContent = 'flex-end';
60
+ `; }
61
+ _toggleMenu() {
62
+ if (this.popper) {
63
+ this.popper.dispose();
64
+ this.popper = null;
65
+ return;
66
+ }
67
+ this.setEdgelessTool(DefaultTool);
68
+ const menu = this.createPopper('edgeless-edgy-menu', this);
69
+ menu.element.edgeless = this.edgeless;
70
+ const el = menu.element;
71
+ const wrap = el.parentElement;
72
+ if (wrap) {
73
+ wrap.style.overflow = 'visible';
74
+ wrap.style.justifyContent = 'flex-end';
75
+ }
76
+ Object.assign(el.style, {
77
+ position: 'static',
78
+ width: 'max-content',
79
+ maxWidth: 'calc(100vw - 16px)',
80
+ marginLeft: '0',
81
+ });
80
82
  }
81
- Object.assign(el.style, {
82
- position: 'static',
83
- width: 'max-content',
84
- maxWidth: 'calc(100vw - 16px)',
85
- marginLeft: '0',
86
- });
87
- }
88
-
89
- override render() {
90
- return html`<edgeless-toolbar-button
83
+ render() {
84
+ return html `<edgeless-toolbar-button
91
85
  class="edgy-button"
92
86
  .tooltip=${this.popper ? '' : 'EDGY'}
93
87
  .tooltipOffset=${4}
@@ -98,5 +92,6 @@ export class EdgelessEdgySeniorButton extends EdgelessToolbarToolMixin(
98
92
  <div class="edgy-card">${edgyToolbarIcon}</div>
99
93
  </div>
100
94
  </edgeless-toolbar-button>`;
101
- }
95
+ }
102
96
  }
97
+ //# sourceMappingURL=edgy-senior-button.js.map
@@ -0,0 +1,13 @@
1
+ /** Colored EDGY facets glyph for the main toolbar button (3 overlapping circles). */
2
+ export declare const edgyToolbarIcon: import("lit-html").TemplateResult<2>;
3
+ /** Menu icon — the facets diagram (colored mini Venn). */
4
+ export declare const edgyFacetsIcon: import("lit-html").TemplateResult<2>;
5
+ /** People — person glyph (official Icon-People), uses currentColor. */
6
+ export declare const edgyPeopleIcon: import("lit-html").TemplateResult<2>;
7
+ /** Outcome — lightly rounded rectangle. */
8
+ export declare const edgyOutcomeIcon: import("lit-html").TemplateResult<2>;
9
+ /** Object — plain rectangle. */
10
+ export declare const edgyObjectIcon: import("lit-html").TemplateResult<2>;
11
+ /** Activity — right-pointing chevron. */
12
+ export declare const edgyActivityIcon: import("lit-html").TemplateResult<2>;
13
+ //# sourceMappingURL=icons.d.ts.map
@@ -1,38 +1,33 @@
1
- import { svg } from 'lit';
2
-
3
- /** Colored EDGY facets glyph for the main toolbar button (3 overlapping circles). */
4
- export const edgyToolbarIcon = svg`<svg width="100%" height="100%" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
1
+ import { svg } from 'lit';
2
+ /** Colored EDGY facets glyph for the main toolbar button (3 overlapping circles). */
3
+ export const edgyToolbarIcon = svg `<svg width="100%" height="100%" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
5
4
  <g opacity="0.95">
6
5
  <circle cx="22" cy="24" r="13" fill="#00ea4e"/>
7
6
  <circle cx="34" cy="24" r="13" fill="#034cee"/>
8
7
  <circle cx="28" cy="34" r="13" fill="#ff0056"/>
9
8
  </g>
10
- </svg>`;
11
-
12
- /** Menu icon the facets diagram (colored mini Venn). */
13
- export const edgyFacetsIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
9
+ </svg>`;
10
+ /** Menu icon — the facets diagram (colored mini Venn). */
11
+ export const edgyFacetsIcon = svg `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
14
12
  <circle cx="9.5" cy="10" r="6" fill="#00ea4e" opacity="0.92"/>
15
13
  <circle cx="14.5" cy="10" r="6" fill="#034cee" opacity="0.92"/>
16
14
  <circle cx="12" cy="14.5" r="6" fill="#ff0056" opacity="0.92"/>
17
- </svg>`;
18
-
19
- /** People person glyph (official Icon-People), uses currentColor. */
20
- export const edgyPeopleIcon = svg`<svg width="24" height="24" viewBox="0 0 32 32" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
15
+ </svg>`;
16
+ /** People — person glyph (official Icon-People), uses currentColor. */
17
+ export const edgyPeopleIcon = svg `<svg width="24" height="24" viewBox="0 0 32 32" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
21
18
  <path d="m16,19c-3.308,0-6-2.692-6-6v-4c0-3.308,2.692-6,6-6s6,2.692,6,6v4c0,3.308-2.692,6-6,6Zm0-14c-2.206,0-4,1.794-4,4v4c0,2.206,1.794,4,4,4s4-1.794,4-4v-4c0-2.206-1.794-4-4-4Z"/>
22
19
  <path d="m29,30H3v-3.5c0-3.308,2.692-6,6-6h14c3.308,0,6,2.692,6,6v3.5Zm-24-2h22v-1.5c0-2.206-1.794-4-4-4h-14c-2.206,0-4,1.794-4,4v1.5Z"/>
23
- </svg>`;
24
-
25
- /** Outcome lightly rounded rectangle. */
26
- export const edgyOutcomeIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
20
+ </svg>`;
21
+ /** Outcome — lightly rounded rectangle. */
22
+ export const edgyOutcomeIcon = svg `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
27
23
  <rect x="3.5" y="6.5" width="17" height="11" rx="2" stroke="currentColor" stroke-width="1.6"/>
28
- </svg>`;
29
-
30
- /** Object plain rectangle. */
31
- export const edgyObjectIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
24
+ </svg>`;
25
+ /** Object — plain rectangle. */
26
+ export const edgyObjectIcon = svg `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
32
27
  <rect x="3.5" y="6.5" width="17" height="11" stroke="currentColor" stroke-width="1.6"/>
33
- </svg>`;
34
-
35
- /** Activity right-pointing chevron. */
36
- export const edgyActivityIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
28
+ </svg>`;
29
+ /** Activity — right-pointing chevron. */
30
+ export const edgyActivityIcon = svg `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
37
31
  <path d="M3.5 6.5 H15 L20.5 12 L15 17.5 H3.5 Z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/>
38
- </svg>`;
32
+ </svg>`;
33
+ //# sourceMappingURL=icons.js.map
@@ -0,0 +1,2 @@
1
+ export declare const edgyNodeToolbarExtension: import("@formicoidea/labre-core/store").ExtensionType;
2
+ //# sourceMappingURL=node-config.d.ts.map
@@ -0,0 +1,162 @@
1
+ import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
2
+ import { packColor, } from '@formicoidea/labre-core/components/color-picker';
3
+ import { shapeToolbarConfig } from '@formicoidea/labre-core/gfx/shape';
4
+ import { DefaultTheme, isTransparent, LineWidth, resolveColor, ShapeElementModel, StrokeStyle, } from '@formicoidea/labre-core/model';
5
+ import { ToolbarModuleExtension, } from '@formicoidea/labre-core/shared/services';
6
+ import { getMostCommonValue } from '@formicoidea/labre-core/shared/utils';
7
+ import { BlockFlavourIdentifier } from '@formicoidea/labre-core/std';
8
+ import { html } from 'lit';
9
+ /**
10
+ * The typical EDGY palette, surfaced as ready-made swatches in the EDGY node
11
+ * color picker (facet + intersection colours, saturated then pastel), followed
12
+ * by the default editor palette.
13
+ */
14
+ const EDGY_PALETTES = [
15
+ { key: 'Identity', value: '#00ea4e' },
16
+ { key: 'Architecture', value: '#034cee' },
17
+ { key: 'Experience', value: '#ff0056' },
18
+ { key: 'Organisation', value: '#00caf4' },
19
+ { key: 'Brand', value: '#ffa500' },
20
+ { key: 'Product', value: '#cf00ff' },
21
+ { key: 'Identity light', value: '#80ffb7' },
22
+ { key: 'Architecture light', value: '#a6c0ff' },
23
+ { key: 'Experience light', value: '#ff99bd' },
24
+ { key: 'Organisation light', value: '#80eaff' },
25
+ { key: 'Brand light', value: '#ffd580' },
26
+ { key: 'Product light', value: '#e599ff' },
27
+ ];
28
+ /**
29
+ * From the default editor palette we keep ONLY the neutrals (greys, white,
30
+ * black, transparent) — the historical colours are dropped in favour of the
31
+ * EDGY swatches above.
32
+ */
33
+ const NEUTRAL_KEY = /grey|gray|white|black|transparent/i;
34
+ const EDGY_PALETTE_LIST = [
35
+ ...EDGY_PALETTES,
36
+ ...DefaultTheme.Palettes.filter(p => NEUTRAL_KEY.test(p.key)),
37
+ ];
38
+ // Mirror of the shape color action's text-color rule.
39
+ function getTextColor(fillColor) {
40
+ if (fillColor === DefaultTheme.black)
41
+ return DefaultTheme.white;
42
+ if (fillColor === DefaultTheme.white)
43
+ return DefaultTheme.black;
44
+ return DefaultTheme.shapeTextColor;
45
+ }
46
+ /**
47
+ * EDGY fill / stroke colour picker — identical to the shape one but seeded with
48
+ * the EDGY palette swatches (`.palettes`).
49
+ */
50
+ const edgyColorAction = {
51
+ id: 'e.color',
52
+ when(ctx) {
53
+ return ctx.getSurfaceModelsByType(ShapeElementModel).length > 0;
54
+ },
55
+ content(ctx) {
56
+ const models = ctx.getSurfaceModelsByType(ShapeElementModel);
57
+ if (!models.length)
58
+ return null;
59
+ const enableCustomColor = ctx.features.getFlag('enable_color_picker');
60
+ const theme = ctx.theme.edgeless$.value;
61
+ const firstModel = models[0];
62
+ const originalFillColor = firstModel.fillColor;
63
+ const originalStrokeColor = firstModel.strokeColor;
64
+ const mapped = models.map(({ filled, fillColor, strokeColor, strokeWidth, strokeStyle }) => ({
65
+ fillColor: filled
66
+ ? resolveColor(fillColor, theme)
67
+ : DefaultTheme.transparent,
68
+ strokeColor: resolveColor(strokeColor, theme),
69
+ strokeWidth,
70
+ strokeStyle,
71
+ }));
72
+ const fillColor = getMostCommonValue(mapped, 'fillColor') ??
73
+ resolveColor(DefaultTheme.shapeFillColor, theme);
74
+ const strokeColor = getMostCommonValue(mapped, 'strokeColor') ??
75
+ resolveColor(DefaultTheme.shapeStrokeColor, theme);
76
+ const strokeWidth = getMostCommonValue(mapped, 'strokeWidth') ?? LineWidth.Four;
77
+ const strokeStyle = getMostCommonValue(mapped, 'strokeStyle') ?? StrokeStyle.Solid;
78
+ const pickColorWrapper = (field, pickCallback) => (e) => {
79
+ e.stopPropagation();
80
+ switch (e.detail.type) {
81
+ case 'pick':
82
+ pickCallback(e.detail.detail);
83
+ break;
84
+ case 'start':
85
+ ctx.store.captureSync();
86
+ models.forEach(model => model.stash(field));
87
+ break;
88
+ case 'end':
89
+ ctx.store.transact(() => {
90
+ models.forEach(model => model.pop(field));
91
+ });
92
+ }
93
+ };
94
+ const onPickFillColor = pickColorWrapper('fillColor', palette => {
95
+ const value = palette.value;
96
+ const filled = isTransparent(value);
97
+ const props = packColor('fillColor', value);
98
+ const crud = ctx.std.get(EdgelessCRUDIdentifier);
99
+ models.forEach(model => {
100
+ if (filled && !model.filled) {
101
+ const color = getTextColor(value);
102
+ Object.assign(props, { filled, color });
103
+ }
104
+ crud.updateElement(model.id, props);
105
+ });
106
+ });
107
+ const onPickStrokeColor = pickColorWrapper('strokeColor', palette => {
108
+ const props = packColor('strokeColor', palette.value);
109
+ const crud = ctx.std.get(EdgelessCRUDIdentifier);
110
+ models.forEach(model => crud.updateElement(model.id, props));
111
+ });
112
+ const onPickStrokeStyle = (e) => {
113
+ e.stopPropagation();
114
+ const { type, value } = e.detail;
115
+ const crud = ctx.std.get(EdgelessCRUDIdentifier);
116
+ const props = type === 'size'
117
+ ? { strokeWidth: value }
118
+ : { strokeStyle: value };
119
+ for (const model of models) {
120
+ crud.updateElement(model.id, props);
121
+ }
122
+ };
123
+ return html `
124
+ <edgeless-shape-color-picker
125
+ @pickFillColor=${onPickFillColor}
126
+ @pickStrokeColor=${onPickStrokeColor}
127
+ @pickStrokeStyle=${onPickStrokeStyle}
128
+ .palettes=${EDGY_PALETTE_LIST}
129
+ .payload=${{
130
+ fillColor,
131
+ strokeColor,
132
+ strokeWidth,
133
+ strokeStyle,
134
+ originalFillColor,
135
+ originalStrokeColor,
136
+ theme,
137
+ enableCustomColor,
138
+ }}
139
+ >
140
+ </edgeless-shape-color-picker>
141
+ `;
142
+ },
143
+ };
144
+ /**
145
+ * EDGY nodes are {@link ShapeElementModel} subclasses, so the shape toolbar's
146
+ * actions operate on them directly. We reuse the line-style + text actions, add
147
+ * the EDGY-seeded color picker, and drop the actions that don't fit an EDGY base
148
+ * shape (switch shape type, edit polygon vertices).
149
+ */
150
+ const KEEP_FROM_SHAPE = (id) => id === 'd.style' || id === 'f.text' || id.startsWith('g.text-');
151
+ const edgyNodeToolbarConfig = {
152
+ actions: [
153
+ ...shapeToolbarConfig.actions.filter(action => KEEP_FROM_SHAPE(action.id)),
154
+ edgyColorAction,
155
+ ],
156
+ when: shapeToolbarConfig.when,
157
+ };
158
+ export const edgyNodeToolbarExtension = ToolbarModuleExtension({
159
+ id: BlockFlavourIdentifier('affine:surface:edgyNode'),
160
+ config: edgyNodeToolbarConfig,
161
+ });
162
+ //# sourceMappingURL=node-config.js.map
@@ -0,0 +1,2 @@
1
+ export declare const edgySeniorTool: import("@formicoidea/labre-core/store").ExtensionType;
2
+ //# sourceMappingURL=senior-tool.d.ts.map
@@ -1,11 +1,11 @@
1
1
  import { SeniorToolExtension } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
2
2
  import { html } from 'lit';
3
-
4
3
  export const edgySeniorTool = SeniorToolExtension('edgy', ({ block }) => {
5
- return {
6
- name: 'EDGY',
7
- content: html`<edgeless-edgy-senior-button
4
+ return {
5
+ name: 'EDGY',
6
+ content: html `<edgeless-edgy-senior-button
8
7
  .edgeless=${block}
9
8
  ></edgeless-edgy-senior-button>`,
10
- };
9
+ };
11
10
  });
11
+ //# sourceMappingURL=senior-tool.js.map
package/dist/view.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { type ViewExtensionContext, ViewExtensionProvider } from '@formicoidea/labre-core/ext-loader';
2
+ export declare class EdgyViewExtension extends ViewExtensionProvider {
3
+ name: string;
4
+ effect(): void;
5
+ setup(context: ViewExtensionContext): void;
6
+ }
7
+ //# sourceMappingURL=view.d.ts.map
package/dist/view.js ADDED
@@ -0,0 +1,36 @@
1
+ import { ViewExtensionProvider, } from '@formicoidea/labre-core/ext-loader';
2
+ import { extendTemplateCategory } from '@formicoidea/labre-core/gfx/template';
3
+ import { effects } from './effects';
4
+ import { edgyTemplateCategory } from './templates';
5
+ import { EdgyFacetsRendererExtension } from './element-renderer';
6
+ import { EdgyInteraction, EdgyView } from './element-view';
7
+ import { EdgyNodeRendererExtension } from './node/node-renderer';
8
+ import { EdgyNodeView } from './node/node-view';
9
+ import { edgyToolbarExtension } from './toolbar/config';
10
+ import { edgyNodeToolbarExtension } from './toolbar/node-config';
11
+ import { edgySeniorTool } from './toolbar/senior-tool';
12
+ export class EdgyViewExtension extends ViewExtensionProvider {
13
+ constructor() {
14
+ super(...arguments);
15
+ this.name = 'affine-edgy-gfx';
16
+ }
17
+ effect() {
18
+ super.effect();
19
+ effects();
20
+ extendTemplateCategory(edgyTemplateCategory);
21
+ }
22
+ setup(context) {
23
+ super.setup(context);
24
+ context.register(EdgyView);
25
+ context.register(EdgyFacetsRendererExtension);
26
+ context.register(EdgyNodeView);
27
+ context.register(EdgyNodeRendererExtension);
28
+ if (this.isEdgeless(context.scope)) {
29
+ context.register(EdgyInteraction);
30
+ context.register(edgySeniorTool);
31
+ context.register(edgyToolbarExtension);
32
+ context.register(edgyNodeToolbarExtension);
33
+ }
34
+ }
35
+ }
36
+ //# sourceMappingURL=view.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@formicoidea/labre-framework-edgy",
3
3
  "description": "Labre edgy framework for @formicoidea/labre-core.",
4
- "version": "0.23.0",
4
+ "version": "0.23.1",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "author": "lajola",
@@ -10,15 +10,24 @@
10
10
  ],
11
11
  "license": "MPL-2.0",
12
12
  "exports": {
13
- ".": "./src/index.ts",
14
- "./view": "./src/view.ts",
15
- "./descriptor": "./src/descriptor.ts"
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./view": {
18
+ "types": "./dist/view.d.ts",
19
+ "import": "./dist/view.js"
20
+ },
21
+ "./descriptor": {
22
+ "types": "./dist/descriptor.d.ts",
23
+ "import": "./dist/descriptor.js"
24
+ }
16
25
  },
17
26
  "files": [
18
- "src"
27
+ "dist"
19
28
  ],
20
29
  "dependencies": {
21
- "@formicoidea/labre-core": "0.23.0",
30
+ "@formicoidea/labre-core": "0.23.1",
22
31
  "lit": "^3.2.0"
23
32
  }
24
33
  }