quasar-ui-danx 0.4.35 → 0.4.37

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quasar-ui-danx",
3
- "version": "0.4.35",
3
+ "version": "0.4.37",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
@@ -2,8 +2,8 @@
2
2
  <div
3
3
  :class="{'cursor-move': !showHandle && !disabled}"
4
4
  :draggable="disabled ? undefined : 'true'"
5
- @dragstart.stop="disabled ? null : dragAndDrop.dragStart"
6
- @dragend="disabled ? null : dragAndDrop.dragEnd"
5
+ @dragstart.stop="dragAndDrop.dragStart"
6
+ @dragend="dragAndDrop.dragEnd"
7
7
  >
8
8
  <div :class="contentClass">
9
9
  <div
@@ -28,7 +28,8 @@
28
28
  </div>
29
29
  </template>
30
30
  <script setup lang="ts">
31
- import { computed } from "vue";
31
+ import { DropZoneResolver } from "src/components/DragAndDrop/dragAndDrop";
32
+ import { computed, watch } from "vue";
32
33
  import { DragHandleDotsIcon as DragHandleIcon } from "../../svg";
33
34
  import { SvgImg } from "../Utility";
34
35
  import { ListDragAndDrop } from "./listDragAndDrop";
@@ -36,7 +37,7 @@ import { ListDragAndDrop } from "./listDragAndDrop";
36
37
  const emit = defineEmits(["position", "update:list-items", "drop-zone"]);
37
38
  const dragging = defineModel<boolean>();
