@douyinfe/semi-foundation 2.67.2 → 2.68.0-beta.0

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.
Files changed (44) hide show
  1. package/datePicker/foundation.ts +1 -1
  2. package/lib/cjs/datePicker/foundation.d.ts +1 -1
  3. package/lib/cjs/resizable/constants.d.ts +5 -0
  4. package/lib/cjs/resizable/constants.js +11 -0
  5. package/lib/cjs/resizable/foundation.d.ts +4 -0
  6. package/lib/cjs/resizable/foundation.js +37 -0
  7. package/lib/cjs/resizable/group/index.d.ts +50 -0
  8. package/lib/cjs/resizable/group/index.js +271 -0
  9. package/lib/cjs/resizable/groupConstants.d.ts +16 -0
  10. package/lib/cjs/resizable/groupConstants.js +25 -0
  11. package/lib/cjs/resizable/resizable.css +34 -0
  12. package/lib/cjs/resizable/resizable.scss +39 -0
  13. package/lib/cjs/resizable/single/index.d.ts +70 -0
  14. package/lib/cjs/resizable/single/index.js +574 -0
  15. package/lib/cjs/resizable/singleConstants.d.ts +105 -0
  16. package/lib/cjs/resizable/singleConstants.js +67 -0
  17. package/lib/cjs/resizable/utils.d.ts +20 -0
  18. package/lib/cjs/resizable/utils.js +142 -0
  19. package/lib/es/datePicker/foundation.d.ts +1 -1
  20. package/lib/es/resizable/constants.d.ts +5 -0
  21. package/lib/es/resizable/constants.js +6 -0
  22. package/lib/es/resizable/foundation.d.ts +4 -0
  23. package/lib/es/resizable/foundation.js +4 -0
  24. package/lib/es/resizable/group/index.d.ts +50 -0
  25. package/lib/es/resizable/group/index.js +262 -0
  26. package/lib/es/resizable/groupConstants.d.ts +16 -0
  27. package/lib/es/resizable/groupConstants.js +19 -0
  28. package/lib/es/resizable/resizable.css +34 -0
  29. package/lib/es/resizable/resizable.scss +39 -0
  30. package/lib/es/resizable/single/index.d.ts +70 -0
  31. package/lib/es/resizable/single/index.js +565 -0
  32. package/lib/es/resizable/singleConstants.d.ts +105 -0
  33. package/lib/es/resizable/singleConstants.js +61 -0
  34. package/lib/es/resizable/utils.d.ts +20 -0
  35. package/lib/es/resizable/utils.js +124 -0
  36. package/package.json +3 -3
  37. package/resizable/constants.ts +13 -0
  38. package/resizable/foundation.ts +31 -0
  39. package/resizable/group/index.ts +293 -0
  40. package/resizable/groupConstants.ts +25 -0
  41. package/resizable/resizable.scss +39 -0
  42. package/resizable/single/index.ts +629 -0
  43. package/resizable/singleConstants.ts +127 -0
  44. package/resizable/utils.ts +145 -0
