@gfazioli/mantine-split-pane 1.1.7 → 2.0.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.
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  import React, { useRef } from 'react';
3
- import { createVarsResolver, parseThemeColor, getSize, getThemeColor, getRadius, rgba, factory, useProps, useStyles, UnstyledButton } from '@mantine/core';
3
+ import { createVarsResolver, parseThemeColor, getSize, rgba, getRadius, factory, useProps, useStyles, UnstyledButton } from '@mantine/core';
4
+ import { useSplitContext } from '../Split.context.mjs';
4
5
  import classes from './SplitPaneResizer.module.css.mjs';
5
6
 
6
7
  const varsResolver = createVarsResolver(
@@ -31,14 +32,23 @@ const varsResolver = createVarsResolver(
31
32
  theme
32
33
  });
33
34
  const hoverColorDarkParsed = parseThemeColor({
34
- color: hoverColor || "dark.3",
35
+ color: hoverColor || theme.primaryColor,
35
36
  theme
36
37
  });
37
38
  const hoverColorLightParsed = parseThemeColor({
38
- color: hoverColor || "gray.4",
39
+ color: hoverColor || theme.primaryColor,
40
+ theme
41
+ });
42
+ const knobColorParsed = parseThemeColor({
43
+ color: knobColor || theme.primaryColor,
44
+ theme
45
+ });
46
+ const knobHoverColorParsed = parseThemeColor({
47
+ color: knobHoverColor || "white",
39
48
  theme
40
49
  });
41
50
  const knobVariant = variant === "dotted" || variant === "dashed";
