@cocreate/position 1.16.0 → 2.0.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.
@@ -0,0 +1,64 @@
1
+ import observer from "@cocreate/observer";
2
+ import Position from "./draggable/engine.js";
3
+
4
+ const selector = "[draggable]";
5
+
6
+ export function initElement(element, options = {}) {
7
+ if (!element?.matches) return;
8
+ const draggable = options.draggable ?? element.matches(selector);
9
+ const previous = element.position;
10
+ const dragScope = options.dragScope || null;
11
+
12
+ if (!previous?._positionController) {
13
+ const position = new Position(element, element.parentElement, {
14
+ draggable,
15
+ dragScope,
16
+ delegated: options.delegated
17
+ });
18
+ if (previous?.place) position.place = previous.place;
19
+ if (previous?.unplace) position.unplace = previous.unplace;
20
+ position._positionController = true;
21
+ element.position = position;
22
+ } else {
23
+ previous.options.draggable = draggable;
24
+ previous.options.dragScope = dragScope;
25
+ if (draggable && !options.delegated && previous.options.delegated) {
26
+ previous.options.delegated = false;
27
+ previous._initEvent();
28
+ }
29
+ }
30
+ return element.position;
31
+ }
32
+
33
+ export function initElements(elements) {
34
+ for (const element of elements) initElement(element);
35
+ }
36
+
37
+ function refresh(node) {
38
+ if (node.nodeType !== 1) return;
39
+ if (node.matches(selector)) initElement(node);
40
+ initElements(node.querySelectorAll(selector));
41
+ }
42
+
43
+ export function init() {
44
+ initElements(document.querySelectorAll(selector));
45
+ }
46
+
47
+ observer.init({
48
+ name: "positionDraggableAddedNodes",
49
+ types: ["addedNodes"],
50
+ selector,
51
+ callback: mutation => refresh(mutation.target)
52
+ });
53
+
54
+ observer.init({
55
+ name: "positionDraggableAttributes",
56
+ types: ["attributes"],
57
+ attributeFilter: ["draggable"],
58
+ selector,
59
+ callback: mutation => initElement(mutation.target)
60
+ });
61
+
62
+ init();
63
+
64
+ export default { init, initElement, initElements };
@@ -0,0 +1,15 @@
1
+ import { getValueFromObject } from "@cocreate/utils";
2
+
3
+ export function resolveEventOperator(query, event) {
4
+ if (typeof query !== "string" || !query.startsWith("event."))
5
+ return { handled: false, value: undefined, selector: null };
6
+
7
+ const [, path, remaining = ""] = query.match(/^event\.([^\s]+)(?:\s+([\s\S]*))?$/) || [];
8
+ if (!path) return { handled: true, value: undefined, selector: null };
9
+
10
+ return {
11
+ handled: true,
12
+ value: getValueFromObject(event, path),
13
+ selector: remaining.trim() || "$this"
14
+ };
15
+ }
@@ -0,0 +1,34 @@
1
+ export function normalizeEvent(event) {
2
+ const detail = event?.detail && typeof event.detail === "object" ? event.detail : {};
3
+ const originalEvent = detail.originalEvent || event;
4
+ const target = detail.target ?? originalEvent?.target ?? event?.target ?? null;
5
+ const cursor = detail.cursor || {};
6
+ return {
7
+ type: originalEvent?.type || event?.type || "",
8
+ target,
9
+ relatedTarget: detail.relatedTarget ?? originalEvent?.relatedTarget ?? event?.relatedTarget ?? null,
10
+ document: detail.document ?? originalEvent?.view?.document ?? target?.ownerDocument ?? null,
11
+ clientX: cursor.x ?? originalEvent?.clientX ?? event?.clientX,
12
+ clientY: cursor.y ?? originalEvent?.clientY ?? event?.clientY,
13
+ which: originalEvent?.which ?? event?.which,
14
+ button: originalEvent?.button ?? event?.button,
15
+ touches: originalEvent?.touches ?? event?.touches,
16
+ changedTouches: originalEvent?.changedTouches ?? event?.changedTouches,
17
+ originalEvent,
18
+ detail: event?.detail,
19
+ preventDefault() {
20
+ originalEvent?.preventDefault?.();
21
+ if (event !== originalEvent) event?.preventDefault?.();
22
+ }
23
+ };
24
+ }
25
+
26
+ export function dispatch(element, name, detail = {}) {
27
+ const EventConstructor = element?.ownerDocument?.defaultView?.CustomEvent;
28
+ if (!EventConstructor) return;
29
+ const event = new EventConstructor(name, {
30
+ detail: { element, instance: element.position, ...detail }
31
+ });
32
+ element.dispatchEvent(event);
33
+ return event;
34
+ }
package/src/index.css CHANGED
@@ -20,3 +20,22 @@
20
20
  [drag-area] {
21
21
  cursor: move;
22
22
  }