@@ -0,0 +1,25 @@
1
+ // group
2
+ const rowStyleBase = {
3
+ width: '100%',
4
+ height: '8px',
5
+ flexShrink: 0,
6
+ margin: '0',
7
+ cursor: 'row-resize',
8
+ } as const;
9
+ const colStyleBase = {
10
+ width: '8px',
11
+ flexShrink: 0,
12
+ height: '100%',
13
+ margin: '0',
14
+ cursor: 'col-resize',
15
+ } as const;
16
+
17
+ export const directionStyles = {
18
+ horizontal: {
19
+ ...colStyleBase,
20
+ },
21
+ vertical: {
22
+ ...rowStyleBase,
23
+ }
24
+ } as const;
25
+
@@ -0,0 +1,39 @@
1
+ $module: #{$prefix}-resizable;
2
+
3
+ .#{$module} {
4
+ &-resizable {
5
+ position: relative;
6
+ box-sizing: border-box;
7
+ flex-shrink: 0;
8
+ }
9
+
10
+ &-resizableHandler {
11
+ position: absolute;
12
+ user-select: none;
13
+ z-index: $z-resizable_handler;
14
+ }
15
+
16
+ &-group {
17
+ display: flex;
18
+ position: relative;
19
+ box-sizing: border-box;
20
+ height: 100%;
21
+ width: 100%;
22
+ }
23
+
24
+ &-item {
25
+ position: relative;
26
+ box-sizing: border-box;
27
+ flex-shrink: 0;
28
+ }
29
+
30
+ &-handler {
31
+ user-select: none;
32
+ z-index: $z-resizable_handler;
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ background-color: var(--semi-color-fill-0);
37
+ opacity: 1;
38
+ }
39
+ }
@@ -0,0 +1,629 @@
1
+ import BaseFoundation, { DefaultAdapter } from '../../base/foundation';
2
+ import { DEFAULT_SIZE, Size, NumberSize, Direction, NewSize } from "../singleConstants";
3
+ import { getStringSize, getNumberSize, has, calculateNewMax, findNextSnap, snap, clamp } from "../utils";
4
+ export interface ResizableHandlerAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
5
+ registerEvent: () => void;
6
+ unregisterEvent: () => void
7
+ }
8
+
9
+ export class ResizableHandlerFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<ResizableHandlerAdapter<P, S>, P, S> {
10
+ constructor(adapter: ResizableHandlerAdapter<P, S>) {
11
+ super({ ...adapter });
12
+ }
13
+
14
+ init(): void {
15
+ this._adapter.registerEvent();
16
+ }
17
+
18
+ onMouseDown = (e: MouseEvent) => {
19
+ this.getProp('onResizeStart')(e, this.getProp('direction'));
20
+ };
21
+
22
+ destroy(): void {
23
+ this._adapter.unregisterEvent();
24
+ }
25
+ }
26
+
27
+ export interface ResizableAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
28
+ getResizable: () => HTMLDivElement | null;
29
+ registerEvent: () => void;
30
+ unregisterEvent: () => void
31
+ }
32
+
33
+ export class ResizableFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<ResizableAdapter<P, S>, P, S> {
34
+ constructor(adapter: ResizableAdapter<P, S>) {
35
+ super({ ...adapter });
36
+ }
37
+
38
+ init(): void {
39
+ if (!this.resizable || !this.window) {
40
+ return;
41
+ }
42
+ const flexBasis = this.window.getComputedStyle(this.resizable).flexBasis;
43
+
44
+ this.setState({
45
+ width: this.propSize.width,
46
+ height: this.propSize.height,
47
+ flexBasis: flexBasis !== 'auto' ? flexBasis : undefined,
48
+ } as any);
49
+
50
+ this.onResizeStart = this.onResizeStart.bind(this);
51
+ this.onMouseMove = this.onMouseMove.bind(this);
52
+ this.onMouseUp = this.onMouseUp.bind(this);
53
+ }
54
+
55
+ flexDirection?: 'row' | 'column';
56
+
57
+ lockAspectRatio = 1;
58
+ resizable: HTMLElement | null = null;
59
+
60
+ parentLeft = 0;
61
+ parentTop = 0;
62
+
63
+ boundaryLeft = 0;
64
+ boundaryRight = 0;
65
+ boundaryTop = 0;
66
+ boundaryBottom = 0;
67
+
68
+ targetLeft = 0;
69
+ targetTop = 0;
70
+
71
+ get parent(): HTMLElement | null {
72
+ if (!this.resizable) {
73
+ return null;
74
+ }
75
+ return this.resizable.parentNode as HTMLElement;
76
+ }
77
+
78
+ get window(): Window | null {
79
+ if (!this.resizable) {
80
+ return null;
81
+ }
82
+ if (!this.resizable.ownerDocument) {
83
+ return null;
84
+ }
85
+ return this.resizable.ownerDocument.defaultView as Window;
86
+ }
87
+
88
+ get propSize(): Size {
89
+ const porps = this.getProps();
90
+ return porps.size || porps.defaultSize || DEFAULT_SIZE;
91
+ }
92
+
93
+ get size(): NumberSize {
94
+ let width = 0;
95
+ let height = 0;
96
+ if (this.resizable && this.window) {
97
+ width = this.resizable.offsetWidth ;
98
+ height = this.resizable.offsetHeight ;
99
+ }
100
+ return { width, height };
101
+ }
102
+
103
+ get sizeStyle(): { width: string; height: string } {
104
+ const size = this.getProp('size');
105
+ const getSize = (property: 'width' | 'height'): string => {
106
+ const value = this.getStates()[property];
107
+ if (typeof value === 'undefined' || value === 'auto') {
108
+ return 'auto';
109
+ }
110
+ const propSizeValue = this.propSize?.[property];
111
+
112
+ if (propSizeValue?.toString().endsWith('%')) {
113
+ if (value.toString().endsWith('%')) {
114
+ return value.toString();
115
+ }
116
+
117
+ const parentSize = this.getParentSize();
118
+ const numberValue = Number(value.toString().replace('px', ''));
119
+ const percentValue = (numberValue / parentSize[property]) * 100;
120
+
121
+ return `${percentValue}%`;
122
+ }
123
+
124
+ return getStringSize(value);
125
+ };
126
+
127
+ const isResizing = this.getStates().isResizing;
128
+
129
+ const width = size && typeof size.width !== 'undefined' && !isResizing
130
+ ? getStringSize(size.width)
131
+ : getSize('width');
132
+
133
+ const height = size && typeof size.height !== 'undefined' && !isResizing
134
+ ? getStringSize(size.height)
135
+ : getSize('height');
136
+
137
+ return { width, height };
138
+ }
139
+
140
+ getParentSize(): { width: number; height: number } {
141
+ const appendPseudo = () => {
142
+ if (!this.resizable || !this.window) {
143
+ return null;
144
+ }
145
+ const parent = this.parent;
146
+ if (!parent) {
147
+ return null;
148
+ }
149
+ const pseudoEle = this.window.document.createElement('div');
150
+ pseudoEle.style.width = '100%';
151
+ pseudoEle.style.height = '100%';
152
+ pseudoEle.style.position = 'absolute';
153
+ pseudoEle.style.transform = 'scale(0, 0)';
154
+ pseudoEle.style.left = '0';
155
+ pseudoEle.style.flex = '0 0 100%';
156
+ parent.appendChild(pseudoEle);
157
+ return pseudoEle;
158
+ };
159
+
160
+ const removePseudo = (pseudo: HTMLElement) => {
161
+ const parent = this.parent;
162
+ if (!parent) {
163
+ return;
164
+ }
165
+ parent.removeChild(pseudo);
166
+ };
167
+ if (!this.parent) {
168
+ if (!this.window) {
169
+ return { width: 0, height: 0 };
170
+ }
171
+ return { width: this.window.innerWidth, height: this.window.innerHeight };
172
+ }
173
+ const pseudoElement = appendPseudo();
174
+
175
+ if (!pseudoElement) {
176
+ return { width: 0, height: 0 };
177
+ }
178
+
179
+ let flexWrapChanged = false;
180
+ const originalFlexWrap = this.parent.style.flexWrap;
181
+
182
+ if (originalFlexWrap !== 'wrap') {
183
+ flexWrapChanged = true;
184
+ this.parent.style.flexWrap = 'wrap';
185
+ }
186
+
187
+ pseudoElement.style.position = 'relative';
188
+ pseudoElement.style.minWidth = '100%';
189
+ pseudoElement.style.minHeight = '100%';
190
+
191
+ const size = {
192
+ width: pseudoElement.offsetWidth,
193
+ height: pseudoElement.offsetHeight,
194
+ };
195
+
196
+ if (flexWrapChanged) {
197
+ this.parent.style.flexWrap = originalFlexWrap;
198
+ }
199
+
200
+ removePseudo(pseudoElement);
201
+ return size;
202
+ }
203
+
204
+ registerEvents() {
205
+ this._adapter.registerEvent();
206
+ }
207
+
208
+ unregisterEvents() {
209
+ this._adapter.unregisterEvent();
210
+ }
211
+
212
+ getCssPropertySize(newSize: number | string, property: 'width' | 'height'): number | string {
213
+ const propSizeValue = this.propSize?.[property];
214
+ const state = this.getStates();
215
+
216
+ const isAutoSize =
217
+ state[property] === 'auto' &&
218
+ state.original[property] === newSize &&
219
+ (typeof propSizeValue === 'undefined' || propSizeValue === 'auto');
220
+
221
+ return isAutoSize ? 'auto' : newSize;
222
+ }
223
+
224
+
225
+ calBoundaryMax(maxWidth?: number, maxHeight?: number) {
226
+ const { boundsByDirection } = this.getProps();
227
+ const { direction } = this.getStates();
228
+
229
+ const isWidthConstrained = boundsByDirection && has('left', direction);
230
+ const isHeightConstrained = boundsByDirection && has('top', direction);
231
+
232
+ let maxWidthConstraint: number;
233
+ let maxHeightConstraint: number;
234
+
235
+ const props = this.getProps();
236
+
237
+ if (props.boundElement === 'parent') {
238
+ const parentElement = this.parent;
239
+ if (parentElement) {
240
+ maxWidthConstraint = isWidthConstrained
241
+ ? this.boundaryRight - this.parentLeft
242
+ : parentElement.offsetWidth + (this.parentLeft - this.boundaryLeft);
243
+
244
+ maxHeightConstraint = isHeightConstrained
245
+ ? this.boundaryBottom - this.parentTop
246
+ : parentElement.offsetHeight + (this.parentTop - this.boundaryTop);
247
+ }
248
+ } else if (props.boundElement === 'window' && this.window) {
249
+ maxWidthConstraint = isWidthConstrained
250
+ ? this.boundaryRight
251
+ : this.window.innerWidth - this.boundaryLeft;
252
+ maxHeightConstraint = isHeightConstrained
253
+ ? this.boundaryBottom
254
+ : this.window.innerHeight - this.boundaryTop;
255
+ } else if (props.boundElement) {
256
+ const boundary = props.boundElement;
257
+
258
+ maxWidthConstraint = isWidthConstrained
259
+ ? this.boundaryRight - this.targetLeft
260
+ : boundary.offsetWidth + (this.targetLeft - this.boundaryLeft);
261
+ maxHeightConstraint = isHeightConstrained
262
+ ? this.boundaryBottom - this.targetTop
263
+ : boundary.offsetHeight + (this.targetTop - this.boundaryTop);
264
+ }
265
+
266
+ if (maxWidthConstraint && Number.isFinite(maxWidthConstraint)) {
267
+ maxWidth = maxWidth && maxWidth < maxWidthConstraint ? maxWidth : maxWidthConstraint;
268
+ }
269
+ if (maxHeightConstraint && Number.isFinite(maxHeightConstraint)) {
270
+ maxHeight = maxHeight && maxHeight < maxHeightConstraint ? maxHeight : maxHeightConstraint;
271
+ }
272
+
273
+ return { maxWidth, maxHeight };
274
+ }
275
+
276
+ calDirectionSize(clientX: number, clientY: number) {
277
+ const props = this.getProps();
278
+ const scale = props.scale || 1;
279
+ let aspectRatio = props.ratio;
280
+ const [resizeRatioX, resizeRatioY] = Array.isArray(aspectRatio) ? aspectRatio : [aspectRatio, aspectRatio];
281
+
282
+ const { direction, original } = this.getStates();
283
+ const { lockAspectRatio, lockAspectRatioExtraHeight = 0, lockAspectRatioExtraWidth = 0 } = props;
284
+
285
+ let newWidth = original.width;
286
+ let newHeight = original.height;
287
+
288
+ const calculateNewWidth = (deltaX: number) => original.width + (deltaX * resizeRatioX) / scale;
289
+ const calculateNewHeight = (deltaY: number) => original.height + (deltaY * resizeRatioY) / scale;
290
+
291
+ if (has('top', direction)) {
292
+ newHeight = calculateNewHeight(original.y - clientY);
293
+ if (lockAspectRatio) {
294
+ newWidth = (newHeight - lockAspectRatioExtraHeight) * this.lockAspectRatio + lockAspectRatioExtraWidth;
295
+ }
296
+ }
297
+ if (has('bottom', direction)) {
298
+ newHeight = calculateNewHeight(clientY - original.y);
299
+ if (lockAspectRatio) {
300
+ newWidth = (newHeight - lockAspectRatioExtraHeight) * this.lockAspectRatio + lockAspectRatioExtraWidth;
301
+ }
302
+ }
303
+ if (has('right', direction)) {
304
+ newWidth = calculateNewWidth(clientX - original.x);
305
+ if (lockAspectRatio) {
306
+ newHeight = (newWidth - lockAspectRatioExtraWidth) / this.lockAspectRatio + lockAspectRatioExtraHeight;
307
+ }
308
+ }
309
+ if (has('left', direction)) {
310
+ newWidth = calculateNewWidth(original.x - clientX);
311
+ if (lockAspectRatio) {
312
+ newHeight = (newWidth - lockAspectRatioExtraWidth) / this.lockAspectRatio + lockAspectRatioExtraHeight;
313
+ }
314
+ }
315
+
316
+ return { newWidth, newHeight };
317
+ }
318
+
319
+ calAspectRatioSize(
320
+ newWidth: number,
321
+ newHeight: number,
322
+ max: { width?: number; height?: number },
323
+ min: { width?: number; height?: number },
324
+ ) {
325
+ const { lockAspectRatio, lockAspectRatioExtraHeight = 0, lockAspectRatioExtraWidth = 0 } = this.getProps();
326
+
327
+ const minWidth = typeof min.width === 'undefined' ? 10 : min.width;
328
+ const maxWidth = typeof max.width === 'undefined' || max.width < 0 ? newWidth : max.width;
329
+ const minHeight = typeof min.height === 'undefined' ? 10 : min.height;
330
+ const maxHeight = typeof max.height === 'undefined' || max.height < 0 ? newHeight : max.height;
331
+
332
+ if (lockAspectRatio) {
333
+ const adjustedMinWidth = (minHeight - lockAspectRatioExtraHeight) * this.lockAspectRatio + lockAspectRatioExtraWidth;
334
+ const adjustedMaxWidth = (maxHeight - lockAspectRatioExtraHeight) * this.lockAspectRatio + lockAspectRatioExtraWidth;
335
+ const adjustedMinHeight = (minWidth - lockAspectRatioExtraWidth) / this.lockAspectRatio + lockAspectRatioExtraHeight;
336
+ const adjustedMaxHeight = (maxWidth - lockAspectRatioExtraWidth) / this.lockAspectRatio + lockAspectRatioExtraHeight;
337
+
338
+ const lockedMinWidth = Math.max(minWidth, adjustedMinWidth);
339
+ const lockedMaxWidth = Math.min(maxWidth, adjustedMaxWidth);
340
+ const lockedMinHeight = Math.max(minHeight, adjustedMinHeight);
341
+ const lockedMaxHeight = Math.min(maxHeight, adjustedMaxHeight);
342
+
343
+ newWidth = clamp(newWidth, lockedMinWidth, lockedMaxWidth);
344
+ newHeight = clamp(newHeight, lockedMinHeight, lockedMaxHeight);
345
+ } else {
346
+ newWidth = clamp(newWidth, minWidth, maxWidth);
347
+ newHeight = clamp(newHeight, minHeight, maxHeight);
348
+ }
349
+ return { newWidth, newHeight };
350
+ }
351
+
352
+ setBoundary() {
353
+ const props = this.getProps();
354
+
355
+ // Set parent boundary
356
+ if (props.boundElement === 'parent') {
357
+ const parentElement = this.parent;
358
+ if (parentElement) {
359
+ const parentRect = parentElement.getBoundingClientRect();
360
+ this.parentLeft = parentRect.left;
361
+ this.parentTop = parentRect.top;
362
+ }
363
+ }
364
+
365
+ // Set target (HTML element) boundary
366
+ if (props.boundElement && typeof props.boundElement !== 'string') {
367
+ const targetRect = props.boundElement.getBoundingClientRect();
368
+ this.targetLeft = targetRect.left;
369
+ this.targetTop = targetRect.top;
370
+ }
371
+
372
+ // Set resizable boundary
373
+ if (this.resizable) {
374
+ const { left, top, right, bottom } = this.resizable.getBoundingClientRect();
375
+ this.boundaryLeft = left;
376
+ this.boundaryRight = right;
377
+ this.boundaryTop = top;
378
+ this.boundaryBottom = bottom;
379
+ }
380
+ }
381
+
382
+
383
+ onResizeStart = (e: MouseEvent, direction: Direction) => {
384
+ this.resizable = this._adapter.getResizable();
385
+ if (!this.resizable || !this.window) {
386
+ return;
387
+ }
388
+
389
+ const { clientX, clientY } = e;
390
+ const props = this.getProps();
391
+ const states = this.getStates();
392
+
393
+ // Call onResizeStart callback if defined
394
+ if (props.onResizeStart) {
395
+ const shouldContinue = props.onResizeStart(e, direction);
396
+ if (shouldContinue === false) {
397
+ return;
398
+ }
399
+ }
400
+
401
+ // Update state with new size if defined
402
+ const { size } = props;
403
+ if (size) {
404
+ const { height, width } = size;
405
+ const { height: currentHeight, width: currentWidth } = states;
406
+
407
+ if (height !== undefined && height !== currentHeight) {
408
+ this.setState({ height } as any);
409
+ }
410
+
411
+ if (width !== undefined && width !== currentWidth) {
412
+ this.setState({ width } as any);
413
+ }
414
+ }
415
+
416
+ // Handle aspect ratio locking
417
+ this.lockAspectRatio = typeof props.lockAspectRatio === 'number'
418
+ ? props.lockAspectRatio
419
+ : this.size.width / this.size.height;
420
+
421
+ // Determine flexBasis if applicable
422
+ let flexBasis: string | undefined;
423
+ const computedStyle = this.window.getComputedStyle(this.resizable);
424
+ if (computedStyle.flexBasis !== 'auto') {
425
+ const parent = this.parent;
426
+ if (parent) {
427
+ const parentStyle = this.window.getComputedStyle(parent);
428
+ this.flexDirection = parentStyle.flexDirection.startsWith('row') ? 'row' : 'column';
429
+ flexBasis = computedStyle.flexBasis;
430
+ }
431
+ }
432
+
433
+ // Set bounding rectangle and register events
434
+ this.setBoundary();
435
+ this.registerEvents();
436
+
437
+ // Update state with initial resize values
438
+ const state = {
439
+ original: {
440
+ x: clientX,
441
+ y: clientY,
442
+ width: this.size.width,
443
+ height: this.size.height,
444
+ },
445
+ isResizing: true,
446
+ backgroundStyle: {
447
+ ...states.backgroundStyle,
448
+ cursor: this.window.getComputedStyle(e.target as HTMLElement).cursor || 'auto',
449
+ },
450
+ direction,
451
+ flexBasis,
452
+ };
453
+
454
+ this.setState(state as any);
455
+ }
456
+
457
+
458
+ onMouseMove = (event: MouseEvent) => {
459
+ const states = this.getStates();
460
+ const props = this.getProps();
461
+
462
+ if (!states.isResizing || !this.resizable || !this.window) {
463
+ return;
464
+ }
465
+
466
+ const { clientX, clientY } = event;
467
+ const { direction, original, width, height } = states;
468
+ const parentSize = this.getParentSize();
469
+ let { maxWidth, maxHeight, minWidth, minHeight } = props;
470
+
471
+ // Calculate max and min dimensions
472
+ const maxBounds = calculateNewMax(
473
+ parentSize,
474
+ this.window.innerWidth,
475
+ this.window.innerHeight,
476
+ maxWidth,
477
+ maxHeight,
478
+ minWidth,
479
+ minHeight
480
+ );
481
+
482
+ maxWidth = maxBounds.maxWidth;
483
+ maxHeight = maxBounds.maxHeight;
484
+ minWidth = maxBounds.minWidth;
485
+ minHeight = maxBounds.minHeight;
486
+
487
+ // Calculate new size based on direction
488
+ let { newWidth, newHeight }: NewSize = this.calDirectionSize(clientX, clientY);
489
+
490
+ // Apply boundary constraints
491
+ const boundaryMax = this.calBoundaryMax(maxWidth, maxHeight);
492
+ newWidth = getNumberSize(newWidth, parentSize.width, this.window.innerWidth, this.window.innerHeight);
493
+ newHeight = getNumberSize(newHeight, parentSize.height, this.window.innerWidth, this.window.innerHeight);
494
+
495
+ // Apply snapping
496
+ if (props.snap) {
497
+ if (props.snap.x) {
498
+ newWidth = findNextSnap(newWidth, props.snap.x, props.snapGap);
499
+ }
500
+ if (props.snap.y) {
501
+ newHeight = findNextSnap(newHeight, props.snap.y, props.snapGap);
502
+ }
503
+ }
504
+
505
+ // Adjust size based on aspect ratio
506
+ const sizeFromAspectRatio = this.calAspectRatioSize(
507
+ newWidth,
508
+ newHeight,
509
+ { width: boundaryMax.maxWidth, height: boundaryMax.maxHeight },
510
+ { width: minWidth, height: minHeight }
511
+ );
512
+ newWidth = sizeFromAspectRatio.newWidth;
513
+ newHeight = sizeFromAspectRatio.newHeight;
514
+
515
+ // Apply grid snapping if defined
516
+ if (props.grid) {
517
+ const [gridW, gridH] = Array.isArray(props.grid) ? props.grid : [props.grid, props.grid];
518
+ const gap = props.snapGap || 0;
519
+ const newGridWidth = snap(newWidth, gridW);
520
+ const newGridHeight = snap(newHeight, gridH);
521
+ newWidth = gap === 0 || Math.abs(newGridWidth - newWidth) <= gap ? newGridWidth : newWidth;
522
+ newHeight = gap === 0 || Math.abs(newGridHeight - newHeight) <= gap ? newGridHeight : newHeight;
523
+ }
524
+
525
+ // Convert width and height to CSS units if needed
526
+ const convertToCssUnit = (size: number, originalSize: number, unit: string): string | number => {
527
+
528
+ if (unit.endsWith('%')) {
529
+ return `${(size / originalSize) * 100}%`;
530
+ } else if (unit.endsWith('vw')) {
531
+ return `${(size / this.window.innerWidth) * 100}vw`;
532
+ } else if (unit.endsWith('vh')) {
533
+ return `${(size / this.window.innerHeight) * 100}vh`;
534
+ }
535
+ return size;
536
+ };
537
+
538
+ if (typeof width === 'string') {
539
+ newWidth = convertToCssUnit(newWidth, parentSize.width, width || '');
540
+ }
541
+
542
+ if (typeof height === 'string') {
543
+ newHeight = convertToCssUnit(newHeight, parentSize.height, height || '');
544
+ }
545
+
546
+ // Create new state
547
+ const newState: { width: string | number; height: string | number; flexBasis?: string | number } = {
548
+ width: this.getCssPropertySize(newWidth, 'width'),
549
+ height: this.getCssPropertySize(newHeight, 'height')
550
+ };
551
+
552
+ if (this.flexDirection === 'row') {
553
+ newState.flexBasis = newState.width;
554
+ } else if (this.flexDirection === 'column') {
555
+ newState.flexBasis = newState.height;
556
+ }
557
+
558
+ // Check for changes
559
+ const widthChanged = states.width !== newState.width;
560
+ const heightChanged = states.height !== newState.height;
561
+ const flexBaseChanged = states.flexBasis !== newState.flexBasis;
562
+ const hasChanges = widthChanged || heightChanged || flexBaseChanged;
563
+
564
+ if (hasChanges) {
565
+ this.setState(newState as any);
566
+
567
+ // Call onChange callback if defined
568
+ if (props.onChange) {
569
+ let newSize = {
570
+ width: newState.width,
571
+ height: newState.height
572
+ };
573
+ props.onChange(newSize, event, direction);
574
+ }
575
+ const size = props.size;
576
+ if (size) {
577
+ this.setState({
578
+ width: size.width ?? 'auto',
579
+ height: size.height ?? 'auto'
580
+ } as any);
581
+ }
582
+ }
583
+ }
584
+
585
+
586
+ onMouseUp = (event: MouseEvent) => {
587
+ const { isResizing, direction, original } = this.getStates();
588
+
589
+ if (!isResizing || !this.resizable) {
590
+ return;
591
+ }
592
+
593
+ const { width: currentWidth, height: currentHeight } = this.size;
594
+ const delta = {
595
+ width: currentWidth - original.width,
596
+ height: currentHeight - original.height,
597
+ };
598
+
599
+ const { onResizeEnd, size } = this.getProps();
600
+
601
+ // Call onResizeEnd callback if defined
602
+ if (onResizeEnd) {
603
+ onResizeEnd(this.size, event, direction);
604
+ }
605
+
606
+ // Update state with new size if provided
607
+ if (size) {
608
+ this.setState({
609
+ width: size.width ?? 'auto',
610
+ height: size.height ?? 'auto'
611
+ } as any);
612
+ }
613
+
614
+ // Unregister events and update state
615
+ this.unregisterEvents();
616
+ this.setState({
617
+ isResizing: false,
618
+ backgroundStyle: {
619
+ ...this.getStates().backgroundStyle,
620
+ cursor: 'auto'
621
+ }
622
+ } as any);
623
+ }
624
+
625
+
626
+ destroy(): void {
627
+ this.unregisterEvents();
628
+ }
629
+ }