quasar-ui-danx 0.4.35 → 0.4.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json 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