38
39
  const props = withDefaults(defineProps<{
39
- dropZone: string | (() => string);
40
+ dropZone: DropZoneResolver;
40
41
  direction?: "vertical" | "horizontal";
41
42
  showHandle?: boolean;
42
43
  changeDropZone?: boolean;
@@ -53,13 +54,21 @@ const props = withDefaults(defineProps<{
53
54
  listItems: () => []
54
55
  });
55
56
 
57
+ watch(() => props.disabled, (value) => dragAndDrop.setOptions({ disabled: value }));
58
+
56
59
  const resolvedHandleClass = computed(() => ({
57
60
  "cursor-move": !props.disabled,
58
61
  ...(typeof props.handleClass === "string" ? { [props.handleClass]: true } : props.handleClass)
59
62
  }));
63
+
60
64
  const dragAndDrop = new ListDragAndDrop()
61
65
  .setDropZone(props.dropZone)
62
- .setOptions({ showPlaceholder: true, allowDropZoneChange: props.changeDropZone, direction: props.direction })
66
+ .setOptions({
67
+ showPlaceholder: true,
68
+ allowDropZoneChange: props.changeDropZone,
69
+ direction: props.direction,
70
+ disabled: props.disabled
71
+ })
63
72
  .onStart(() => dragging.value = true)
64
73
  .onEnd(() => dragging.value = false)
65
74
  .onDropZoneChange((target, dropZone, newPosition, oldPosition, data) => {
@@ -1,3 +1,9 @@
1
+ import { AnyObject } from "src/types";
2
+
3
+ export type DropZoneResolver = ((e: DragEvent) => HTMLElement) | string | null;
4
+ export type DragAndDropCallback = ((e: DragEvent, data: DraggableData) => void) | null;
5
+ export type DraggableData = AnyObject | string | number | null;
6
+
1
7
  /**
2
8
  * Drag and Drop basic functionality for dragging elements and firing events on drag start, drag over and drag end
3
9
  */
@@ -7,6 +13,7 @@ export class DragAndDrop {
7
13
  hideDragImage?: boolean,
8
14
  showPlaceholder?: boolean,
9
15
  allowDropZoneChange?: boolean,
16
+ disabled?: boolean,
10
17
  } = { direction: "vertical", hideDragImage: false };
11
18
 
12
19
  // State
@@ -15,19 +22,20 @@ export class DragAndDrop {
15
22
  startSize = 0;
16
23
  cursorY = 0;
17
24
  cursorX = 0;
18
- onStartCb = null;
19
- onEndCb = null;
20
- onDropCb = null;
21
- onDraggingCb = null;
22
- dropZoneResolver = null;
25
+ onStartCb: DragAndDropCallback = null;
26
+ onEndCb: DragAndDropCallback = null;
27
+ onDropCb: DragAndDropCallback = null;
28
+ onDraggingCb: DragAndDropCallback = null;
29
+ dropZoneResolver: DropZoneResolver = null;
23
30
  currentDropZone: HTMLElement | null = null;
24
- draggableData = null;
31
+ draggableData: DraggableData = null;
25
32
  // Used to abort dragging event listeners on the element
26
- abortController = null;
33
+ abortController: AbortController | null = null;
27
34
 
28
35
  constructor(options = {}) {
29
36
  // Options
30
37
  options = {
38
+ disabled: false,
31
39
  direction: "vertical",
32
40
  hideDragImage: false,
33
41
  ...options
@@ -38,8 +46,6 @@ export class DragAndDrop {
38
46
 
39
47
  /**
40
48
  * Set the options for the drag and drop instance
41
- * @param options
42
- * @returns {DragAndDrop}
43
49
  */
44
50
  setOptions(options = {}) {
45
51
  this.options = { ...this.options, ...options };
@@ -48,7 +54,6 @@ export class DragAndDrop {
48
54
 
49
55
  /**
50
56
  * Returns if the list is stacked vertically or horizontally
51
- * @returns {boolean}
52
57
  */
53
58
  isVertical() {
54
59
  return this.options.direction === "vertical";
@@ -56,77 +61,68 @@ export class DragAndDrop {
56
61
 
57
62
  /**
58
63
  * Set the target drop zone for draggable elements
59
- * @param dropZone
60
- * @returns {DragAndDrop}
61
64
  */
62
- setDropZone(dropZone) {
65
+ setDropZone(dropZone: DropZoneResolver) {
63
66
  this.dropZoneResolver = dropZone;
64
67
  return this;
65
68
  }
66
69
 
67
70
  /**
68
71
  * Callback that fires when an element has started dragging
69
- * @param cb
70
- * @returns {DragAndDrop}
71
72
  */
72
- onStart(cb) {
73
+ onStart(cb: DragAndDropCallback) {
73
74
  this.onStartCb = cb;
74
75
  return this;
75
76
  }
76
77
 
77
78
  /**
78
79
  * Callback that fires when an element has stopped dragging
79
- * @param cb
80
- * @returns {DragAndDrop}
81
80
  */
82
- onEnd(cb) {
81
+ onEnd(cb: DragAndDropCallback) {
83
82
  this.onEndCb = cb;
84
83
  return this;
85
84
  }
86
85
 
87
86
  /**
88
87
  * Callback that fires when the dragging element is moved
89
- * @param cb
90
- * @returns {DragAndDrop}
91
88
  */
92
- onDragging(cb) {
89
+ onDragging(cb: DragAndDropCallback) {
93
90
  this.onDraggingCb = cb;
94
91
  return this;
95
92
  }
96
93
 
97
94
  /**
98
95
  * Callback that fires when the dragging element has been dropped
99
- * @param cb
100
- * @returns {DragAndDrop}
101
96
  */
102
- onDrop(cb) {
97
+ onDrop(cb: DragAndDropCallback) {
103
98
  this.onDropCb = cb;
104
99
  return this;
105
100
  }
106
101
 
107
102
  /**
108
103
  * Start listening for drag events and prepare an element for drag/drop
109
- * @param e
110
- * @param data
111
104
  */
112
- dragStart(e, data) {
105
+ dragStart(e: DragEvent, data: DraggableData) {
106
+ if (this.options.disabled) return;
113
107
  this.currentDropZone = this.getDropZone(e);
114
108
 
115
109
  if (this.currentDropZone) {
116
110
  this.startY = e.clientY;
117
111
  this.startX = e.clientX;
118
112
  this.startSize = this.getDropZoneSize();
119
- e.dataTransfer.effectAllowed = "move";
120
- e.dataTransfer.dropEffect = "move";
113
+ if (e.dataTransfer) {
114
+ e.dataTransfer.effectAllowed = "move";
115
+ e.dataTransfer.dropEffect = "move";
116
+ }
121
117
  this.draggableData = data;
122
118
  this.abortController = new AbortController();
123
119
  const options = { signal: this.abortController.signal };
124
120
  document.addEventListener("dragenter", (e) => this.dragEnter(e), options);
125
121
  document.addEventListener("dragover", (e) => this.dragOver(e), options);
126
122
  document.addEventListener("drop", (e) => this.drop(e), options);
127
- this.onStartCb && this.onStartCb(e);
123
+ this.onStartCb && this.onStartCb(e, this.draggableData);
128
124
 
129
- if (this.options.hideDragImage) {
125
+ if (e.dataTransfer && this.options.hideDragImage) {
130
126
  e.dataTransfer.setDragImage(new Image(), 0, 0);
131
127
  }
132
128
  } else {
@@ -137,55 +133,53 @@ export class DragAndDrop {
137
133
  /**
138
134
  * Clean up event listeners after dragging is done
139
135
  */
140
- dragEnd(e) {
136
+ dragEnd(e: DragEvent) {
137
+ if (this.options.disabled) return;
141
138
  this.currentDropZone = null;
142
139
  this.abortController?.abort();
140
+ this.onEndCb && this.onEndCb(e, this.draggableData);
143
141
  this.draggableData = null;
144
- this.onEndCb && this.onEndCb(e);
145
142
  }
146
143
 
147
144
  /**
148
145
  * The dragging element has entered a new target
149
- * @param e
150
146
  */
151
- dragEnter(e) {
147
+ dragEnter(e: DragEvent) {
152
148
  e.preventDefault();
153
149
  }
154
150
 
155
151
  /**
156
152
  * The dragging element is moving
157
- * @param e
158
153
  */
159
- dragOver(e) {
154
+ dragOver(e: DragEvent) {
155
+ if (this.options.disabled) return;
160
156
  e.preventDefault();
161
157
  this.cursorY = e.clientY;
162
158
  this.cursorX = e.clientX;
163
- this.onDraggingCb && this.onDraggingCb(e);
159
+ this.onDraggingCb && this.onDraggingCb(e, this.draggableData);
164
160
  }
165
161
 
166
162
  /**
167
163
  * Handle dropping the element into its correct position
168
- * @param e
169
164
  */
170
- drop(e) {
171
- e.dataTransfer.dropEffect = "move";
165
+ drop(e: DragEvent) {
166
+ if (this.options.disabled) return;
167
+ e.dataTransfer && (e.dataTransfer.dropEffect = "move");
172
168
  e.preventDefault();
173
169
  this.onDropCb && this.onDropCb(e, this.draggableData);
174
170
  }
175
171
 
176
172
  /**
177
173
  * Returns the drop zone if the current target element is or is inside the drop zone
178
- * @param e
179
- * @returns {HTMLElement|null}
180
174
  */
181
- getDropZone(e) {
175
+ getDropZone(e: DragEvent): HTMLElement | null {
182
176
  if (typeof this.dropZoneResolver === "string") {
183
- let target = e.target;
177
+ let target = e.target as HTMLElement;
184
178
  while (target) {
185
179
  if (target.dataset?.dropZone === this.dropZoneResolver) {
186
180
  return target;
187
181
  }
188
- target = target.parentNode;
182
+ target = target.parentNode as HTMLElement;
189
183
  }
190
184
  return null;
191
185
  } else if (typeof this.dropZoneResolver === "function") {
@@ -197,9 +191,8 @@ export class DragAndDrop {
197
191
 
198
192
  /**
199
193
  * Returns the distance between the start and current cursor position
200
- * @returns {number}
201
194
  */
202
- getDistance() {
195
+ getDistance(): number {
203
196
  return this.isVertical()
204
197
  ? this.cursorY - this.startY
205
198
  : this.cursorX - this.startX;
@@ -208,19 +201,17 @@ export class DragAndDrop {
208
201
  /**
209
202
  * Returns the size of the drop zone
210
203
  */
211
- getDropZoneSize() {
212
- return this.isVertical()
204
+ getDropZoneSize(): number {
205
+ return (this.isVertical()
213
206
  ? this.currentDropZone?.offsetHeight
214
- : this.currentDropZone?.offsetWidth;
207
+ : this.currentDropZone?.offsetWidth) || 0;
215
208
  }
216
209
 
217
210
  /**
218
211
  * Returns the percent change between the start and current cursor position relative to the drop zone size
219
- *
220
- * @returns {number}
221
212
  */
222
213
  getPercentChange() {
223
214
  const distance = this.getDistance();
224
- return (distance / this.startSize) * 100;
215
+ return (distance / (this.startSize || 1)) * 100;
225
216
  }
226
217
  }
@@ -1,20 +1,25 @@
1
- import { DragAndDrop } from "./dragAndDrop";
1
+ import { DragAndDrop, DraggableData } from "./dragAndDrop";
2
+
3
+ export type DragPositionChangeCallback =
4
+ ((position: number, initialPosition: number, data: DraggableData) => void)
5
+ | null;
6
+ export type DropZoneChangeCallback =
7
+ ((e: DragEvent, dropZone: HTMLElement, position: number, initialPosition: number, data: DraggableData) => void)
8
+ | null;
2
9
 
3
10
  /**
4
11
  * ListDragAndDrop supports dragging elements in a list to new positions in the same list.
5
12
  * A placeholder is rendered to show the position the list item will be dropped.
6
- *
7
- * @class
8
13
  */
9
14
  export class ListDragAndDrop extends DragAndDrop {
10
15
  listPosition = 0;
11
16
  cursorPosition = 0;
12
17
  initialPosition = 0;
13
18
  initialDropZone: HTMLElement | null = null;
14
- onPositionChangeCb = null;
15
- onDragPositionChangeCb = null;
16
- onDropZoneChangeCb = null;
17
- placeholder = null;
19
+ onPositionChangeCb: DragPositionChangeCallback = null;
20
+ onDragPositionChangeCb: DragPositionChangeCallback = null;
21
+ onDropZoneChangeCb: DropZoneChangeCallback = null;
22
+ placeholder: HTMLElement | null = null;
18
23
 
19
24
  constructor(options = {}) {
20
25
  super({
@@ -27,7 +32,7 @@ export class ListDragAndDrop extends DragAndDrop {
27
32
  /**
28
33
  * Callback that fires after dragging has ended and the list position has changed from the original
29
34
  */
30
- onPositionChange(cb): ListDragAndDrop {
35
+ onPositionChange(cb: DragPositionChangeCallback): ListDragAndDrop {
31
36
  this.onPositionChangeCb = cb;
32
37
  return this;
33
38
  }
@@ -35,31 +40,29 @@ export class ListDragAndDrop extends DragAndDrop {
35
40
  /**
36
41
  * Callback that fires when the drop zone has changed
37
42
  */
38
- onDropZoneChange(cb): ListDragAndDrop {
43
+ onDropZoneChange(cb: DropZoneChangeCallback): ListDragAndDrop {
39
44
  this.onDropZoneChangeCb = cb;
40
45
  return this;
41
46
  }
42
47
 
43
48
  /**
44
49
  * Callback that fires while dragging the element when the cursor's position has changed in the list
45
- * @param cb
46
- * @returns {ListDragAndDrop}
47
50
  */
48
- onDragPositionChange(cb) {
51
+ onDragPositionChange(cb: DragPositionChangeCallback) {
49
52
  this.onDragPositionChangeCb = cb;
50
53
  return this;
51
54
  }
52
55
 
53
56
  /**
54
57
  * Start listening for drag events and prepare an element for drag/drop
55
- * @param e
56
- * @param data
57
58
  */
58
- dragStart(e, data) {
59
+ dragStart(e: DragEvent, data: DraggableData) {
60
+ if (this.options.disabled) return;
61
+
59
62
  super.dragStart(e, data);
60
63
 
61
64
  if (this.currentDropZone) {
62
- this.listPosition = this.getListPosition(e.target);
65
+ this.listPosition = this.getListPosition(e.target as HTMLElement);
63
66
  this.initialPosition = this.listPosition;
64
67
  this.initialDropZone = this.currentDropZone;
65
68
  this.updateScrollPosition();
@@ -69,7 +72,9 @@ export class ListDragAndDrop extends DragAndDrop {
69
72
  /**
70
73
  * When dragging has ended, check for list position changes and fire the onPositionChange callback if it has
71
74
  */
72
- dragEnd(e) {
75
+ dragEnd(e: DragEvent) {
76
+ if (this.options.disabled) return;
77
+
73
78
  const draggableData = this.draggableData;
74
79
  this.placeholder?.remove();
75
80
  const newDropZone = this.currentDropZone;
@@ -87,16 +92,15 @@ export class ListDragAndDrop extends DragAndDrop {
87
92
 
88
93
  /**
89
94
  * The dragging element is moving
90
- * @param e
91
95
  */
92
- dragOver(e) {
96
+ dragOver(e: DragEvent) {
97
+ if (this.options.disabled) return;
93
98
  super.dragOver(e);
94
99
  this.updateListPosition(e);
95
100
  }
96
101
 
97
102
  /**
98
103
  * Notify if the targeted position of the cursor is different from the current position
99
- * @param e
100
104
  */
101
105
  updateListPosition(e: MouseEvent) {
102
106
  const point = {
@@ -129,17 +133,15 @@ export class ListDragAndDrop extends DragAndDrop {
129
133
  // The position has changed, trigger the callback
130
134
  if (this.listPosition !== prevPosition) {
131
135
  this.onDragPositionChangeCb &&
132
- this.onDragPositionChangeCb(this.listPosition, this.draggableData);
136
+ this.onDragPositionChangeCb(this.listPosition, this.initialPosition, this.draggableData);
133
137
  }
134
138
  }
135
139
  }
136
140
 
137
141
  /**
138
142
  * Find the numeric position of the element in the children of the list
139
- * @returns {Number|null}
140
- * @param item
141
143
  */
142
- getListPosition(item) {
144
+ getListPosition(item: HTMLElement): number {
143
145
  let index = 0;
144
146
  for (const child of this.getChildren()) {
145
147
  if (child === item) {
@@ -148,14 +150,14 @@ export class ListDragAndDrop extends DragAndDrop {
148
150
  index++;
149
151
  }
150
152
 
151
- return null;
153
+ return 0;
152
154
  }
153
155
 
154
156
  /**
155
157
  * Get all the children of the current drop zone, excluding the placeholder
156
- * @returns {*}
157
158
  */
158
159
  getChildren() {
160
+ // @ts-expect-error HTMLCollection is iterable
159
161
  return [...(this.currentDropZone?.children || [])].filter(
160
162
  (c) => c.className.match(/dx-drag-placeholder/) === null
161
163
  );
@@ -177,10 +179,8 @@ export class ListDragAndDrop extends DragAndDrop {
177
179
 
178
180
  /**
179
181
  * Find the element at the current cursor position in the given drop zone
180
- * @param point
181
- * @returns {null}
182
182
  */
183
- getListPositionOfPoint(point) {
183
+ getListPositionOfPoint(point: { x: number, y: number }) {
184
184
  let index = 0;
185
185
  const children = this.getChildren();
186
186
 
@@ -205,45 +205,47 @@ export class ListDragAndDrop extends DragAndDrop {
205
205
  * Updates the scroll position while dragging an element so a user can navigate a longer list while dragging
206
206
  */
207
207
  updateScrollPosition() {
208
- if (this.currentDropZone) {
209
- const rect = this.currentDropZone.getBoundingClientRect();
210
- const threshold = 100;
211
- let velocity = 0;
212
- const velocityFn = (x) => x * 5;
213
- const cursorPos = this.isVertical() ? this.cursorY : this.cursorX;
214
- const rectStart = this.isVertical() ? rect.top : rect.left;
215
- const rectEnd = this.isVertical() ? rect.bottom : rect.right;
216
- const beforeDiff = rectStart + threshold - cursorPos;
217
- const afterDiff = cursorPos - (rectEnd - threshold);
218
-
219
- if (beforeDiff > 0) {
220
- velocity = -velocityFn(beforeDiff);
221
- } else if (afterDiff > 0) {
222
- velocity = velocityFn(afterDiff);
223
- }
208
+ if (!this.currentDropZone) return;
224
209
 
225
- if (velocity) {
226
- if (this.isVertical()) {
227
- this.currentDropZone.scrollTo({
228
- top: this.currentDropZone.scrollTop + velocity,
229
- behavior: "smooth"
230
- });
231
- } else {
232
- this.currentDropZone.scrollTo({
233
- left: this.currentDropZone.scrollLeft + velocity,
234
- behavior: "smooth"
235
- });
236
- }
237
- }
210
+ const rect = this.currentDropZone.getBoundingClientRect();
211
+ const threshold = 100;
212
+ let velocity = 0;
213
+ const velocityFn = (x: number) => x * 5;
214
+ const cursorPos = this.isVertical() ? this.cursorY : this.cursorX;
215
+ const rectStart = this.isVertical() ? rect.top : rect.left;
216
+ const rectEnd = this.isVertical() ? rect.bottom : rect.right;
217
+ const beforeDiff = rectStart + threshold - cursorPos;
218
+ const afterDiff = cursorPos - (rectEnd - threshold);
238
219
 
239
- setTimeout(() => this.updateScrollPosition(), 500);
220
+ if (beforeDiff > 0) {
221
+ velocity = -velocityFn(beforeDiff);
222
+ } else if (afterDiff > 0) {
223
+ velocity = velocityFn(afterDiff);
240
224
  }
225
+
226
+ if (velocity) {
227
+ if (this.isVertical()) {
228
+ this.currentDropZone.scrollTo({
229
+ top: this.currentDropZone.scrollTop + velocity,
230
+ behavior: "smooth"
231
+ });
232
+ } else {
233
+ this.currentDropZone.scrollTo({
234
+ left: this.currentDropZone.scrollLeft + velocity,
235
+ behavior: "smooth"
236
+ });
237
+ }
238
+ }
239
+
240
+ setTimeout(() => this.updateScrollPosition(), 500);
241
241
  }
242
242
 
243
243
  /**
244
244
  * Render a placeholder element at the given position (in between the elements)
245
245
  */
246
246
  renderPlaceholder() {
247
+ if (!this.currentDropZone) return;
248
+
247
249
  // If we're not allowed to change drop zones and we're not in the same drop zone, don't render the placeholder
248
250
  if (!this.options.allowDropZoneChange && !this.isSameDropZone()) {
249
251
  return;
@@ -258,7 +260,7 @@ export class ListDragAndDrop extends DragAndDrop {
258
260
  if (this.isVertical()) {
259
261
  this.placeholder.classList.add("dx-direction-vertical");
260
262
  this.placeholder.classList.remove("dx-direction-horizontal");
261
- this.placeholder.style.height = undefined;
263
+ this.placeholder.style.height = "";
262
264
  } else {
263
265
  this.placeholder.classList.add("dx-direction-horizontal");
264
266
  this.placeholder.classList.remove("dx-direction-vertical");
@@ -11,15 +11,11 @@ export function useActionRoutes(baseUrl: string, extend?: object): ListControlsR
11
11
  summary(filter) {
12
12
  return request.post(`${baseUrl}/summary`, { filter });
13
13
  },
14
- details(target) {
15
- return request.get(`${baseUrl}/${target.id}/details`);
14
+ details(target, fields) {
15
+ return request.get(`${baseUrl}/${target.id}/details`, { params: { fields } });
16
16
  },
17
- async detailsAndStore(target) {
18
- const item = await request.get(`${baseUrl}/${target.id}/details`);
19
- return storeObject(item);
20
- },
21
- async relation(target, relation) {
22
- const item = await request.get(`${baseUrl}/${target.id}/relation/${relation}`);
17
+ async detailsAndStore(target, fields) {
18
+ const item = await request.get(`${baseUrl}/${target.id}/details`, { params: { fields } });
23
19
  return storeObject(item);
24
20
  },
25
21
  fieldOptions() {
@@ -24,11 +24,9 @@ export interface ListControlsRoutes<T = ActionTargetItem> {
24
24
 
25
25
  summary?(filter?: ListControlsFilter): Promise<AnyObject>;
26
26
 
27
- details?(target: T): Promise<T>;
27
+ details?(target: T, fields: ControlsFieldsList): Promise<T>;
28
28
 
29
- detailsAndStore?(target: T): Promise<T>;
30
-
31
- relation?(target: T, relation: string): Promise<T>;
29
+ detailsAndStore?(target: T, fields: ControlsFieldsList): Promise<T>;
32
30
 
33
31
  more?(pager: ListControlsPagination): Promise<T[]>;
34
32