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

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": "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);