51
+ const forceKnobOpacityValue = withKnob && knobAlwaysOn && !knobVariant ? knobOpacity : "0";
42
52
  return {
43
53
  root: {
44
54
  "--split-resizer-size": getSize(size, "split-resizer-size"),
@@ -49,11 +59,11 @@ const varsResolver = createVarsResolver(
49
59
  "--split-resizer-hover-color-dark": rgba(hoverColorDarkParsed.value, 1),
50
60
  "--split-resizer-radius": getRadius(radius),
51
61
  "--split-resizer-knob-size": getSize(knobSize, "split-resizer-knob-size"),
52
- "--split-resizer-knob-opacity": withKnob && knobAlwaysOn && !knobVariant ? knobOpacity : "0",
62
+ "--split-resizer-knob-opacity": forceKnobOpacityValue,
53
63
  "--split-resizer-knob-hover-opacity": withKnob || knobVariant ? "1" : "0",
54
64
  "--split-resizer-knob-radius": getRadius(knobRadius),
55
- "--split-resizer-knob-color": getThemeColor(knobColor, theme),
56
- "--split-resizer-knob-hover-color": getThemeColor(knobHoverColor, theme),
65
+ "--split-resizer-knob-color": rgba(knobColorParsed.value, Number(forceKnobOpacityValue)),
66
+ "--split-resizer-knob-hover-color": rgba(knobHoverColorParsed.value, 1),
57
67
  "--split-resizer-spacing": getSize(spacing, "split-resizer-spacing"),
58
68
  "--split-resizer-cursor-vertical": cursorVertical || "col-resize",
59
69
  "--split-resizer-cursor-horizontal": cursorHorizontal || "row-resize"
@@ -62,55 +72,52 @@ const varsResolver = createVarsResolver(
62
72
  }
63
73
  );
64
74
  const defaultProps = {
65
- size: "md",
75
+ orientation: "vertical",
66
76
  opacity: 0.8,
77
+ size: "sm",
67
78
  radius: "xs",
68
- knobColor: "blue",
79
+ withKnob: false,
80
+ knobAlwaysOn: true,
69
81
  knobSize: "sm",
70
82
  knobOpacity: 0.5,
71
83
  knobRadius: "sm",
72
- minWidth: 20,
73
- minHeight: 20,
74
- orientation: "vertical",
75
- paneRef: { current: null },
76
- variant: "default",
77
- withKnob: false,
78
- knobAlwaysOn: true,
84
+ knobColor: "white",
85
+ knobHoverColor: "white",
86
+ spacing: "xs",
79
87
  step: 8,
80
88
  shiftStep: 64,
81
89
  cursorVertical: "col-resize",
82
90
  cursorHorizontal: "row-resize"
83
91
  };
84
- const SplitPaneResizer = factory((_props, ref) => {
85
- const props = useProps("Resizer", defaultProps, _props);
92
+ const SplitPaneResizer = factory((_props, _) => {
93
+ const ctx = useSplitContext();
94
+ const props = useProps("SplitPaneResizer", { ...defaultProps, ...ctx }, _props);
86
95
  const {
87
- size,
96
+ orientation,
88
97
  opacity,
98
+ size,
89
99
  radius,
90
- color,
91
- hoverColor,
100
+ withKnob,
101
+ knobAlwaysOn,
92
102
  knobSize,
93
103
  knobOpacity,
94
104
  knobRadius,
95
105
  knobColor,
96
106
  knobHoverColor,
97
- minWidth,
98
- minHeight,
99
- maxWidth,
100
- maxHeight,
101
- orientation,
102
- onResizeStart,
103
- onResizing,
104
- onResizeEnd,
105
- paneRef,
106
- variant,
107
- withKnob,
108
- knobAlwaysOn,
109
107
  spacing,
110
108
  step,
111
109
  shiftStep,
112
110
  cursorVertical,
113
111
  cursorHorizontal,
112
+ color,
113
+ hoverColor,
114
+ variant,
115
+ onResizeStart,
116
+ onResizing,
117
+ onResizeEnd,
118
+ onDoubleClick,
119
+ __beforeRef: beforeRef,
120
+ __afterRef: afterRef,
114
121
  className,
115
122
  style,
116
123
  classNames,
@@ -118,7 +125,7 @@ const SplitPaneResizer = factory((_props, ref) => {
118
125
  unstyled,
119
126
  vars,
120
127
  mod,
121
- ...others
128
+ ...rest
122
129
  } = props;
123
130
  const getStyles = useStyles({
124
131
  name: "SplitPaneResizer",
@@ -133,6 +140,150 @@ const SplitPaneResizer = factory((_props, ref) => {
133
140
  varsResolver
134
141
  });
135
142
  const containerRef = useRef(null);
143
+ const processVerticalSize = (deltaX = 0) => {
144
+ const minBeforeWidth = beforeRef.current.getMinWidth();
145
+ const maxBeforeWidth = beforeRef.current.getMaxWidth();
146
+ const minAfterWidth = afterRef.current.getMinWidth();
147
+ const maxAfterWidth = afterRef.current.getMaxWidth();
148
+ const beforePane = beforeRef.current.splitPane;
149
+ const afterPane = afterRef.current.splitPane;
150
+ let beforeWidth = beforePane.getBoundingClientRect().width;
151
+ let afterWidth = afterPane.getBoundingClientRect().width;
152
+ const isBeforeWidthMaxExceeded = maxBeforeWidth && beforeWidth + deltaX > maxBeforeWidth;
153
+ const isAfterWidthMaxExceeded = maxAfterWidth && afterWidth - deltaX > maxAfterWidth;
154
+ const isBeforeWidthMinExceeded = minBeforeWidth && beforeWidth + deltaX < minBeforeWidth;
155
+ const isAfterWidthMinExceeded = minAfterWidth && afterWidth - deltaX < minAfterWidth;
156
+ const isBeforeWidthNegative = beforeWidth + deltaX < 0;
157
+ const isAfterWidthNegative = afterWidth - deltaX < 0;
158
+ function setVerticalSize() {
159
+ const beforeWidthString = `${beforeWidth}px`;
160
+ const afterWidthString = `${afterWidth}px`;
161
+ const beforePaneSizes = {
162
+ width: beforeWidth,
163
+ height: beforePane.getBoundingClientRect().height
164
+ };
165
+ const afterPaneSizes = {
166
+ width: afterWidth,
167
+ height: afterPane.getBoundingClientRect().height
168
+ };
169
+ beforeRef.current.onResizing?.(beforePaneSizes);
170
+ afterRef.current.onResizing?.(afterPaneSizes);
171
+ onResizing?.({
172
+ beforePane: beforePaneSizes,
173
+ afterPane: afterPaneSizes
174
+ });
175
+ beforePane.style.width = beforeWidthString;
176
+ afterPane.style.width = afterWidthString;
177
+ }
178
+ if (!isAfterWidthMaxExceeded && isBeforeWidthMinExceeded) {
179
+ afterWidth += beforeWidth - minBeforeWidth;
180
+ beforeWidth = minBeforeWidth;
181
+ return setVerticalSize();
182
+ }
183
+ if (!isAfterWidthMaxExceeded && isBeforeWidthNegative) {
184
+ afterWidth += beforeWidth;
185
+ beforeWidth = 0;
186
+ return setVerticalSize();
187
+ }
188
+ if (!isAfterWidthMinExceeded && !isAfterWidthNegative && isBeforeWidthMaxExceeded) {
189
+ afterWidth -= maxBeforeWidth - beforeWidth;
190
+ beforeWidth = maxBeforeWidth;
191
+ return setVerticalSize();
192
+ }
193
+ if (!isBeforeWidthMaxExceeded && isAfterWidthMinExceeded) {
194
+ beforeWidth += afterWidth - minAfterWidth;
195
+ afterWidth = minAfterWidth;
196
+ return setVerticalSize();
197
+ }
198
+ if (!isBeforeWidthMaxExceeded && isAfterWidthNegative) {
199
+ beforeWidth += afterWidth;
200
+ afterWidth = 0;
201
+ return setVerticalSize();
202
+ }
203
+ if (!isBeforeWidthMinExceeded && !isBeforeWidthNegative && isAfterWidthMaxExceeded) {
204
+ beforeWidth -= maxAfterWidth - afterWidth;
205
+ afterWidth = maxAfterWidth;
206
+ return setVerticalSize();
207
+ }
208
+ if (isBeforeWidthNegative || isAfterWidthNegative || isBeforeWidthMaxExceeded || isAfterWidthMaxExceeded || isBeforeWidthMinExceeded || isAfterWidthMinExceeded) {
209
+ return;
210
+ }
211
+ beforeWidth += deltaX;
212
+ afterWidth -= deltaX;
213
+ setVerticalSize();
214
+ };
215
+ const processHorizontalSize = (deltaY = 0) => {
216
+ const minBeforeHeight = beforeRef.current.getMinHeight();
217
+ const maxBeforeHeight = beforeRef.current.getMaxHeight();
218
+ const minAfterHeight = afterRef.current.getMinHeight();
219
+ const maxAfterHeight = afterRef.current.getMaxHeight();
220
+ const beforePane = beforeRef.current.splitPane;
221
+ const afterPane = afterRef.current.splitPane;
222
+ let beforeHeight = beforePane.getBoundingClientRect().height;
223
+ let afterHeight = afterPane.getBoundingClientRect().height;
224
+ const isBeforeHeightMaxExceeded = maxBeforeHeight && beforeHeight + deltaY > maxBeforeHeight;
225
+ const isAfterHeightMaxExceeded = maxAfterHeight && afterHeight - deltaY > maxAfterHeight;
226
+ const isBeforeHeightMinExceeded = minBeforeHeight && beforeHeight + deltaY < minBeforeHeight;
227
+ const isAfterHeightMinExceeded = minAfterHeight && afterHeight - deltaY < minAfterHeight;
228
+ const isBeforeHeightNegative = beforeHeight + deltaY < 0;
229
+ const isAfterHeightNegative = afterHeight - deltaY < 0;
230
+ function setHorizontalSize() {
231
+ const beforeHeightString = `${beforeHeight}px`;
232
+ const afterHeightString = `${afterHeight}px`;
233
+ const beforePaneSizes = {
234
+ width: beforePane.getBoundingClientRect().width,
235
+ height: beforeHeight
236
+ };
237
+ const afterPaneSizes = {
238
+ width: afterPane.getBoundingClientRect().width,
239
+ height: afterHeight
240
+ };
241
+ onResizing?.({
242
+ beforePane: beforePaneSizes,
243
+ afterPane: afterPaneSizes
244
+ });
245
+ beforeRef.current.onResizing?.(beforePaneSizes);
246
+ afterRef.current.onResizing?.(afterPaneSizes);
247
+ beforePane.style.height = beforeHeightString;
248
+ afterPane.style.height = afterHeightString;
249
+ }
250
+ if (!isAfterHeightMaxExceeded && isBeforeHeightMinExceeded) {
251
+ afterHeight += beforeHeight - minBeforeHeight;
252
+ beforeHeight = minBeforeHeight;
253
+ return setHorizontalSize();
254
+ }
255
+ if (!isAfterHeightMaxExceeded && isBeforeHeightNegative) {
256
+ afterHeight += beforeHeight;
257
+ beforeHeight = 0;
258
+ return setHorizontalSize();
259
+ }
260
+ if (!isAfterHeightMinExceeded && !isAfterHeightNegative && isBeforeHeightMaxExceeded) {
261
+ afterHeight -= maxBeforeHeight - beforeHeight;
262
+ beforeHeight = maxBeforeHeight;
263
+ return setHorizontalSize();
264
+ }
265
+ if (!isBeforeHeightMaxExceeded && isAfterHeightMinExceeded) {
266
+ beforeHeight += afterHeight - minAfterHeight;
267
+ afterHeight = minAfterHeight;
268
+ return setHorizontalSize();
269
+ }
270
+ if (!isBeforeHeightMaxExceeded && isAfterHeightNegative) {
271
+ beforeHeight += afterHeight;
272
+ afterHeight = 0;
273
+ return setHorizontalSize();
274
+ }
275
+ if (!isBeforeHeightMinExceeded && !isBeforeHeightNegative && isAfterHeightMaxExceeded) {
276
+ beforeHeight -= maxAfterHeight - afterHeight;
277
+ afterHeight = maxAfterHeight;
278
+ return setHorizontalSize();
279
+ }
280
+ if (isBeforeHeightNegative || isAfterHeightNegative || isBeforeHeightMaxExceeded || isAfterHeightMaxExceeded || isBeforeHeightMinExceeded || isAfterHeightMinExceeded) {
281
+ return;
282
+ }
283
+ beforeHeight += deltaY;
284
+ afterHeight -= deltaY;
285
+ setHorizontalSize();
286
+ };
136
287
  const handleStart = (event) => {
137
288
  event.preventDefault();
138
289
  event.stopPropagation();
@@ -147,115 +298,132 @@ const SplitPaneResizer = factory((_props, ref) => {
147
298
  document.addEventListener("touchend", handleTouchEnd);
148
299
  }
149
300
  onResizeStart?.();
150
- document.body.style.cursor = "col-resize";
301
+ beforeRef.current.onResizeStart?.();
302
+ afterRef.current.onResizeStart?.();
303
+ document.body.style.cursor = orientation === "vertical" ? cursorVertical : cursorHorizontal;
151
304
  };
152
305
  const handleMove = (event) => {
153
- if (!paneRef.current) return;
306
+ if (!beforeRef.current || !afterRef.current) {
307
+ throw new Error("beforeRef or afterRef is not defined");
308
+ }
154
309
  event.preventDefault();
155
310
  event.stopPropagation();
156
311
  const computedStyle = window.getComputedStyle(containerRef.current);
157
- const clientX = "clientX" in event ? event.clientX : event.touches[0].clientX;
158
- const clientY = "clientY" in event ? event.clientY : event.touches[0].clientY;
159
312
  if (orientation === "vertical") {
160
- const margin = parseFloat(computedStyle.getPropertyValue("margin-left")) + 1;
161
- const delta = clientX - paneRef.current.getBoundingClientRect().right - margin;
162
- const width = paneRef.current.getBoundingClientRect().width + delta;
163
- const widthString = `${width}px`;
164
- if (minWidth && width < minWidth) return;
165
- if (maxWidth && width > maxWidth) return;
166
- onResizing?.({
167
- width: widthString,
168
- height: paneRef.current.style.height
169
- });
170
- paneRef.current.style.width = widthString;
171
- } else {
172
- const margin = parseFloat(computedStyle.getPropertyValue("margin-top")) + 1;
173
- const delta = clientY - paneRef.current.getBoundingClientRect().bottom - margin;
174
- const height = paneRef.current.getBoundingClientRect().height + delta;
175
- const heightString = `${height}px`;
176
- if (minHeight && height < minHeight) return;
177
- if (maxHeight && height > maxHeight) return;
178
- onResizing?.({
179
- width: paneRef.current.style.width,
180
- height: heightString
181
- });
182
- paneRef.current.style.height = heightString;
313
+ const margin = parseFloat(computedStyle.getPropertyValue("margin-right")) - 1;
314
+ const clientX = "clientX" in event ? event.clientX : event.touches[0].clientX;
315
+ const deltaX = clientX - containerRef.current.getBoundingClientRect().left - margin;
316
+ return processVerticalSize(deltaX);
317
+ }
318
+ if (orientation === "horizontal") {
319
+ const margin = parseFloat(computedStyle.getPropertyValue("margin-bottom")) - 1;
320
+ const clientY = "clientY" in event ? event.clientY : event.touches[0].clientY;
321
+ const deltaY = clientY - containerRef.current.getBoundingClientRect().top - margin;
322
+ return processHorizontalSize(deltaY);
183
323
  }
184
324
  };
185
325
  const handleMouseUp = () => {
186
- if (!paneRef.current) return;
326
+ if (!beforeRef.current || !afterRef.current) {
327
+ throw new Error("beforeRef or afterRef is not defined");
328
+ }
187
329
  document.body.style.userSelect = "initial";
188
330
  document.body.style.webkitUserSelect = "initial";
189
331
  document.removeEventListener("mousemove", handleMove);
190
332
  document.removeEventListener("mouseup", handleMouseUp);
191
333
  document.body.style.cursor = "initial";
192
- const { width, height } = paneRef.current.style || {};
193
- onResizeEnd?.({ width, height });
334
+ const beforePane = beforeRef.current.splitPane;
335
+ const afterPane = afterRef.current.splitPane;
336
+ const beforePaneSizes = {
337
+ width: beforePane.getBoundingClientRect().width,
338
+ height: beforePane.getBoundingClientRect().height
339
+ };
340
+ const afterPaneSizes = {
341
+ width: afterPane.getBoundingClientRect().width,
342
+ height: afterPane.getBoundingClientRect().height
343
+ };
344
+ onResizeEnd?.({
345
+ beforePane: beforePaneSizes,
346
+ afterPane: afterPaneSizes
347
+ });
348
+ beforeRef.current.onResizeEnd?.(beforePaneSizes);
349
+ afterRef.current.onResizeEnd?.(afterPaneSizes);
194
350
  };
195
351
  const handleTouchEnd = () => {
196
- if (!paneRef.current) return;
352
+ if (!beforeRef.current || !afterRef.current) {
353
+ throw new Error("beforeRef or afterRef is not defined");
354
+ }
197
355
  document.removeEventListener("touchmove", handleMove);
198
356
  document.removeEventListener("touchend", handleTouchEnd);
199
357
  document.body.style.cursor = "initial";
200
- const { width, height } = paneRef.current.style || {};
201
- onResizeEnd?.({ width, height });
358
+ const beforePane = beforeRef.current.splitPane;
359
+ const afterPane = afterRef.current.splitPane;
360
+ const beforePaneSizes = {
361
+ width: beforePane.getBoundingClientRect().width,
362
+ height: beforePane.getBoundingClientRect().height
363
+ };
364
+ const afterPaneSizes = {
365
+ width: afterPane.getBoundingClientRect().width,
366
+ height: afterPane.getBoundingClientRect().height
367
+ };
368
+ onResizeEnd?.({
369
+ beforePane: beforePaneSizes,
370
+ afterPane: afterPaneSizes
371
+ });
372
+ beforeRef.current.onResizeEnd?.(beforePaneSizes);
373
+ afterRef.current.onResizeEnd?.(afterPaneSizes);
202
374
  };
203
375
  const handleKeyUp = (event) => {
204
376
  if (containerRef.current !== document.activeElement) {
205
377
  return;
206
378
  }
379
+ const code = event.nativeEvent.code;
380
+ const arrowLeftRight = code === "ArrowRight" || code === "ArrowLeft";
381
+ const arrowUpDown = code === "ArrowUp" || code === "ArrowDown";
207
382
  const delta = event.shiftKey ? shiftStep : step;
208
- if (orientation === "vertical" && (event.nativeEvent.code === "ArrowRight" || event.nativeEvent.code === "ArrowLeft")) {
383
+ if (orientation === "vertical" && arrowLeftRight) {
209
384
  event.preventDefault();
210
385
  event.stopPropagation();
211
- const deltaSign = event.nativeEvent.code === "ArrowRight" ? 1 : -1;
212
- const width = paneRef.current.getBoundingClientRect().width + delta * deltaSign;
213
- const widthString = `${width}px`;
214
- if (minWidth && width < minWidth) return;
215
- if (maxWidth && width > maxWidth) return;
216
- onResizing?.({
217
- width: widthString,
218
- height: paneRef.current.style.height
219
- });
220
- paneRef.current.style.width = widthString;
386
+ const deltaSign = code === "ArrowRight" ? 1 : -1;
387
+ const deltaX = delta * deltaSign;
388
+ return processVerticalSize(deltaX);
221
389
  }
222
- if (orientation === "horizontal" && (event.nativeEvent.code === "ArrowUp" || event.nativeEvent.code === "ArrowDown")) {
390
+ if (orientation === "horizontal" && arrowUpDown) {
223
391
  event.preventDefault();
224
392
  event.stopPropagation();
225
- const deltaSign = event.nativeEvent.code === "ArrowDown" ? 1 : -1;
226
- const height = paneRef.current.getBoundingClientRect().height + delta * deltaSign;
227
- const heightString = `${height}px`;
228
- if (minHeight && height < minHeight) return;
229
- if (maxHeight && height > maxHeight) return;
230
- onResizing?.({
231
- width: paneRef.current.style.width,
232
- height: heightString
233
- });
234
- paneRef.current.style.height = heightString;
393
+ const deltaSign = code === "ArrowDown" ? 1 : -1;
394
+ const deltaY = delta * deltaSign;
395
+ return processHorizontalSize(deltaY);
235
396
  }
236
- if (event.nativeEvent.code === "Escape") {
397
+ if (code === "Escape") {
237
398
  event.preventDefault();
238
399
  event.stopPropagation();
239
400
  containerRef.current.blur();
240
401
  }
241
402
  };
403
+ const handleDoubleClick = (e) => {
404
+ e.preventDefault();
405
+ e.stopPropagation();
406
+ beforeRef.current.resetInitialSize(e);
407
+ afterRef.current.resetInitialSize(e);
408
+ onDoubleClick?.(e);
409
+ };
242
410
  return /* @__PURE__ */ React.createElement(
243
411
  UnstyledButton,
244
412
  {
245
- onDoubleClick: props?.onDoubleClick,
246
413
  ref: containerRef,
247
414
  mod: { orientation },
248
415
  onMouseDown: handleStart,
249
416
  onKeyDown: handleKeyUp,
250
417
  onTouchStart: handleStart,
418
+ onDoubleClick: handleDoubleClick,
251
419
  "aria-label": "Resize",
252
- ...getStyles("root", { variant }),
253
- ...others
420
+ ...getStyles("root", { variant: variant || "default" }),
421
+ ...rest
254
422
  }
255
423
  );
256
424
  });
257
425
  SplitPaneResizer.classes = classes;
258
426
  SplitPaneResizer.displayName = "SplitPaneResizer";
259
427
 
260
- export { SplitPaneResizer };
428
+ export { SplitPaneResizer, defaultProps };
261
429
  //# sourceMappingURL=SplitPaneResizer.mjs.map