plain-design 1.0.0-beta.72 → 1.0.0-beta.73

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": "plain-design",
3
- "version": "1.0.0-beta.72",
3
+ "version": "1.0.0-beta.73",
4
4
  "description": "",
5
5
  "main": "dist/plain-design.min.js",
6
6
  "module": "dist/plain-design.commonjs.min.js",
@@ -143,22 +143,25 @@ export function createVirtualDraggier(
143
143
 
144
144
  const getItemStyles = (index: number) => {
145
145
  const { dragIndex, initDragElementRect } = dragStaticState;
146
- if (dragReactiveState.pointIndex == null || dragIndex == null || dragReactiveState.pointIndex == dragIndex || index == dragIndex) {
146
+ if (dragReactiveState.pointIndex == null || dragIndex == null) {
147
147
  return;
148
148
  }
149
+ const styles: StyleProperties = {};
150
+ styles.transition = 'transform ease 300ms';
149
151
  if (dragReactiveState.pointIndex >= index && index > dragIndex) {
150
- return {
151
- transform: props.horizontal ?
152
- `translateX(-${initDragElementRect?.width || 0}px)` :
153
- `translateY(-${initDragElementRect?.height || 0}px)`
154
- };
152
+ styles.transform = props.horizontal ?
153
+ `translateX(-${initDragElementRect?.width || 0}px)` :
154
+ `translateY(-${initDragElementRect?.height || 0}px)`;
155
155
  } else if (dragReactiveState.pointIndex <= index && index < dragIndex) {
156
- return {
157
- transform: props.horizontal ?
158
- `translateX(${initDragElementRect?.width || 0}px)` :
159
- `translateY(${initDragElementRect?.height || 0}px)`
160
- };
156
+ styles.transform = props.horizontal ?
157
+ `translateX(${initDragElementRect?.width || 0}px)` :
158
+ `translateY(${initDragElementRect?.height || 0}px)`;
159
+ } else {
160
+ styles.transform = props.horizontal ?
161
+ `translateX(0px)` :
162
+ `translateY(0px)`;
161
163
  }
164
+ return styles;
162
165
  };
163
166
 
164
167
  const draggier = createWebDraggier({
@@ -257,17 +260,19 @@ export function createVirtualDraggier(
257
260
  dragReactiveState.endingDefer.resolve();
258
261
  };
259
262
 
260
- const newStart = (() => {
261
- if (props.horizontal) {
262
- return dragReactiveState.pointIndex < dragIndex ?
263
- (getElementRect(strutElement).left + pointNode.start) + 'px' :
264
- (getElementRect(strutElement).left + pointNode.end - initDragElementRect.width) + 'px';
265
- } else {
266
- return dragReactiveState.pointIndex < dragIndex ?
267
- (getElementRect(strutElement).top + pointNode.start) + 'px' :
268
- (getElementRect(strutElement).top + pointNode.end - initDragElementRect.height) + 'px';
269
- }
270
- })();
263
+ const newStart = Math.ceil(
264
+ (() => {
265
+ if (props.horizontal) {
266
+ return dragReactiveState.pointIndex < dragIndex ?
267
+ (getElementRect(strutElement).left + pointNode.start) :
268
+ (getElementRect(strutElement).left + pointNode.end - initDragElementRect.width);
269
+ } else {
270
+ return dragReactiveState.pointIndex < dragIndex ?
271
+ (getElementRect(strutElement).top + pointNode.start) :
272
+ (getElementRect(strutElement).top + pointNode.end - initDragElementRect.height);
273
+ }
274
+ })()
275
+ ) + 'px';
271
276
 
272
277
  const styleName = props.horizontal ? 'left' : 'top';
273
278
  if (dragElementShadow.style[styleName] == newStart) {
@@ -350,7 +355,7 @@ export function createVirtualDraggier(
350
355
  };
351
356
  }
352
357
 
353
- interface iDraggierStaticState {
358
+ export interface iDraggierStaticState {
354
359
  /*拖拽节点在所有数据中的索引*/
355
360
  dragIndex: number;
356
361
  /*拖拽元素的节点的 data-vid 标识字符*/
@@ -369,14 +374,14 @@ interface iDraggierStaticState {
369
374
  strutElement: HTMLElement;
370
375
  }
371
376
 
372
- interface iDraggierReactiveState {
377
+ export interface iDraggierReactiveState {
373
378
  pointIndex: number | null,
374
379
  onScroll: SimpleFunction,
375
380
  isDragging: boolean,
376
381
  endingDefer: DFD<void>,
377
382
  }
378
383
 
379
- function createDefaultDraggierReactiveState(): iDraggierReactiveState {
384
+ export function createDefaultDraggierReactiveState(): iDraggierReactiveState {
380
385
  return {
381
386
  pointIndex: null,
382
387
  onScroll: doNothing,
@@ -385,7 +390,7 @@ function createDefaultDraggierReactiveState(): iDraggierReactiveState {
385
390
  };
386
391
  }
387
392
 
388
- function createDefaultDraggierStaticState(): iDraggierStaticState {
393
+ export function createDefaultDraggierStaticState(): iDraggierStaticState {
389
394
  const fakeEle = null as any;
390
395
  return {
391
396
  dragIndex: -1,
@@ -64,7 +64,7 @@ export const VirtualList = designComponent({
64
64
  draggier.value?.emitScroll(e);
65
65
  }));
66
66
 
67
- const { virtual } = useVirtualList({ dataModel, refs, props, emit, isDragging: () => !!(draggier.value?.dragReactiveState.isDragging) });
67
+ const { virtual } = useVirtualList({ dataModel, refs, props, emit });
68
68
 
69
69
  const onRefScroll = (scroll: typeof Scroll.use.class | null) => {
70
70
  onRef.scroll(scroll);
@@ -34,7 +34,6 @@ export function useVirtualList(
34
34
  refs,
35
35
  emit,
36
36
  transform,
37
- isDragging
38
37
  }: {
39
38
  dataModel: { value: any[] | undefined | null },
40
39
  props: {
@@ -57,8 +56,6 @@ export function useVirtualList(
57
56
  onPageIndexChange: (data: { pageIndex: number, startIndex: number, endIndex: number }) => void,
58
57
  },
59
58
  transform?: boolean,
60
- /*当前是否处于拖拽中的状态,是则会给组件根节点加上一个class,使得内部所有的data-vid元素启用transition动画*/
61
- isDragging?: () => boolean,
62
59
  }
63
60
  ) {
64
61
 
@@ -83,7 +80,7 @@ export function useVirtualList(
83
80
  const idMap = new WeakMap<PlainObject, string>();
84
81
  let prevEnd = 0;
85
82
 
86
- const nodes = computed(() => {
83
+ const nodes = computed((): iVirtualNode[] => {
87
84
  prevEnd = 0;
88
85
  return (dataModel.value || []).map((item, index): DataNode => {
89
86
  let id = idMap.get(item);
@@ -136,7 +133,6 @@ export function useVirtualList(
136
133
  {
137
134
  'virtual-list-disabled': props.disabled,
138
135
  'virtual-list-horizontal': props.horizontal,
139
- 'virtual-list-is-dragging': !!(isDragging?.()),
140
136
  },
141
137
  ]);
142
138
 
@@ -410,3 +406,12 @@ export function useVirtualList(
410
406
  }
411
407
 
412
408
  export type iVirtualList = ReturnType<typeof useVirtualList>['virtual']
409
+
410
+ export interface iVirtualNode<T = PlainObject> {
411
+ id: string,
412
+ item: T,
413
+ size: number,
414
+ start: number,
415
+ end: number,
416
+ index: number
417
+ }
@@ -42,12 +42,6 @@
42
42
  }
43
43
  }
44
44
  }
45
-
46
- &.virtual-list-is-dragging {
47
- [data-vid] {
48
- transition: all ease 300ms;
49
- }
50
- }
51
45
  }
52
46
 
53
47
 
@@ -0,0 +1,374 @@
1
+ import {getComponentCls, iMouseEvent, iTouchEvent, reactive, StyleProperties} from "plain-design-composition";
2
+ import {doNothing} from "plain-utils/utils/doNothing";
3
+ import {findParentElement} from "plain-utils/dom/findParentElement";
4
+ import {hasClass} from "plain-utils/dom/hasClass";
5
+ import {getElementRect} from "../../utils/getElementRect";
6
+ import {createWebDraggier} from "../../utils/createDraggier";
7
+ import {delay} from "plain-utils/utils/delay";
8
+ import {PlainScroll} from "../Scroll";
9
+ import {createDefaultDraggierReactiveState, createDefaultDraggierStaticState, iVirtualDraggierHandler} from "../VirtualList/createVirtualDraggier";
10
+ import {iVirtualNode} from "../VirtualList/useVirtualList";
11
+
12
+ /**
13
+ * 创建虚拟列表的拖拽移动节点管理对象
14
+ * @author 韦胜健
15
+ * @date 2024/5/29 14:55
16
+ */
17
+ export function createScrollDraggier(
18
+ {
19
+ props,
20
+ getScroll,
21
+ dataModel,
22
+ getItemElements,
23
+ onDraggierStart,
24
+ onDraggierMove,
25
+ onDraggierEnd,
26
+ onDraggierTap,
27
+ }: {
28
+ props: { horizontal: boolean },
29
+ getScroll: () => PlainScroll | undefined,
30
+ dataModel: { value: any[] | undefined | null },
31
+ getItemElements: () => HTMLElement[],
32
+
33
+ onDraggierStart?: iVirtualDraggierHandler,
34
+ onDraggierMove?: iVirtualDraggierHandler,
35
+ onDraggierEnd?: iVirtualDraggierHandler,
36
+ onDraggierTap?: iVirtualDraggierHandler,
37
+ }
38
+ ) {
39
+
40
+ /*管理自动滚动*/
41
+ const autoScrollManager = (() => {
42
+ const _state = { value: null as null | 'top' | 'bottom' | 'left' | 'right' };
43
+
44
+ const handler = {
45
+ top: () => {
46
+ getScroll()?.methods.autoScrollTop();
47
+ },
48
+ bottom: () => {
49
+ getScroll()?.methods.autoScrollBottom();
50
+ },
51
+ left: () => {
52
+ getScroll()?.methods.autoScrollLeft();
53
+ },
54
+ right: () => {
55
+ getScroll()?.methods.autoScrollRight();
56
+ },
57
+ stop: () => {
58
+ getScroll()?.methods.stopAutoScroll();
59
+ },
60
+ };
61
+
62
+ const set = (val: null | 'top' | 'bottom' | 'left' | 'right') => {
63
+ const newVal = val;
64
+ const oldVal = _state.value;
65
+ if (newVal == oldVal) {
66
+ return;
67
+ }
68
+ _state.value = val;
69
+
70
+ if (!oldVal && !!newVal) {
71
+ handler[newVal]();
72
+ } else {
73
+ handler.stop();
74
+ }
75
+ };
76
+ const get = () => _state.value;
77
+ const stop = () => getScroll()?.methods.stopAutoScroll();
78
+ return { get, set, stop };
79
+ })();
80
+
81
+ /*静态变量*/
82
+ const dragStaticState = {
83
+ ...createDefaultDraggierStaticState(),
84
+ nodes: [] as iVirtualNode[],
85
+ };
86
+
87
+ /*响应式变量*/
88
+ const dragReactiveState = reactive(createDefaultDraggierReactiveState());
89
+
90
+ const start = (e: iMouseEvent | iTouchEvent, startIndex: number) => {
91
+
92
+ const itemElements = getItemElements();
93
+
94
+ dragStaticState.nodes = (() => {
95
+ let tempStart = 0;
96
+ return itemElements.map((item, index) => {
97
+ const start = tempStart;
98
+ const size = item[props.horizontal ? 'offsetWidth' : 'offsetHeight'];
99
+ const end = start + size;
100
+ tempStart += size;
101
+ return {
102
+ id: String(index),
103
+ size,
104
+ start,
105
+ end,
106
+ index,
107
+ item: dataModel.value?.[index],
108
+ };
109
+ });
110
+ })();
111
+
112
+ /*重置静态变量*/
113
+ Object.assign(dragReactiveState, createDefaultDraggierReactiveState());
114
+ /*重置响应式变量*/
115
+ Object.assign(dragReactiveState, createDefaultDraggierStaticState());
116
+
117
+ /*初始化部分静态变量*/
118
+ dragStaticState.dragElement = itemElements[startIndex];
119
+ if (!dragStaticState.dragElement) {
120
+ throw new Error(`Can't find [data-vid] elements!`);
121
+ }
122
+
123
+ dragStaticState.hostElement = findParentElement(dragStaticState.dragElement, el => hasClass(el, getComponentCls('scroll')))!;
124
+ if (!dragStaticState.hostElement) {
125
+ throw new Error(`Can't find host elements`);
126
+ }
127
+ dragStaticState.strutElement = findParentElement(dragStaticState.dragElement, el => hasClass(el, 'scroll-content'))!;
128
+ if (!dragStaticState.strutElement) {
129
+ throw new Error(`Can't find strut elements`);
130
+ }
131
+
132
+ dragStaticState.initDragElementRect = getElementRect(dragStaticState.dragElement);
133
+ dragStaticState.dragIndex = startIndex;
134
+ if (dragStaticState.dragIndex == -1) {
135
+ throw new Error(`Can't find index of drag elements`);
136
+ }
137
+
138
+ draggier.start(e);
139
+ };
140
+
141
+ const update = () => {
142
+
143
+ const { strutElement } = dragStaticState;
144
+
145
+ const offsetStart = props.horizontal ?
146
+ draggier.staticState.moveX - getElementRect(strutElement!).left :
147
+ draggier.staticState.moveY - getElementRect(strutElement!).top;
148
+
149
+ const newPointIndex = dragStaticState.nodes.findIndex(node => node.start <= offsetStart && node.end > offsetStart);
150
+
151
+ /*值有效的时候才更新值,新值无效的话,就用老的值,因为如果超出边界的话就会得到无效的新值,此时用老值即可*/
152
+ if (newPointIndex != null && newPointIndex != -1) {
153
+ dragReactiveState.pointIndex = newPointIndex || 0;
154
+ }
155
+ };
156
+
157
+ const getItemStyles = (index: number) => {
158
+ const { dragIndex, initDragElementRect } = dragStaticState;
159
+ if (dragReactiveState.pointIndex == null || dragIndex == null) {
160
+ return;
161
+ }
162
+ const styles: StyleProperties = {};
163
+ styles.transition = 'transform ease 300ms';
164
+ if (dragReactiveState.pointIndex >= index && index > dragIndex) {
165
+ styles.transform = props.horizontal ?
166
+ `translateX(-${initDragElementRect?.width || 0}px)` :
167
+ `translateY(-${initDragElementRect?.height || 0}px)`;
168
+ } else if (dragReactiveState.pointIndex <= index && index < dragIndex) {
169
+ styles.transform = props.horizontal ?
170
+ `translateX(${initDragElementRect?.width || 0}px)` :
171
+ `translateY(${initDragElementRect?.height || 0}px)`;
172
+ } else {
173
+ styles.transform = props.horizontal ?
174
+ `translateX(0px)` :
175
+ `translateY(0px)`;
176
+ }
177
+ return styles;
178
+ };
179
+
180
+ const draggier = createWebDraggier({
181
+ horizontal: props.horizontal,
182
+ vertical: !props.horizontal,
183
+ onInit: () => {
184
+ },
185
+ onStart: (draggierData) => {
186
+
187
+ const { draggierEffects } = draggierData;
188
+
189
+ const { dragElement, initDragElementRect, hostElement } = dragStaticState;
190
+
191
+ if (!dragElement || !initDragElementRect || !hostElement) {
192
+ return;
193
+ }
194
+
195
+ const dragElementShadow = dragStaticState.dragElementShadow = dragElement.cloneNode(true) as any;
196
+
197
+ if (!dragElementShadow) {
198
+ return;
199
+ }
200
+
201
+ /*强制启用动画,避免被Scroll禁用*/
202
+ dragElementShadow.setAttribute('data-force-transition', 'true');
203
+ dragElementShadow.style.height = initDragElementRect.height + 'px';
204
+ dragElementShadow.style.width = initDragElementRect.width + 'px';
205
+ dragElementShadow.style.position = 'fixed';
206
+ dragElementShadow.style.top = initDragElementRect.top + 'px';
207
+ dragElementShadow.style.left = initDragElementRect.left + 'px';
208
+ dragElementShadow.style.pointerEvents = 'none';
209
+ dragElementShadow.style.transition = 'none';
210
+
211
+ /*添加克隆节点*/
212
+ hostElement.appendChild(dragElementShadow);
213
+ draggierEffects.push(async () => {
214
+ if (!dragElementShadow || !hostElement) {
215
+ return;
216
+ }
217
+ await dragReactiveState.endingDefer.promise;
218
+ await delay(23);
219
+ hostElement.removeChild(dragElementShadow);
220
+ dragStaticState.dragElementShadow = null as any;
221
+ });
222
+
223
+ /*隐藏原始对象*/
224
+ dragStaticState.oldDragElementStyles.opacity = dragElement.style.opacity;
225
+ dragElement.style.opacity = "0";
226
+ draggierEffects.push(async () => {
227
+ if (!dragElement) {
228
+ return;
229
+ }
230
+ await dragReactiveState.endingDefer.promise;
231
+ await delay(23);
232
+ dragElement.style.opacity = dragStaticState.oldDragElementStyles.opacity;
233
+ dragStaticState.dragElement = null as any;
234
+ });
235
+
236
+ /*监听滚动*/
237
+ dragReactiveState.onScroll = (e: Event) => {
238
+ update();
239
+ };
240
+ const ejectOnScroll = getScroll()!.on.onScroll(dragReactiveState.onScroll);
241
+ draggierEffects.push(() => {
242
+ ejectOnScroll();
243
+ dragReactiveState.onScroll = doNothing;
244
+ });
245
+
246
+ /*拖拽结束的时候停止自动滚动*/
247
+ draggierEffects.push(() => getScroll()?.methods.stopAutoScroll());
248
+ draggierEffects.push(async () => {
249
+ await dragReactiveState.endingDefer.promise;
250
+ dragReactiveState.pointIndex = null;
251
+ });
252
+
253
+ /*标记拖拽中响应式变量*/
254
+ dragReactiveState.isDragging = true;
255
+ draggierEffects.push(async () => {
256
+ await dragReactiveState.endingDefer.promise;
257
+ dragReactiveState.isDragging = false;
258
+ });
259
+
260
+ /*操作结束之后的更新数据动作*/
261
+ draggierEffects.push(async () => {
262
+ const { strutElement, dragIndex } = dragStaticState;
263
+ if (!initDragElementRect || dragReactiveState.pointIndex == null || !dragElementShadow || !strutElement || dragIndex == null) {
264
+ return;
265
+ }
266
+ // console.log('==>>', dragReactiveState.pointIndex);
267
+ const pointNode = dragStaticState.nodes[dragReactiveState.pointIndex];
268
+ // console.log(pointNode, dragElementShadow);
269
+ const afterTransition = () => {
270
+ if (dragReactiveState && dragReactiveState.pointIndex != null && dragIndex != null && dragReactiveState.pointIndex != dragIndex) {
271
+ const insertIndex = dragReactiveState.pointIndex;
272
+ const newList = [...dataModel.value || []];
273
+ const spliceArr = newList.splice(dragIndex, 1);
274
+ newList.splice(insertIndex, 0, ...spliceArr);
275
+ dataModel.value = (newList);
276
+ }
277
+ dragReactiveState.endingDefer.resolve();
278
+ };
279
+
280
+ const newStart = Math.ceil((() => {
281
+ if (props.horizontal) {
282
+ return dragReactiveState.pointIndex < dragIndex ?
283
+ (getElementRect(strutElement).left + pointNode.start) :
284
+ (getElementRect(strutElement).left + pointNode.end - initDragElementRect.width);
285
+ } else {
286
+ return dragReactiveState.pointIndex < dragIndex ?
287
+ (getElementRect(strutElement).top + pointNode.start) :
288
+ (getElementRect(strutElement).top + pointNode.end - initDragElementRect.height);
289
+ }
290
+ })()) + 'px';
291
+
292
+ const styleName = props.horizontal ? 'left' : 'top';
293
+
294
+ // console.log(newStart, dragElementShadow.style[styleName]);
295
+
296
+ if (dragElementShadow.style[styleName] == newStart) {
297
+ /*位置没有变化,直接更新数据,因为值没有变化,导致就算复制也不会触发transition动画*/
298
+ afterTransition();
299
+ } else {
300
+ /*位置有变化,等transition动画结束之后再更新数据*/
301
+ dragElementShadow.style.transition = 'all ease 300ms';
302
+ dragElementShadow.addEventListener('transitionend', async () => {
303
+ afterTransition();
304
+ });
305
+ }
306
+ dragElementShadow.style[styleName] = newStart;
307
+ });
308
+
309
+ onDraggierStart?.({ ...draggierData, dragStaticState, dragReactiveState });
310
+ },
311
+ onMove: (draggierData) => {
312
+
313
+ const { staticState } = draggierData;
314
+
315
+ const { dragElementShadow, initDragElementRect, hostElement, strutElement } = dragStaticState;
316
+
317
+ if (!dragElementShadow || !initDragElementRect || !hostElement || !strutElement) {
318
+ return;
319
+ }
320
+
321
+ const strutRect = getElementRect(strutElement);
322
+ const hostRect = getElementRect(hostElement);
323
+
324
+ const newStart = props.horizontal ?
325
+ initDragElementRect.left + staticState.durX :
326
+ initDragElementRect.top + staticState.durY;
327
+ const minStart = props.horizontal ?
328
+ getElementRect(hostElement).left :
329
+ getElementRect(hostElement).top;
330
+ const maxStart1 = props.horizontal ?
331
+ strutRect.left + strutRect.width - initDragElementRect.width :
332
+ strutRect.top + strutRect.height - initDragElementRect.height;
333
+ const maxStart2 = props.horizontal ?
334
+ hostRect.left + hostRect.width - initDragElementRect.width :
335
+ hostRect.top + hostRect.height - initDragElementRect.height;
336
+ const maxStart = Math.min(maxStart1, maxStart2);
337
+
338
+ const resultStart = Math.max(minStart, Math.min(maxStart, newStart));
339
+
340
+ if (resultStart == minStart) {
341
+ autoScrollManager.set(props.horizontal ? 'left' : 'top');
342
+ } else if (resultStart == maxStart) {
343
+ autoScrollManager.set(props.horizontal ? 'right' : 'bottom');
344
+ } else {
345
+ autoScrollManager.set(null);
346
+ }
347
+
348
+ const styleName = props.horizontal ? 'left' : 'top';
349
+ dragElementShadow.style[styleName] = resultStart + 'px';
350
+
351
+ update();
352
+
353
+ onDraggierMove?.({ ...draggierData, dragStaticState, dragReactiveState });
354
+ },
355
+ onEnd: (draggierData) => {
356
+ onDraggierEnd?.({ ...draggierData, dragStaticState, dragReactiveState });
357
+ },
358
+ onTap: (draggierData) => {
359
+ onDraggierTap?.({ ...draggierData, dragStaticState, dragReactiveState });
360
+ },
361
+ });
362
+
363
+ const emitScroll = (e: any) => {
364
+ dragReactiveState.onScroll(e);
365
+ };
366
+
367
+ return {
368
+ dragStaticState,
369
+ dragReactiveState,
370
+ start,
371
+ getItemStyles,
372
+ emitScroll,
373
+ };
374
+ }
@@ -222,6 +222,7 @@ export {createRequestInterceptor} from './components/createRequestInterceptor';
222
222
  export type{iRequestInterceptor} from './components/createRequestInterceptor';
223
223
  export {createVirtualDraggier} from './components/createVirtualDraggier';
224
224
  export {createWebDraggier} from './components/createWebDraggier';
225
+ export {createScrollDraggier} from './components/createScrollDraggier';
225
226
 
226
227
  // @ts-ignore
227
228
  setComponentPrefix(globalComponentPrefix);