23
+
24
+ .position-moving:not(.position-cloning) {
25
+ position: fixed !important;
26
+ width: var(--position-moving-width) !important;
27
+ height: var(--position-moving-height) !important;
28
+ min-width: var(--position-moving-width) !important;
29
+ min-height: var(--position-moving-height) !important;
30
+ max-width: var(--position-moving-width) !important;
31
+ max-height: var(--position-moving-height) !important;
32
+ box-sizing: border-box !important;
33
+ }
34
+
35
+ .position-cloning {
36
+ opacity: 0.6;
37
+ }
38
+
39
+ .position-moving-preview {
40
+ pointer-events: none;
41
+ }
package/src/index.js CHANGED
@@ -1,71 +1,222 @@
1
- import observer from "@cocreate/observer";
2
- import { queryElements } from "@cocreate/utils";
3
- import position from "./position";
4
- import "./index.css";
5
-
6
- const positionTriggers = new WeakSet();
7
-
8
- function init() {
9
- let elements = document.querySelectorAll("[moveable], [positionable]");
10
- initElements(elements);
11
- initPositionTriggers(document.querySelectorAll("[position-query]"));
12
- }
13
-
14
- function initElements(elements) {
15
- for (let element of elements) initElement(element);
16
- }
17
-
18
- function initElement(element) {
19
- if (!element.position)
20
- element.position = new position(element, element.parentElement);
21
- }
22
-
23
- function initPositionTriggers(elements) {
24
- for (const trigger of elements) {
25
- if (positionTriggers.has(trigger)) continue;
26
- positionTriggers.add(trigger);
27
- trigger.addEventListener("click", () => {
28
- requestAnimationFrame(() => {
29
- const targets = queryElements({ element: trigger, prefix: "position" });
30
- const anchors = queryElements({ element: trigger, prefix: "position-anchor" });
31
- const anchor = anchors[0] || trigger;
32
- for (const target of targets) {
33
- initElement(target);
34
- target.position.place(anchor);
35
- }
36
- });
37
- });
38
- }
39
- }
40
-
41
- observer.init({
42
- name: "positionAddedNodes",
43
- types: ["addedNodes"],
44
- selector: "[moveable], [positionable]",
45
- callback: (mutation) => {
46
- initElement(mutation.target);
47
- }
48
- });
49
-
50
- observer.init({
51
- name: "positionQueryAddedNodes",
52
- types: ["addedNodes", "attributes"],
53
- selector: "[position-query]",
54
- attributeFilter: ["position-query"],
55
- callback: (mutation) => {
56
- initPositionTriggers([mutation.target]);
57
- }
58
- });
59
-
60
- observer.init({
61
- name: "positionAttributes",
62
- types: ["attributes"],
63
- attributeFilter: ["moveable", "positionable"],
64
- callback: (mutation) => {
65
- initElement(mutation.target);
66
- }
67
- });
68
-
69
- init();
70
-
71
- export default position;
1
+ import observer from "@cocreate/observer";
2
+ import "./index.css";
3
+
4
+ const draggableSelector = "[draggable], [draggable-query], [clonable-query]";
5
+ const snappableSelector = "[droppable-query], [snappable-query], [position-reference]";
6
+ const placementSelector = "[position-query], [position-element], [position-events], [position-box]";
7
+
8
+ let draggablePromise;
9
+ let snappablePromise;
10
+ let placementPromise;
11
+
12
+ function load(getPromise, setPromise, importer) {
13
+ let promise = getPromise();
14
+ if (!promise) {
15
+ promise = importer()
16
+ .then(module => {
17
+ module.init?.();
18
+ return module.default || module;
19
+ })
20
+ .catch(error => {
21
+ setPromise(undefined);
22
+ throw error;
23
+ });
24
+ }
25
+ setPromise(promise);
26
+ return promise;
27
+ }
28
+
29
+ /**
30
+ *
31
+ * @public
32
+ * @environment client
33
+ */
34
+ function loadDraggable() {
35
+ return load(() => draggablePromise, value => { draggablePromise = value; },
36
+ () => import(/* webpackChunkName: "position-draggable" */ "./draggable.js"));
37
+ }
38
+
39
+ /**
40
+ *
41
+ * @public
42
+ * @environment client
43
+ */
44
+ function loadSnappable() {
45
+ return load(() => snappablePromise, value => { snappablePromise = value; },
46
+ () => import(/* webpackChunkName: "position-snappable" */ "./snappable.js"));
47
+ }
48
+
49
+ /**
50
+ *
51
+ * @public
52
+ * @environment client
53
+ */
54
+ function loadPlacement() {
55
+ return load(() => placementPromise, value => { placementPromise = value; },
56
+ () => import(/* webpackChunkName: "position-placement" */ "./position.js"));
57
+ }
58
+
59
+ function reportError(element, error, feature) {
60
+ const EventConstructor = element?.ownerDocument?.defaultView?.CustomEvent || CustomEvent;
61
+ element?.dispatchEvent(new EventConstructor("position-error", {
62
+ bubbles: true,
63
+ detail: { error, feature }
64
+ }));
65
+ }
66
+
67
+ function loadForMutation({ target, attributeName }) {
68
+ if (target?.matches?.(draggableSelector) || attributeName === "draggable")
69
+ loadDraggable().catch(error => reportError(target, error, "draggable"));
70
+ if (target?.matches?.(snappableSelector) || ["draggable-query", "clonable-query", "droppable-query", "snappable-query", "position-reference"].includes(attributeName))
71
+ loadSnappable().catch(error => reportError(target, error, "snappable"));
72
+ if (target?.matches?.(placementSelector) || ["position-query", "position-element", "position-events", "position-box"].includes(attributeName))
73
+ loadPlacement().catch(error => reportError(target, error, "placement"));
74
+ }
75
+
76
+ observer.init([
77
+ {
78
+ name: "CoCreatePositionLoaderAddedNodes",
79
+ types: ["addedNodes"],
80
+ selector: [draggableSelector, snappableSelector, placementSelector].join(", "),
81
+ callback: loadForMutation
82
+ },
83
+ {
84
+ name: "CoCreatePositionLoaderAttributes",
85
+ types: ["attributes"],
86
+ attributeFilter: ["draggable", "draggable-query", "clonable-query", "droppable-query", "snappable-query", "position-reference", "position-query", "position-element", "position-events", "position-box"],
87
+ selector: [draggableSelector, snappableSelector, placementSelector].join(", "),
88
+ callback: loadForMutation
89
+ }
90
+ ]);
91
+
92
+ function start() {
93
+ if (document.querySelector(draggableSelector)) loadDraggable().catch(() => {});
94
+ if (document.querySelector(snappableSelector)) loadSnappable().catch(() => {});
95
+ if (document.querySelector(placementSelector)) loadPlacement().catch(() => {});
96
+ }
97
+
98
+ start();
99
+
100
+ /** Load and initialize the requested position feature modules. * @public
101
+ * @environment client
102
+ */
103
+ export function init() {
104
+ start();
105
+ return Promise.all([draggablePromise, snappablePromise, placementPromise].filter(Boolean));
106
+ }
107
+
108
+ /**
109
+ *
110
+ * @public
111
+ * @environment client
112
+ */
113
+ export function initElement(element, options) {
114
+ return loadDraggable().then(module => module.initElement(element, options));
115
+ }
116
+
117
+ /**
118
+ *
119
+ * @public
120
+ * @environment client
121
+ */
122
+ export function initElements(elements) {
123
+ return loadDraggable().then(module => module.initElements(elements));
124
+ }
125
+
126
+ export { loadDraggable, loadSnappable, loadPlacement };
127
+
128
+ export default { init, initElement, initElements, loadDraggable, loadSnappable, loadPlacement };
129
+
130
+ /**
131
+ *
132
+ * @name clonable-query
133
+ * @kind attribute
134
+ * @environment client
135
+ */
136
+
137
+ /**
138
+ *
139
+ * @name draggable
140
+ * @kind attribute
141
+ * @environment client
142
+ */
143
+
144
+ /**
145
+ *
146
+ * @name draggable-query
147
+ * @kind attribute
148
+ * @environment client
149
+ */
150
+
151
+ /**
152
+ *
153
+ * @name droppable-query
154
+ * @kind attribute
155
+ * @environment client
156
+ */
157
+
158
+ /**
159
+ *
160
+ * @name position-box
161
+ * @kind attribute
162
+ * @environment client
163
+ */
164
+
165
+ /**
166
+ *
167
+ * @name position-element
168
+ * @kind attribute
169
+ * @environment client
170
+ */
171
+
172
+ /**
173
+ *
174
+ * @name position-events
175
+ * @kind attribute
176
+ * @environment client
177
+ */
178
+
179
+ /**
180
+ *
181
+ * @name position-query
182
+ * @kind attribute
183
+ * @environment client
184
+ */
185
+
186
+ /**
187
+ *
188
+ * @name position-reference
189
+ * @kind attribute
190
+ * @environment client
191
+ */
192
+
193
+ /**
194
+ *
195
+ * @name snappable-query
196
+ * @kind attribute
197
+ * @environment client
198
+ */
199
+
200
+ /**
201
+ * Declarative DOM positioning, dragging, snapping, and drag-and-drop for web interfaces.
202
+ *
203
+ * @module position
204
+ * @environment client
205
+ */
206
+
207
+ /**
208
+ * Declarative selectors recognized by this module.
209
+ *
210
+ * @name declarative-selectors
211
+ * @kind member
212
+ * @selectors
213
+ * @environment client
214
+ * @example
215
+ * ```json
216
+ * [
217
+ * "[draggable], [draggable-query], [clonable-query]",
218
+ * "[droppable-query], [snappable-query], [position-reference]",
219
+ * "[position-query], [position-element], [position-events], [position-box]"
220
+ * ]
221
+ * ```
222
+ */