@knotx/plugins-selection 0.3.0 → 0.3.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/dist/index.cjs CHANGED
@@ -10,9 +10,7 @@ const SelectionArea__default = /*#__PURE__*/_interopDefaultCompat(SelectionArea)
10
10
 
11
11
  var __create = Object.create;
12
12
  var __defProp = Object.defineProperty;
13
- var __defProps = Object.defineProperties;
14
13
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
15
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
16
14
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
17
15
  var __hasOwnProp = Object.prototype.hasOwnProperty;
18
16
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
@@ -32,7 +30,6 @@ var __spreadValues = (a, b) => {
32
30
  }
33
31
  return a;
34
32
  };
35
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
36
33
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
37
34
  var __decoratorStart = (base) => {
38
35
  var _a2;
@@ -76,123 +73,185 @@ var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use
76
73
  var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
77
74
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
78
75
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
79
- var _destroy_dec, _clearSelection_dec, _getSelectedNodeIds_dec, _init_dec, _canInteract_dec, _endInteraction_dec, _startInteraction_dec, _selectStatus_dec, _selectedNodeIds_dec, _a, _init;
80
- class Selection extends (_a = core.BasePlugin, _selectedNodeIds_dec = [decorators.register("selectedNodeIds")], _selectStatus_dec = [decorators.register("selectStatus")], _startInteraction_dec = [decorators.inject.startInteraction()], _endInteraction_dec = [decorators.inject.endInteraction()], _canInteract_dec = [decorators.inject.canInteract()], _init_dec = [decorators.OnInit], _getSelectedNodeIds_dec = [decorators.tool("Get selected node ids", {})], _clearSelection_dec = [decorators.tool("Clear selection", {})], _destroy_dec = [decorators.OnDestroy], _a) {
76
+ var _destroy_dec, _clearSelection_dec, _getSelected_dec, _init_dec, _interaction_dec, _registerRules_dec, _selected_dec, _a, _init;
77
+ class Selection extends (_a = core.BasePlugin, _selected_dec = [decorators.register("selected")], _registerRules_dec = [decorators.register("registerRules")], _interaction_dec = [decorators.inject.interaction()], _init_dec = [decorators.OnInit], _getSelected_dec = [decorators.tool("Get selected items", {})], _clearSelection_dec = [decorators.tool("Clear selection", {})], _destroy_dec = [decorators.OnDestroy], _a) {
81
78
  constructor() {
82
79
  super(...arguments);
83
80
  __runInitializers(_init, 5, this);
84
81
  __publicField(this, "name", "selection");
85
- __publicField(this, "config");
86
82
  __publicField(this, "selection");
87
- __publicField(this, "selectedNodeIds", __runInitializers(_init, 8, this, [])), __runInitializers(_init, 11, this);
88
- __publicField(this, "selectStatus", __runInitializers(_init, 12, this, "stop")), __runInitializers(_init, 15, this);
89
- __publicField(this, "startInteraction", __runInitializers(_init, 16, this)), __runInitializers(_init, 19, this);
90
- __publicField(this, "endInteraction", __runInitializers(_init, 20, this)), __runInitializers(_init, 23, this);
91
- __publicField(this, "canInteract", __runInitializers(_init, 24, this)), __runInitializers(_init, 27, this);
92
- }
93
- init(config) {
94
- this.config = __spreadValues({
95
- boundaries: [`.${core.bem("canvas", "wrapper")}`]
96
- }, config);
97
- this.createSelection();
98
- }
99
- createSelection() {
100
- const nodeWrapperClassName = core.bem("node", "wrapper");
101
- const nodeClassName = core.addBemModifier(nodeWrapperClassName, "selected");
102
- const selectionAreaClass = core.bem("selection-area");
103
- const selectables = [`.${nodeWrapperClassName}`];
104
- const endSelect = () => {
105
- this.endInteraction(this.pluginId, "select");
106
- this.selectStatus = "stop";
107
- };
108
- this.selection = new SelectionArea__default(__spreadProps(__spreadValues({}, this.config), { selectionAreaClass, selectables })).on("start", ({ store, event, selection }) => {
83
+ __publicField(this, "container", "body");
84
+ __publicField(this, "selectionAreaClassName");
85
+ __publicField(this, "ruleMap", /* @__PURE__ */ new Map());
86
+ __publicField(this, "enableDrag", true);
87
+ __publicField(this, "isDragging", false);
88
+ __publicField(this, "updateFrameId");
89
+ __publicField(this, "selected", __runInitializers(_init, 8, this, [])), __runInitializers(_init, 11, this);
90
+ __publicField(this, "registerRules", __runInitializers(_init, 12, this, (rules) => {
91
+ rules.forEach((rule) => {
92
+ this.ruleMap.set(rule.className, rule);
93
+ });
94
+ return () => {
95
+ rules.forEach((rule) => {
96
+ if (this.ruleMap.get(rule.className) === rule) {
97
+ this.ruleMap.delete(rule.className);
98
+ }
99
+ });
100
+ };
101
+ })), __runInitializers(_init, 15, this);
102
+ __publicField(this, "interaction", __runInitializers(_init, 16, this)), __runInitializers(_init, 19, this);
103
+ __publicField(this, "onBeforeStart", (_) => {
104
+ });
105
+ __publicField(this, "onBeforeDrag", () => {
106
+ this.isDragging = this.enableDrag;
107
+ return this.enableDrag && this.interaction.canInteract(core.InteractionPriority.MarqueeSelection);
108
+ });
109
+ __publicField(this, "onStart", ({ event, store }) => {
110
+ var _a2;
111
+ this.interaction.start(this.pluginId, "select", core.InteractionPriority.MarqueeSelection);
109
112
  if (!(event == null ? void 0 : event.ctrlKey) && !(event == null ? void 0 : event.metaKey) && !(event == null ? void 0 : event.shiftKey)) {
110
- store.stored.forEach((el) => {
111
- if (el.className.includes(nodeWrapperClassName)) {
112
- el.classList.remove(nodeClassName);
113
+ store.stored.forEach((element) => {
114
+ const rule = this.getElementRule(element);
115
+ if (rule == null ? void 0 : rule.activeClassName) {
116
+ element.classList.remove(rule.activeClassName);
113
117
  }
114
118
  });
115
- selection.clearSelection();
116
- this.selectedNodeIds = [];
119
+ (_a2 = this.selection) == null ? void 0 : _a2.clearSelection(true, true);
117
120
  }
118
- this.startInteraction(this.pluginId, "select", core.InteractionPriority.MarqueeSelection);
119
- this.selectStatus = "start";
120
- }).on("move", ({ store: { changed: { added, removed } }, selection, event }) => {
121
- if (event) {
122
- this.selectStatus = "moving";
123
- }
124
- if (!this.canInteract(this.pluginId, "select", core.InteractionPriority.MarqueeSelection)) {
125
- selection.cancel();
126
- endSelect();
127
- return;
128
- }
129
- const addedNodeIds = [];
130
- added.forEach((el) => {
131
- if (el.className.includes(nodeWrapperClassName)) {
132
- el.classList.add(nodeClassName);
133
- const nodeId = el.getAttribute("data-node-id");
134
- if (nodeId)
135
- addedNodeIds.push(nodeId);
121
+ });
122
+ __publicField(this, "onMove", ({ store: { stored, changed: { added, removed } }, selection }) => {
123
+ const getSelectedElements = () => {
124
+ return stored.reduce((dict, item) => {
125
+ const rule = this.getElementRule(item);
126
+ if (rule) {
127
+ dict[rule.type] = [...dict[rule.type] || [], { id: rule.idGetter(item), element: item }];
128
+ }
129
+ return dict;
130
+ }, {});
131
+ };
132
+ const context = {
133
+ isDragging: this.isDragging,
134
+ getSelectedElements,
135
+ select: (elements) => {
136
+ elements.forEach((element) => {
137
+ selection.select(element);
138
+ });
139
+ },
140
+ deselect: (elements) => {
141
+ elements.forEach((element) => {
142
+ selection.deselect(element);
143
+ });
144
+ }
145
+ };
146
+ removed.forEach((element) => {
147
+ const rule = this.getElementRule(element);
148
+ if (!rule) {
149
+ return;
150
+ }
151
+ if (rule.activeClassName) {
152
+ element.classList.remove(rule.activeClassName);
136
153
  }
137
154
  });
138
- const removedNodeIds = [];
139
- removed.forEach((el) => {
140
- if (el.className.includes(nodeWrapperClassName)) {
141
- el.classList.remove(nodeClassName);
142
- const nodeId = el.getAttribute("data-node-id");
143
- if (nodeId)
144
- removedNodeIds.push(nodeId);
155
+ added.forEach((element) => {
156
+ const rule = this.getElementRule(element);
157
+ if (!rule) {
158
+ return;
159
+ }
160
+ const type = rule.type;
161
+ const id = rule.idGetter(element);
162
+ for (const [, rule2] of this.ruleMap) {
163
+ if (rule2.onSelected) {
164
+ if (!rule2.onSelected(__spreadValues({ element, type, id }, context))) {
165
+ selection.deselect(element, true);
166
+ return;
167
+ }
168
+ }
169
+ }
170
+ if (rule.activeClassName) {
171
+ element.classList.add(rule.activeClassName);
145
172
  }
146
173
  });
147
- this.updateSelection(addedNodeIds, removedNodeIds);
148
- }).on("stop", ({ event }) => {
174
+ });
175
+ __publicField(this, "onStop", ({ event }) => {
176
+ this.interaction.end(this.pluginId, "select");
149
177
  if (event) {
150
- endSelect();
178
+ if (this.isDragging) {
179
+ this.isDragging = false;
180
+ }
151
181
  }
182
+ cancelAnimationFrame(this.updateFrameId);
183
+ this.updateFrameId = requestAnimationFrame(this.updateSelected);
184
+ });
185
+ __publicField(this, "updateSelected", () => {
186
+ var _a2;
187
+ const selected = [];
188
+ for (const element of ((_a2 = this.selection) == null ? void 0 : _a2.getSelection()) || []) {
189
+ const rule = this.getElementRule(element);
190
+ if (rule) {
191
+ selected.push({ type: rule.type, id: rule.idGetter(element) });
192
+ }
193
+ }
194
+ this.selected = selected;
152
195
  });
153
196
  }
154
- // 更新选中的节点和组
155
- updateSelection(addedNodeIds = [], removedNodeIds = []) {
156
- const currentNodeIds = this.selectedNodeIds;
157
- const newNodeIds = [
158
- ...currentNodeIds.filter((id) => !removedNodeIds.includes(id)),
159
- ...addedNodeIds.filter((id) => !currentNodeIds.includes(id))
160
- ];
161
- this.selectedNodeIds = newNodeIds;
162
- }
163
- getSelectedNodeIds() {
164
- return this.selectedNodeIds;
197
+ init(config = {}) {
198
+ this.container = config.container || core.bemSelector("canvas", "wrapper");
199
+ this.selectionAreaClassName = config.selectionAreaClassName || core.bem("selection-area");
200
+ if (!config.disableDefaultRules) {
201
+ const nodeRule = {
202
+ type: "node",
203
+ className: core.bem("node", "wrapper"),
204
+ activeClassName: core.bem("node", "wrapper", "selected"),
205
+ idGetter: (element) => element.getAttribute("data-node-id") || ""
206
+ };
207
+ this.ruleMap.set(nodeRule.className, nodeRule);
208
+ }
209
+ for (const rule of config.rules || []) {
210
+ this.ruleMap.set(rule.className, rule);
211
+ }
212
+ if (config.enableDrag !== void 0) {
213
+ this.enableDrag = config.enableDrag;
214
+ }
215
+ queueMicrotask(() => this.createSelection());
165
216
  }
166
- clearSelection() {
167
- this.selectedNodeIds = [];
217
+ createSelection() {
218
+ const container = this.container;
219
+ this.selection = new SelectionArea__default({
220
+ // container,
221
+ boundaries: [container],
222
+ selectionAreaClass: this.selectionAreaClassName,
223
+ selectables: Array.from(this.ruleMap.keys()).map((className) => `.${className}`)
224
+ }).on("beforedrag", this.onBeforeDrag).on("beforestart", this.onBeforeStart).on("start", this.onStart).on("move", this.onMove).on("stop", this.onStop);
168
225
  }
169
- // 选择节点
170
- selectNodes(nodeIds, clearPrevious = true) {
171
- if (clearPrevious) {
172
- this.selectedNodeIds = nodeIds;
173
- } else {
174
- const currentNodeIds = this.selectedNodeIds;
175
- this.selectedNodeIds = [
176
- ...currentNodeIds,
177
- ...nodeIds.filter((id) => !currentNodeIds.includes(id))
178
- ];
226
+ getElementRule(element) {
227
+ for (const [className, rule] of this.ruleMap) {
228
+ if (element.classList.contains(className)) {
229
+ return rule;
230
+ }
179
231
  }
232
+ return void 0;
233
+ }
234
+ getSelected() {
235
+ return this.selected;
236
+ }
237
+ clearSelection() {
238
+ var _a2;
239
+ (_a2 = this.selection) == null ? void 0 : _a2.clearSelection(true, false);
180
240
  }
181
241
  destroy() {
182
242
  var _a2;
183
243
  (_a2 = this.selection) == null ? void 0 : _a2.destroy();
244
+ this.ruleMap.clear();
184
245
  }
185
246
  }
186
247
  _init = __decoratorStart(_a);
187
248
  __decorateElement(_init, 1, "init", _init_dec, Selection);
188
- __decorateElement(_init, 1, "getSelectedNodeIds", _getSelectedNodeIds_dec, Selection);
249
+ __decorateElement(_init, 1, "getSelected", _getSelected_dec, Selection);
189
250
  __decorateElement(_init, 1, "clearSelection", _clearSelection_dec, Selection);
190
251
  __decorateElement(_init, 1, "destroy", _destroy_dec, Selection);
191
- __decorateElement(_init, 5, "selectedNodeIds", _selectedNodeIds_dec, Selection);
192
- __decorateElement(_init, 5, "selectStatus", _selectStatus_dec, Selection);
193
- __decorateElement(_init, 5, "startInteraction", _startInteraction_dec, Selection);
194
- __decorateElement(_init, 5, "endInteraction", _endInteraction_dec, Selection);
195
- __decorateElement(_init, 5, "canInteract", _canInteract_dec, Selection);
252
+ __decorateElement(_init, 5, "selected", _selected_dec, Selection);
253
+ __decorateElement(_init, 5, "registerRules", _registerRules_dec, Selection);
254
+ __decorateElement(_init, 5, "interaction", _interaction_dec, Selection);
196
255
  __decoratorMetadata(_init, Selection);
197
256
 
198
257
  exports.Selection = Selection;
package/dist/index.d.cts CHANGED
@@ -1,38 +1,88 @@
1
- import { PartialSelectionOptions } from '@viselect/vanilla';
2
1
  import { BasePlugin } from '@knotx/core';
3
2
 
3
+ interface SelectionSelectedItem {
4
+ type: string;
5
+ id: string;
6
+ }
4
7
  declare module '@knotx/core' {
5
8
  interface PluginData {
6
9
  selection: {
7
- selectedNodeIds: string[];
8
- selectStatus: 'start' | 'moving' | 'stop';
10
+ selected: SelectionSelectedItem[];
11
+ registerRules: (rules: SelectionRule[]) => () => void;
9
12
  };
10
13
  }
11
14
  interface PluginTools {
12
15
  selection: {
13
- getSelectedNodeIds: () => string[];
16
+ getSelected: () => SelectionSelectedItem[];
14
17
  clearSelection: () => void;
15
18
  };
16
19
  }
17
20
  }
18
- interface SelectionConfig extends PartialSelectionOptions {
21
+ interface SelectionRule {
22
+ /**
23
+ * resource type
24
+ */
25
+ type: string;
26
+ className: string;
27
+ idGetter: (element: Element) => string;
28
+ /**
29
+ * default: null
30
+ */
31
+ activeClassName?: string;
32
+ onSelected?: (context: {
33
+ element: Element;
34
+ type: string;
35
+ id: string;
36
+ isDragging: boolean;
37
+ getSelectedElements: () => Record<string, {
38
+ id: string;
39
+ element: Element;
40
+ }[]>;
41
+ select: (elements: Element[]) => void;
42
+ deselect: (elements: Element[]) => void;
43
+ }) => boolean;
44
+ }
45
+ interface SelectionConfig {
46
+ selectionAreaClassName?: string;
47
+ /**
48
+ * default: true
49
+ */
50
+ enableDrag?: boolean;
51
+ rules?: SelectionRule[];
52
+ /**
53
+ * default:
54
+ * [{type: 'node', className: bem('node', 'wrapper'), activeClassName: bem('node', 'wrapper', 'selected') }]
55
+ */
56
+ disableDefaultRules?: boolean;
57
+ /**
58
+ * default: bemSelector('canvas', 'wrapper')
59
+ */
60
+ container?: string;
19
61
  }
20
62
  declare class Selection extends BasePlugin<'selection', SelectionConfig> {
21
63
  name: "selection";
22
- private config;
23
64
  private selection;
24
- selectedNodeIds: string[];
25
- selectStatus: 'start' | 'moving' | 'stop';
26
- private startInteraction;
27
- private endInteraction;
28
- private canInteract;
29
- init(config: SelectionConfig): void;
65
+ private container;
66
+ private selectionAreaClassName;
67
+ private ruleMap;
68
+ private enableDrag;
69
+ private isDragging;
70
+ private updateFrameId;
71
+ selected: SelectionSelectedItem[];
72
+ registerRules: (rules: SelectionRule[]) => () => void;
73
+ private interaction;
74
+ init(config?: SelectionConfig): void;
30
75
  private createSelection;
31
- private updateSelection;
32
- getSelectedNodeIds(): string[];
76
+ private onBeforeStart;
77
+ private onBeforeDrag;
78
+ private onStart;
79
+ private onMove;
80
+ private onStop;
81
+ private getElementRule;
82
+ private updateSelected;
83
+ getSelected(): SelectionSelectedItem[];
33
84
  clearSelection(): void;
34
- selectNodes(nodeIds: string[], clearPrevious?: boolean): void;
35
85
  destroy(): void;
36
86
  }
37
87
 
38
- export { Selection, type SelectionConfig };
88
+ export { Selection, type SelectionConfig, type SelectionRule, type SelectionSelectedItem };
package/dist/index.d.mts CHANGED
@@ -1,38 +1,88 @@
1
- import { PartialSelectionOptions } from '@viselect/vanilla';
2
1
  import { BasePlugin } from '@knotx/core';
3
2
 
3
+ interface SelectionSelectedItem {
4
+ type: string;
5
+ id: string;
6
+ }
4
7
  declare module '@knotx/core' {
5
8
  interface PluginData {
6
9
  selection: {
7
- selectedNodeIds: string[];
8
- selectStatus: 'start' | 'moving' | 'stop';
10
+ selected: SelectionSelectedItem[];
11
+ registerRules: (rules: SelectionRule[]) => () => void;
9
12
  };
10
13
  }
11
14
  interface PluginTools {
12
15
  selection: {
13
- getSelectedNodeIds: () => string[];
16
+ getSelected: () => SelectionSelectedItem[];
14
17
  clearSelection: () => void;
15
18
  };
16
19
  }
17
20
  }
18
- interface SelectionConfig extends PartialSelectionOptions {
21
+ interface SelectionRule {
22
+ /**
23
+ * resource type
24
+ */
25
+ type: string;
26
+ className: string;
27
+ idGetter: (element: Element) => string;
28
+ /**
29
+ * default: null
30
+ */
31
+ activeClassName?: string;
32
+ onSelected?: (context: {
33
+ element: Element;
34
+ type: string;
35
+ id: string;
36
+ isDragging: boolean;
37
+ getSelectedElements: () => Record<string, {
38
+ id: string;
39
+ element: Element;
40
+ }[]>;
41
+ select: (elements: Element[]) => void;
42
+ deselect: (elements: Element[]) => void;
43
+ }) => boolean;
44
+ }
45
+ interface SelectionConfig {
46
+ selectionAreaClassName?: string;
47
+ /**
48
+ * default: true
49
+ */
50
+ enableDrag?: boolean;
51
+ rules?: SelectionRule[];
52
+ /**
53
+ * default:
54
+ * [{type: 'node', className: bem('node', 'wrapper'), activeClassName: bem('node', 'wrapper', 'selected') }]
55
+ */
56
+ disableDefaultRules?: boolean;
57
+ /**
58
+ * default: bemSelector('canvas', 'wrapper')
59
+ */
60
+ container?: string;
19
61
  }
20
62
  declare class Selection extends BasePlugin<'selection', SelectionConfig> {
21
63
  name: "selection";
22
- private config;
23
64
  private selection;
24
- selectedNodeIds: string[];
25
- selectStatus: 'start' | 'moving' | 'stop';
26
- private startInteraction;
27
- private endInteraction;
28
- private canInteract;
29
- init(config: SelectionConfig): void;
65
+ private container;
66
+ private selectionAreaClassName;
67
+ private ruleMap;
68
+ private enableDrag;
69
+ private isDragging;
70
+ private updateFrameId;
71
+ selected: SelectionSelectedItem[];
72
+ registerRules: (rules: SelectionRule[]) => () => void;
73
+ private interaction;
74
+ init(config?: SelectionConfig): void;
30
75
  private createSelection;
31
- private updateSelection;
32
- getSelectedNodeIds(): string[];
76
+ private onBeforeStart;
77
+ private onBeforeDrag;
78
+ private onStart;
79
+ private onMove;
80
+ private onStop;
81
+ private getElementRule;
82
+ private updateSelected;
83
+ getSelected(): SelectionSelectedItem[];
33
84
  clearSelection(): void;
34
- selectNodes(nodeIds: string[], clearPrevious?: boolean): void;
35
85
  destroy(): void;
36
86
  }
37
87
 
38
- export { Selection, type SelectionConfig };
88
+ export { Selection, type SelectionConfig, type SelectionRule, type SelectionSelectedItem };
package/dist/index.d.ts CHANGED
@@ -1,38 +1,88 @@
1
- import { PartialSelectionOptions } from '@viselect/vanilla';
2
1
  import { BasePlugin } from '@knotx/core';
3
2
 
3
+ interface SelectionSelectedItem {
4
+ type: string;
5
+ id: string;
6
+ }
4
7
  declare module '@knotx/core' {
5
8
  interface PluginData {
6
9
  selection: {
7
- selectedNodeIds: string[];
8
- selectStatus: 'start' | 'moving' | 'stop';
10
+ selected: SelectionSelectedItem[];
11
+ registerRules: (rules: SelectionRule[]) => () => void;
9
12
  };
10
13
  }
11
14
  interface PluginTools {
12
15
  selection: {
13
- getSelectedNodeIds: () => string[];
16
+ getSelected: () => SelectionSelectedItem[];
14
17
  clearSelection: () => void;
15
18
  };
16
19
  }
17
20
  }
18
- interface SelectionConfig extends PartialSelectionOptions {
21
+ interface SelectionRule {
22
+ /**
23
+ * resource type
24
+ */
25
+ type: string;
26
+ className: string;
27
+ idGetter: (element: Element) => string;
28
+ /**
29
+ * default: null
30
+ */
31
+ activeClassName?: string;
32
+ onSelected?: (context: {
33
+ element: Element;
34
+ type: string;
35
+ id: string;
36
+ isDragging: boolean;
37
+ getSelectedElements: () => Record<string, {
38
+ id: string;
39
+ element: Element;
40
+ }[]>;
41
+ select: (elements: Element[]) => void;
42
+ deselect: (elements: Element[]) => void;
43
+ }) => boolean;
44
+ }
45
+ interface SelectionConfig {
46
+ selectionAreaClassName?: string;
47
+ /**
48
+ * default: true
49
+ */
50
+ enableDrag?: boolean;
51
+ rules?: SelectionRule[];
52
+ /**
53
+ * default:
54
+ * [{type: 'node', className: bem('node', 'wrapper'), activeClassName: bem('node', 'wrapper', 'selected') }]
55
+ */
56
+ disableDefaultRules?: boolean;
57
+ /**
58
+ * default: bemSelector('canvas', 'wrapper')
59
+ */
60
+ container?: string;
19
61
  }
20
62
  declare class Selection extends BasePlugin<'selection', SelectionConfig> {
21
63
  name: "selection";
22
- private config;
23
64
  private selection;
24
- selectedNodeIds: string[];
25
- selectStatus: 'start' | 'moving' | 'stop';
26
- private startInteraction;
27
- private endInteraction;
28
- private canInteract;
29
- init(config: SelectionConfig): void;
65
+ private container;
66
+ private selectionAreaClassName;
67
+ private ruleMap;
68
+ private enableDrag;
69
+ private isDragging;
70
+ private updateFrameId;
71
+ selected: SelectionSelectedItem[];
72
+ registerRules: (rules: SelectionRule[]) => () => void;
73
+ private interaction;
74
+ init(config?: SelectionConfig): void;
30
75
  private createSelection;
31
- private updateSelection;
32
- getSelectedNodeIds(): string[];
76
+ private onBeforeStart;
77
+ private onBeforeDrag;
78
+ private onStart;
79
+ private onMove;
80
+ private onStop;
81
+ private getElementRule;
82
+ private updateSelected;
83
+ getSelected(): SelectionSelectedItem[];
33
84
  clearSelection(): void;
34
- selectNodes(nodeIds: string[], clearPrevious?: boolean): void;
35
85
  destroy(): void;
36
86
  }
37
87
 
38
- export { Selection, type SelectionConfig };
88
+ export { Selection, type SelectionConfig, type SelectionRule, type SelectionSelectedItem };
package/dist/index.js CHANGED
@@ -1,12 +1,10 @@
1
- import { bem, addBemModifier, InteractionPriority, BasePlugin } from '@knotx/core';
1
+ import { bemSelector, bem, BasePlugin, InteractionPriority } from '@knotx/core';
2
2
  import { register, inject, tool, OnInit, OnDestroy } from '@knotx/decorators';
3
3
  import SelectionArea from '@viselect/vanilla';
4
4
 
5
5
  var __create = Object.create;
6
6
  var __defProp = Object.defineProperty;
7
- var __defProps = Object.defineProperties;
8
7
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
10
8
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
11
9
  var __hasOwnProp = Object.prototype.hasOwnProperty;
12
10
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
@@ -26,7 +24,6 @@ var __spreadValues = (a, b) => {
26
24
  }
27
25
  return a;
28
26
  };
29
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
30
27
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
31
28
  var __decoratorStart = (base) => {
32
29
  var _a2;
@@ -70,123 +67,185 @@ var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use
70
67
  var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
71
68
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
72
69
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
73
- var _destroy_dec, _clearSelection_dec, _getSelectedNodeIds_dec, _init_dec, _canInteract_dec, _endInteraction_dec, _startInteraction_dec, _selectStatus_dec, _selectedNodeIds_dec, _a, _init;
74
- class Selection extends (_a = BasePlugin, _selectedNodeIds_dec = [register("selectedNodeIds")], _selectStatus_dec = [register("selectStatus")], _startInteraction_dec = [inject.startInteraction()], _endInteraction_dec = [inject.endInteraction()], _canInteract_dec = [inject.canInteract()], _init_dec = [OnInit], _getSelectedNodeIds_dec = [tool("Get selected node ids", {})], _clearSelection_dec = [tool("Clear selection", {})], _destroy_dec = [OnDestroy], _a) {
70
+ var _destroy_dec, _clearSelection_dec, _getSelected_dec, _init_dec, _interaction_dec, _registerRules_dec, _selected_dec, _a, _init;
71
+ class Selection extends (_a = BasePlugin, _selected_dec = [register("selected")], _registerRules_dec = [register("registerRules")], _interaction_dec = [inject.interaction()], _init_dec = [OnInit], _getSelected_dec = [tool("Get selected items", {})], _clearSelection_dec = [tool("Clear selection", {})], _destroy_dec = [OnDestroy], _a) {
75
72
  constructor() {
76
73
  super(...arguments);
77
74
  __runInitializers(_init, 5, this);
78
75
  __publicField(this, "name", "selection");
79
- __publicField(this, "config");
80
76
  __publicField(this, "selection");
81
- __publicField(this, "selectedNodeIds", __runInitializers(_init, 8, this, [])), __runInitializers(_init, 11, this);
82
- __publicField(this, "selectStatus", __runInitializers(_init, 12, this, "stop")), __runInitializers(_init, 15, this);
83
- __publicField(this, "startInteraction", __runInitializers(_init, 16, this)), __runInitializers(_init, 19, this);
84
- __publicField(this, "endInteraction", __runInitializers(_init, 20, this)), __runInitializers(_init, 23, this);
85
- __publicField(this, "canInteract", __runInitializers(_init, 24, this)), __runInitializers(_init, 27, this);
86
- }
87
- init(config) {
88
- this.config = __spreadValues({
89
- boundaries: [`.${bem("canvas", "wrapper")}`]
90
- }, config);
91
- this.createSelection();
92
- }
93
- createSelection() {
94
- const nodeWrapperClassName = bem("node", "wrapper");
95
- const nodeClassName = addBemModifier(nodeWrapperClassName, "selected");
96
- const selectionAreaClass = bem("selection-area");
97
- const selectables = [`.${nodeWrapperClassName}`];
98
- const endSelect = () => {
99
- this.endInteraction(this.pluginId, "select");
100
- this.selectStatus = "stop";
101
- };
102
- this.selection = new SelectionArea(__spreadProps(__spreadValues({}, this.config), { selectionAreaClass, selectables })).on("start", ({ store, event, selection }) => {
77
+ __publicField(this, "container", "body");
78
+ __publicField(this, "selectionAreaClassName");
79
+ __publicField(this, "ruleMap", /* @__PURE__ */ new Map());
80
+ __publicField(this, "enableDrag", true);
81
+ __publicField(this, "isDragging", false);
82
+ __publicField(this, "updateFrameId");
83
+ __publicField(this, "selected", __runInitializers(_init, 8, this, [])), __runInitializers(_init, 11, this);
84
+ __publicField(this, "registerRules", __runInitializers(_init, 12, this, (rules) => {
85
+ rules.forEach((rule) => {
86
+ this.ruleMap.set(rule.className, rule);
87
+ });
88
+ return () => {
89
+ rules.forEach((rule) => {
90
+ if (this.ruleMap.get(rule.className) === rule) {
91
+ this.ruleMap.delete(rule.className);
92
+ }
93
+ });
94
+ };
95
+ })), __runInitializers(_init, 15, this);
96
+ __publicField(this, "interaction", __runInitializers(_init, 16, this)), __runInitializers(_init, 19, this);
97
+ __publicField(this, "onBeforeStart", (_) => {
98
+ });
99
+ __publicField(this, "onBeforeDrag", () => {
100
+ this.isDragging = this.enableDrag;
101
+ return this.enableDrag && this.interaction.canInteract(InteractionPriority.MarqueeSelection);
102
+ });
103
+ __publicField(this, "onStart", ({ event, store }) => {
104
+ var _a2;
105
+ this.interaction.start(this.pluginId, "select", InteractionPriority.MarqueeSelection);
103
106
  if (!(event == null ? void 0 : event.ctrlKey) && !(event == null ? void 0 : event.metaKey) && !(event == null ? void 0 : event.shiftKey)) {
104
- store.stored.forEach((el) => {
105
- if (el.className.includes(nodeWrapperClassName)) {
106
- el.classList.remove(nodeClassName);
107
+ store.stored.forEach((element) => {
108
+ const rule = this.getElementRule(element);
109
+ if (rule == null ? void 0 : rule.activeClassName) {
110
+ element.classList.remove(rule.activeClassName);
107
111
  }
108
112
  });
109
- selection.clearSelection();
110
- this.selectedNodeIds = [];
113
+ (_a2 = this.selection) == null ? void 0 : _a2.clearSelection(true, true);
111
114
  }
112
- this.startInteraction(this.pluginId, "select", InteractionPriority.MarqueeSelection);
113
- this.selectStatus = "start";
114
- }).on("move", ({ store: { changed: { added, removed } }, selection, event }) => {
115
- if (event) {
116
- this.selectStatus = "moving";
117
- }
118
- if (!this.canInteract(this.pluginId, "select", InteractionPriority.MarqueeSelection)) {
119
- selection.cancel();
120
- endSelect();
121
- return;
122
- }
123
- const addedNodeIds = [];
124
- added.forEach((el) => {
125
- if (el.className.includes(nodeWrapperClassName)) {
126
- el.classList.add(nodeClassName);
127
- const nodeId = el.getAttribute("data-node-id");
128
- if (nodeId)
129
- addedNodeIds.push(nodeId);
115
+ });
116
+ __publicField(this, "onMove", ({ store: { stored, changed: { added, removed } }, selection }) => {
117
+ const getSelectedElements = () => {
118
+ return stored.reduce((dict, item) => {
119
+ const rule = this.getElementRule(item);
120
+ if (rule) {
121
+ dict[rule.type] = [...dict[rule.type] || [], { id: rule.idGetter(item), element: item }];
122
+ }
123
+ return dict;
124
+ }, {});
125
+ };
126
+ const context = {
127
+ isDragging: this.isDragging,
128
+ getSelectedElements,
129
+ select: (elements) => {
130
+ elements.forEach((element) => {
131
+ selection.select(element);
132
+ });
133
+ },
134
+ deselect: (elements) => {
135
+ elements.forEach((element) => {
136
+ selection.deselect(element);
137
+ });
138
+ }
139
+ };
140
+ removed.forEach((element) => {
141
+ const rule = this.getElementRule(element);
142
+ if (!rule) {
143
+ return;
144
+ }
145
+ if (rule.activeClassName) {
146
+ element.classList.remove(rule.activeClassName);
130
147
  }
131
148
  });
132
- const removedNodeIds = [];
133
- removed.forEach((el) => {
134
- if (el.className.includes(nodeWrapperClassName)) {
135
- el.classList.remove(nodeClassName);
136
- const nodeId = el.getAttribute("data-node-id");
137
- if (nodeId)
138
- removedNodeIds.push(nodeId);
149
+ added.forEach((element) => {
150
+ const rule = this.getElementRule(element);
151
+ if (!rule) {
152
+ return;
153
+ }
154
+ const type = rule.type;
155
+ const id = rule.idGetter(element);
156
+ for (const [, rule2] of this.ruleMap) {
157
+ if (rule2.onSelected) {
158
+ if (!rule2.onSelected(__spreadValues({ element, type, id }, context))) {
159
+ selection.deselect(element, true);
160
+ return;
161
+ }
162
+ }
163
+ }
164
+ if (rule.activeClassName) {
165
+ element.classList.add(rule.activeClassName);
139
166
  }
140
167
  });
141
- this.updateSelection(addedNodeIds, removedNodeIds);
142
- }).on("stop", ({ event }) => {
168
+ });
169
+ __publicField(this, "onStop", ({ event }) => {
170
+ this.interaction.end(this.pluginId, "select");
143
171
  if (event) {
144
- endSelect();
172
+ if (this.isDragging) {
173
+ this.isDragging = false;
174
+ }
145
175
  }
176
+ cancelAnimationFrame(this.updateFrameId);
177
+ this.updateFrameId = requestAnimationFrame(this.updateSelected);
178
+ });
179
+ __publicField(this, "updateSelected", () => {
180
+ var _a2;
181
+ const selected = [];
182
+ for (const element of ((_a2 = this.selection) == null ? void 0 : _a2.getSelection()) || []) {
183
+ const rule = this.getElementRule(element);
184
+ if (rule) {
185
+ selected.push({ type: rule.type, id: rule.idGetter(element) });
186
+ }
187
+ }
188
+ this.selected = selected;
146
189
  });
147
190
  }
148
- // 更新选中的节点和组
149
- updateSelection(addedNodeIds = [], removedNodeIds = []) {
150
- const currentNodeIds = this.selectedNodeIds;
151
- const newNodeIds = [
152
- ...currentNodeIds.filter((id) => !removedNodeIds.includes(id)),
153
- ...addedNodeIds.filter((id) => !currentNodeIds.includes(id))
154
- ];
155
- this.selectedNodeIds = newNodeIds;
156
- }
157
- getSelectedNodeIds() {
158
- return this.selectedNodeIds;
191
+ init(config = {}) {
192
+ this.container = config.container || bemSelector("canvas", "wrapper");
193
+ this.selectionAreaClassName = config.selectionAreaClassName || bem("selection-area");
194
+ if (!config.disableDefaultRules) {
195
+ const nodeRule = {
196
+ type: "node",
197
+ className: bem("node", "wrapper"),
198
+ activeClassName: bem("node", "wrapper", "selected"),
199
+ idGetter: (element) => element.getAttribute("data-node-id") || ""
200
+ };
201
+ this.ruleMap.set(nodeRule.className, nodeRule);
202
+ }
203
+ for (const rule of config.rules || []) {
204
+ this.ruleMap.set(rule.className, rule);
205
+ }
206
+ if (config.enableDrag !== void 0) {
207
+ this.enableDrag = config.enableDrag;
208
+ }
209
+ queueMicrotask(() => this.createSelection());
159
210
  }
160
- clearSelection() {
161
- this.selectedNodeIds = [];
211
+ createSelection() {
212
+ const container = this.container;
213
+ this.selection = new SelectionArea({
214
+ // container,
215
+ boundaries: [container],
216
+ selectionAreaClass: this.selectionAreaClassName,
217
+ selectables: Array.from(this.ruleMap.keys()).map((className) => `.${className}`)
218
+ }).on("beforedrag", this.onBeforeDrag).on("beforestart", this.onBeforeStart).on("start", this.onStart).on("move", this.onMove).on("stop", this.onStop);
162
219
  }
163
- // 选择节点
164
- selectNodes(nodeIds, clearPrevious = true) {
165
- if (clearPrevious) {
166
- this.selectedNodeIds = nodeIds;
167
- } else {
168
- const currentNodeIds = this.selectedNodeIds;
169
- this.selectedNodeIds = [
170
- ...currentNodeIds,
171
- ...nodeIds.filter((id) => !currentNodeIds.includes(id))
172
- ];
220
+ getElementRule(element) {
221
+ for (const [className, rule] of this.ruleMap) {
222
+ if (element.classList.contains(className)) {
223
+ return rule;
224
+ }
173
225
  }
226
+ return void 0;
227
+ }
228
+ getSelected() {
229
+ return this.selected;
230
+ }
231
+ clearSelection() {
232
+ var _a2;
233
+ (_a2 = this.selection) == null ? void 0 : _a2.clearSelection(true, false);
174
234
  }
175
235
  destroy() {
176
236
  var _a2;
177
237
  (_a2 = this.selection) == null ? void 0 : _a2.destroy();
238
+ this.ruleMap.clear();
178
239
  }
179
240
  }
180
241
  _init = __decoratorStart(_a);
181
242
  __decorateElement(_init, 1, "init", _init_dec, Selection);
182
- __decorateElement(_init, 1, "getSelectedNodeIds", _getSelectedNodeIds_dec, Selection);
243
+ __decorateElement(_init, 1, "getSelected", _getSelected_dec, Selection);
183
244
  __decorateElement(_init, 1, "clearSelection", _clearSelection_dec, Selection);
184
245
  __decorateElement(_init, 1, "destroy", _destroy_dec, Selection);
185
- __decorateElement(_init, 5, "selectedNodeIds", _selectedNodeIds_dec, Selection);
186
- __decorateElement(_init, 5, "selectStatus", _selectStatus_dec, Selection);
187
- __decorateElement(_init, 5, "startInteraction", _startInteraction_dec, Selection);
188
- __decorateElement(_init, 5, "endInteraction", _endInteraction_dec, Selection);
189
- __decorateElement(_init, 5, "canInteract", _canInteract_dec, Selection);
246
+ __decorateElement(_init, 5, "selected", _selected_dec, Selection);
247
+ __decorateElement(_init, 5, "registerRules", _registerRules_dec, Selection);
248
+ __decorateElement(_init, 5, "interaction", _interaction_dec, Selection);
190
249
  __decoratorMetadata(_init, Selection);
191
250
 
192
251
  export { Selection };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knotx/plugins-selection",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Selection Plugin for Knotx",
5
5
  "author": "boenfu",
6
6
  "license": "MIT",
@@ -30,13 +30,13 @@
30
30
  "dependencies": {
31
31
  "@viselect/vanilla": "^3.9.0",
32
32
  "rxjs": "^7.8.1",
33
- "@knotx/core": "0.3.0",
34
- "@knotx/decorators": "0.3.0"
33
+ "@knotx/core": "0.3.2",
34
+ "@knotx/decorators": "0.3.2"
35
35
  },
36
36
  "devDependencies": {
37
- "@knotx/build-config": "0.3.0",
38
- "@knotx/eslint-config": "0.3.0",
39
- "@knotx/typescript-config": "0.3.0"
37
+ "@knotx/build-config": "0.3.2",
38
+ "@knotx/eslint-config": "0.3.2",
39
+ "@knotx/typescript-config": "0.3.2"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "unbuild",