@tcn/ui 0.12.4 → 0.12.5
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/dist/overlay/frame/frame.d.ts.map +1 -1
- package/dist/overlay/frame/frame.js +57 -63
- package/dist/overlay/frame/frame.js.map +1 -1
- package/dist/stacks/box/box.d.ts +5 -4
- package/dist/stacks/box/box.d.ts.map +1 -1
- package/dist/stacks/box/box.js.map +1 -1
- package/dist/stacks/box/detect_resize_bounds.d.ts +15 -0
- package/dist/stacks/box/detect_resize_bounds.d.ts.map +1 -0
- package/dist/stacks/box/detect_resize_bounds.js +49 -0
- package/dist/stacks/box/detect_resize_bounds.js.map +1 -0
- package/dist/stacks/box/resize_handlers.d.ts.map +1 -1
- package/dist/stacks/box/resize_handlers.js +51 -44
- package/dist/stacks/box/resize_handlers.js.map +1 -1
- package/dist/stacks/box/start_resize_handle.d.ts.map +1 -1
- package/dist/stacks/box/start_resize_handle.js +2 -1
- package/dist/stacks/box/start_resize_handle.js.map +1 -1
- package/dist/stacks/box/types.d.ts +17 -4
- package/dist/stacks/box/types.d.ts.map +1 -1
- package/dist/surfaces/modal/modal.d.ts.map +1 -1
- package/dist/surfaces/modal/modal.js +22 -13
- package/dist/surfaces/modal/modal.js.map +1 -1
- package/dist/surfaces/window/window.d.ts.map +1 -1
- package/dist/surfaces/window/window.js +21 -24
- package/dist/surfaces/window/window.js.map +1 -1
- package/package.json +2 -2
- package/src/overlay/frame/frame.tsx +34 -51
- package/src/stacks/box/box.tsx +10 -16
- package/src/stacks/box/detect_resize_bounds.ts +84 -0
- package/src/stacks/box/resize_handlers.ts +27 -15
- package/src/stacks/box/start_resize_handle.tsx +6 -3
- package/src/stacks/box/types.ts +23 -25
- package/src/surfaces/modal/__stories__/modal.stories.tsx +70 -3
- package/src/surfaces/modal/modal.tsx +11 -2
- package/src/surfaces/window/window.stories.tsx +64 -8
- package/src/surfaces/window/window.tsx +6 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/stacks/box/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,OAAO,CAAC;AACjD,MAAM,MAAM,kBAAkB,GAAG,KAAK,GAAG,QAAQ,CAAC;AAElD,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/stacks/box/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,OAAO,CAAC;AACjD,MAAM,MAAM,kBAAkB,GAAG,KAAK,GAAG,QAAQ,CAAC;AAElD,MAAM,WAAW,mBAAmB,CAClC,MAAM,SAAS,iBAAiB,GAAG,kBAAkB;IAErD,MAAM,EAAE,MAAM,CAAC;IAEf,UAAU,EAAE,MAAM,CAAC;IAEnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAqB,SAAQ,mBAAmB,CAAC,iBAAiB,CAAC;IAClF,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB,CAAC,kBAAkB,CAAC;IACpF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAClF,MAAM,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAErF,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAC;AACpE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAEtE,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,KAAK,CAAC,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACtD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,KAAK,CAAC,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACtD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["../../../src/surfaces/modal/modal.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAMtE,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC;AAEpC,eAAO,MAAM,KAAK,+
|
|
1
|
+
{"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["../../../src/surfaces/modal/modal.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAMtE,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC;AAEpC,eAAO,MAAM,KAAK,+QA2BhB,CAAC"}
|
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
import { jsx as o } from "react/jsx-runtime";
|
|
2
|
-
import { clsx as
|
|
3
|
-
import
|
|
4
|
-
import { Frame as
|
|
5
|
-
import { Scaffold as
|
|
6
|
-
import '../../modal.css';const
|
|
2
|
+
import { clsx as s } from "clsx";
|
|
3
|
+
import d from "react";
|
|
4
|
+
import { Frame as n } from "../../overlay/frame/frame.js";
|
|
5
|
+
import { Scaffold as i } from "../../layouts/scaffold/scaffold.js";
|
|
6
|
+
import '../../modal.css';const p = "_modal_473c6ef", u = { modal: p }, R = d.forwardRef(function({
|
|
7
|
+
children: a,
|
|
8
|
+
className: t,
|
|
9
|
+
isOpen: r,
|
|
10
|
+
veil: m = !0,
|
|
11
|
+
resizable: f = !1,
|
|
12
|
+
draggable: l = !1,
|
|
13
|
+
...c
|
|
14
|
+
}, e) {
|
|
7
15
|
return /* @__PURE__ */ o(
|
|
8
|
-
|
|
16
|
+
n,
|
|
9
17
|
{
|
|
10
|
-
ref:
|
|
18
|
+
ref: e,
|
|
11
19
|
isOpen: r,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
veil: m,
|
|
21
|
+
draggable: l,
|
|
22
|
+
resizable: f,
|
|
23
|
+
className: s(u.modal, "tcn-surface", "tcn-modal", t),
|
|
24
|
+
...c,
|
|
25
|
+
children: /* @__PURE__ */ o(i, { className: "tcn-modal-scaffold", width: "100%", height: "100%", children: a })
|
|
17
26
|
}
|
|
18
27
|
);
|
|
19
28
|
});
|
|
20
29
|
export {
|
|
21
|
-
|
|
30
|
+
R as Modal
|
|
22
31
|
};
|
|
23
32
|
//# sourceMappingURL=modal.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modal.js","sources":["../../../src/surfaces/modal/modal.tsx"],"sourcesContent":["import { clsx } from 'clsx';\nimport React from 'react';\nimport { Frame, type FrameProps } from '../../overlay/frame/frame.js';\nimport { Scaffold } from '../../layouts/scaffold/scaffold.js';\n\n// Styles\nimport styles from './modal.module.css';\n\nexport type ModalProps = FrameProps;\n\nexport const Modal = React.forwardRef<HTMLElement, ModalProps>(function Modal(\n {
|
|
1
|
+
{"version":3,"file":"modal.js","sources":["../../../src/surfaces/modal/modal.tsx"],"sourcesContent":["import { clsx } from 'clsx';\nimport React from 'react';\nimport { Frame, type FrameProps } from '../../overlay/frame/frame.js';\nimport { Scaffold } from '../../layouts/scaffold/scaffold.js';\n\n// Styles\nimport styles from './modal.module.css';\n\nexport type ModalProps = FrameProps;\n\nexport const Modal = React.forwardRef<HTMLElement, ModalProps>(function Modal(\n {\n children,\n className,\n isOpen,\n veil = true,\n resizable = false,\n draggable = false,\n ...props\n }: ModalProps,\n ref\n) {\n return (\n <Frame\n ref={ref}\n isOpen={isOpen}\n veil={veil}\n draggable={draggable}\n resizable={resizable}\n className={clsx(styles['modal'], 'tcn-surface', 'tcn-modal', className)}\n {...props}\n >\n <Scaffold className={'tcn-modal-scaffold'} width=\"100%\" height=\"100%\">\n {children}\n </Scaffold>\n </Frame>\n );\n});\n"],"names":["Modal","React","children","className","isOpen","veil","resizable","draggable","props","ref","jsx","Frame","clsx","styles","Scaffold"],"mappings":";;;;;8CAUaA,IAAQC,EAAM,WAAoC,SAC7D;AAAA,EACE,UAAAC;AAAA,EACA,WAAAC;AAAA,EACA,QAAAC;AAAA,EACA,MAAAC,IAAO;AAAA,EACP,WAAAC,IAAY;AAAA,EACZ,WAAAC,IAAY;AAAA,EACZ,GAAGC;AACL,GACAC,GACA;AACA,SACE,gBAAAC;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,KAAAF;AAAA,MACA,QAAAL;AAAA,MACA,MAAAC;AAAA,MACA,WAAAE;AAAA,MACA,WAAAD;AAAA,MACA,WAAWM,EAAKC,EAAO,OAAU,eAAe,aAAaV,CAAS;AAAA,MACrE,GAAGK;AAAA,MAEJ,UAAA,gBAAAE,EAACI,KAAS,WAAW,sBAAsB,OAAM,QAAO,QAAO,QAC5D,UAAAZ,EAAA,CACH;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../../../src/surfaces/window/window.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAMtE,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC;AAErC,eAAO,MAAM,MAAM,+
|
|
1
|
+
{"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../../../src/surfaces/window/window.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAMtE,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC;AAErC,eAAO,MAAM,MAAM,+QA2BjB,CAAC"}
|
|
@@ -1,35 +1,32 @@
|
|
|
1
1
|
import { jsx as o } from "react/jsx-runtime";
|
|
2
|
-
import { clsx as
|
|
3
|
-
import
|
|
4
|
-
import { Frame as
|
|
5
|
-
import { Scaffold as
|
|
6
|
-
import '../../window.css';const
|
|
7
|
-
children:
|
|
8
|
-
className:
|
|
9
|
-
isOpen:
|
|
10
|
-
draggable: i = !0,
|
|
11
|
-
resizable: _ = !0,
|
|
2
|
+
import { clsx as d } from "clsx";
|
|
3
|
+
import w from "react";
|
|
4
|
+
import { Frame as a } from "../../overlay/frame/frame.js";
|
|
5
|
+
import { Scaffold as m } from "../../layouts/scaffold/scaffold.js";
|
|
6
|
+
import '../../window.css';const l = "_window_7610fdb", p = { window: l }, W = w.forwardRef(function({
|
|
7
|
+
children: t,
|
|
8
|
+
className: r,
|
|
9
|
+
isOpen: f,
|
|
12
10
|
veil: n = !1,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
...
|
|
16
|
-
},
|
|
11
|
+
draggable: i = !0,
|
|
12
|
+
resizable: c = !1,
|
|
13
|
+
...e
|
|
14
|
+
}, s) {
|
|
17
15
|
return /* @__PURE__ */ o(
|
|
18
|
-
|
|
16
|
+
a,
|
|
19
17
|
{
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
ref: s,
|
|
19
|
+
isOpen: f,
|
|
22
20
|
veil: n,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
children: /* @__PURE__ */ o(l, { width: "100%", height: "100%", className: "tcn-window-scaffold", children: r })
|
|
21
|
+
draggable: i,
|
|
22
|
+
resizable: c,
|
|
23
|
+
className: d(p.window, "tcn-surface", "tcn-window", r),
|
|
24
|
+
...e,
|
|
25
|
+
children: /* @__PURE__ */ o(m, { className: "tcn-window-scaffold", width: "100%", height: "100%", children: t })
|
|
29
26
|
}
|
|
30
27
|
);
|
|
31
28
|
});
|
|
32
29
|
export {
|
|
33
|
-
|
|
30
|
+
W as Window
|
|
34
31
|
};
|
|
35
32
|
//# sourceMappingURL=window.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"window.js","sources":["../../../src/surfaces/window/window.tsx"],"sourcesContent":["import { clsx } from 'clsx';\nimport React from 'react';\nimport { Frame, type FrameProps } from '../../overlay/frame/frame.js';\nimport { Scaffold } from '../../layouts/scaffold/scaffold.js';\n\n// Styles\nimport styles from './window.module.css';\n\nexport type WindowProps = FrameProps;\n\nexport const Window = React.forwardRef<HTMLElement, WindowProps>(function Window(\n {\n children,\n className,\n isOpen,\n
|
|
1
|
+
{"version":3,"file":"window.js","sources":["../../../src/surfaces/window/window.tsx"],"sourcesContent":["import { clsx } from 'clsx';\nimport React from 'react';\nimport { Frame, type FrameProps } from '../../overlay/frame/frame.js';\nimport { Scaffold } from '../../layouts/scaffold/scaffold.js';\n\n// Styles\nimport styles from './window.module.css';\n\nexport type WindowProps = FrameProps;\n\nexport const Window = React.forwardRef<HTMLElement, WindowProps>(function Window(\n {\n children,\n className,\n isOpen,\n veil = false,\n draggable = true,\n resizable = false,\n ...props\n }: WindowProps,\n ref\n) {\n return (\n <Frame\n ref={ref}\n isOpen={isOpen}\n veil={veil}\n draggable={draggable}\n resizable={resizable}\n className={clsx(styles['window'], 'tcn-surface', 'tcn-window', className)}\n {...props}\n >\n <Scaffold className={'tcn-window-scaffold'} width=\"100%\" height=\"100%\">\n {children}\n </Scaffold>\n </Frame>\n );\n});\n"],"names":["Window","React","children","className","isOpen","veil","draggable","resizable","props","ref","jsx","Frame","clsx","styles","Scaffold"],"mappings":";;;;;gDAUaA,IAASC,EAAM,WAAqC,SAC/D;AAAA,EACE,UAAAC;AAAA,EACA,WAAAC;AAAA,EACA,QAAAC;AAAA,EACA,MAAAC,IAAO;AAAA,EACP,WAAAC,IAAY;AAAA,EACZ,WAAAC,IAAY;AAAA,EACZ,GAAGC;AACL,GACAC,GACA;AACA,SACE,gBAAAC;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,KAAAF;AAAA,MACA,QAAAL;AAAA,MACA,MAAAC;AAAA,MACA,WAAAC;AAAA,MACA,WAAAC;AAAA,MACA,WAAWK,EAAKC,EAAO,QAAW,eAAe,cAAcV,CAAS;AAAA,MACvE,GAAGK;AAAA,MAEJ,UAAA,gBAAAE,EAACI,KAAS,WAAW,uBAAuB,OAAM,QAAO,QAAO,QAC7D,UAAAZ,EAAA,CACH;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tcn/ui",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "",
|
|
6
6
|
"author": "TCN",
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
"react-color": "^2.19.3",
|
|
145
145
|
"react-phone-number-input": "^3.4.16",
|
|
146
146
|
"@tcn/icons": "2.3.0",
|
|
147
|
-
"@tcn/state": "1.
|
|
147
|
+
"@tcn/state": "1.3.0"
|
|
148
148
|
},
|
|
149
149
|
"scripts": {
|
|
150
150
|
"build": "vite build",
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
2
|
import React, { useCallback } from 'react';
|
|
3
3
|
import { flushSync } from 'react-dom';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
Box,
|
|
6
|
+
ZStack,
|
|
7
|
+
type BoxProps,
|
|
8
|
+
type OnHeightResizePayload,
|
|
9
|
+
type OnWidthResizePayload,
|
|
10
|
+
} from '../../stacks/index.js';
|
|
5
11
|
import { useDragContainer } from '../../utils/dnd/context.js';
|
|
6
12
|
import { Draggable } from '../../utils/dnd/draggable/draggable.js';
|
|
7
13
|
import { Portal } from '../portal/portal.js';
|
|
@@ -48,6 +54,10 @@ export const Frame = React.forwardRef<HTMLElement, FrameProps>(function Frame(
|
|
|
48
54
|
enableResizeOnRight={resizable}
|
|
49
55
|
enableResizeOnTop={resizable}
|
|
50
56
|
enableResizeOnBottom={resizable}
|
|
57
|
+
// TODO: check to see if these should be enabled, and if so - if left/right should be disabled.
|
|
58
|
+
// Could add a "directional" prop and use that in conjunction with enableResizeOnStart/End and deprecate enableResizeOnLeft/Right.
|
|
59
|
+
// enableResizeOnStart={resizable}
|
|
60
|
+
// enableResizeOnEnd={resizable}
|
|
51
61
|
draggable={draggable}
|
|
52
62
|
{...rest}
|
|
53
63
|
>
|
|
@@ -77,64 +87,37 @@ export const FrameDialog = React.forwardRef<HTMLElement, FrameDialogProps>(
|
|
|
77
87
|
const drag = useDragContainer();
|
|
78
88
|
|
|
79
89
|
const handleWidthResize = React.useCallback(
|
|
80
|
-
(
|
|
81
|
-
width: number,
|
|
82
|
-
origin: 'left' | 'right',
|
|
83
|
-
totalDelta: number,
|
|
84
|
-
currentDelta: number,
|
|
85
|
-
atLimit: boolean
|
|
86
|
-
) => {
|
|
90
|
+
(payload: OnWidthResizePayload) => {
|
|
87
91
|
if (!draggable) return;
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
if (origin === 'left') {
|
|
98
|
-
flushSync(() => {
|
|
99
|
-
drag.setPosition(prev => ({
|
|
100
|
-
x: prev.x - currentDelta / 2,
|
|
101
|
-
y: prev.y,
|
|
102
|
-
}));
|
|
103
|
-
});
|
|
104
|
-
}
|
|
92
|
+
if (payload.currentDelta === 0) return;
|
|
93
|
+
const sign = payload.origin === 'right' ? 1 : -1;
|
|
94
|
+
const dx = (payload.currentDelta / 2) * sign;
|
|
95
|
+
flushSync(() => {
|
|
96
|
+
drag.setPosition(prev => ({
|
|
97
|
+
x: prev.x + dx,
|
|
98
|
+
y: prev.y,
|
|
99
|
+
}));
|
|
100
|
+
});
|
|
105
101
|
|
|
106
|
-
onWidthResize?.(
|
|
102
|
+
onWidthResize?.(payload);
|
|
107
103
|
},
|
|
108
104
|
[onWidthResize, drag, draggable]
|
|
109
105
|
);
|
|
110
106
|
|
|
111
107
|
const handleHeightResize = useCallback(
|
|
112
|
-
(
|
|
113
|
-
height: number,
|
|
114
|
-
origin: 'top' | 'bottom',
|
|
115
|
-
totalDelta: number,
|
|
116
|
-
currentDelta: number,
|
|
117
|
-
atLimit: boolean
|
|
118
|
-
) => {
|
|
108
|
+
(payload: OnHeightResizePayload) => {
|
|
119
109
|
if (!draggable) return;
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
drag.setPosition(prev => ({
|
|
132
|
-
x: prev.x,
|
|
133
|
-
y: prev.y - currentDelta / 2,
|
|
134
|
-
}));
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
onHeightResize?.(height, origin, totalDelta, currentDelta, atLimit);
|
|
110
|
+
if (payload.currentDelta === 0) return;
|
|
111
|
+
const sign = payload.origin === 'bottom' ? 1 : -1;
|
|
112
|
+
const half = payload.currentDelta / 2;
|
|
113
|
+
const dy = half * sign;
|
|
114
|
+
flushSync(() => {
|
|
115
|
+
drag.setPosition(prev => ({
|
|
116
|
+
x: prev.x,
|
|
117
|
+
y: prev.y + dy,
|
|
118
|
+
}));
|
|
119
|
+
});
|
|
120
|
+
onHeightResize?.(payload);
|
|
138
121
|
},
|
|
139
122
|
[onHeightResize, drag, draggable]
|
|
140
123
|
);
|
package/src/stacks/box/box.tsx
CHANGED
|
@@ -11,6 +11,12 @@ import { LeftResizeHandle } from './left_resize_handle.js';
|
|
|
11
11
|
import { RightResizeHandle } from './right_resize_handle.js';
|
|
12
12
|
import { StartResizeHandle } from './start_resize_handle.js';
|
|
13
13
|
import { TopResizeHandle } from './top_resize_handle.js';
|
|
14
|
+
import type {
|
|
15
|
+
OnHeightResize,
|
|
16
|
+
OnHeightResizeEnd,
|
|
17
|
+
OnWidthResize,
|
|
18
|
+
OnWidthResizeEnd,
|
|
19
|
+
} from './types.js';
|
|
14
20
|
|
|
15
21
|
export interface BoxProps<T extends HTMLElement = HTMLElement> extends HTMLAttributes<T> {
|
|
16
22
|
as?: string;
|
|
@@ -45,22 +51,10 @@ export interface BoxProps<T extends HTMLElement = HTMLElement> extends HTMLAttri
|
|
|
45
51
|
enableResizeOnRight?: boolean;
|
|
46
52
|
horizontalHandleProps?: HandleProps;
|
|
47
53
|
verticalHandleProps?: HandleProps;
|
|
48
|
-
onWidthResize?:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
currentDelta: number,
|
|
53
|
-
atLimit: boolean
|
|
54
|
-
) => void;
|
|
55
|
-
onHeightResize?: (
|
|
56
|
-
height: number,
|
|
57
|
-
origin: 'top' | 'bottom',
|
|
58
|
-
totalDelta: number,
|
|
59
|
-
currentDelta: number,
|
|
60
|
-
atLimit: boolean
|
|
61
|
-
) => void;
|
|
62
|
-
onWidthResizeEnd?: (width: number, origin: 'left' | 'right') => void;
|
|
63
|
-
onHeightResizeEnd?: (height: number, origin: 'top' | 'bottom') => void;
|
|
54
|
+
onWidthResize?: OnWidthResize;
|
|
55
|
+
onHeightResize?: OnHeightResize;
|
|
56
|
+
onWidthResizeEnd?: OnWidthResizeEnd;
|
|
57
|
+
onHeightResizeEnd?: OnHeightResizeEnd;
|
|
64
58
|
}
|
|
65
59
|
|
|
66
60
|
export const Box = React.forwardRef<HTMLElement, BoxProps>(function Box(
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
type ConstraintAxis = 'width' | 'height';
|
|
2
|
+
|
|
3
|
+
export type DetectResizeBoundsParams = {
|
|
4
|
+
element: HTMLElement;
|
|
5
|
+
axis: ConstraintAxis;
|
|
6
|
+
nextSize: number;
|
|
7
|
+
epsilon?: number; // Tolerance for the constraint hit detection.
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type DetectResizeBoundsResult = {
|
|
11
|
+
hitMin: boolean;
|
|
12
|
+
hitMax: boolean;
|
|
13
|
+
clamped: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const styleKeys = {
|
|
17
|
+
width: {
|
|
18
|
+
size: 'width',
|
|
19
|
+
min: 'minWidth',
|
|
20
|
+
max: 'maxWidth',
|
|
21
|
+
},
|
|
22
|
+
height: {
|
|
23
|
+
size: 'height',
|
|
24
|
+
min: 'minHeight',
|
|
25
|
+
max: 'maxHeight',
|
|
26
|
+
},
|
|
27
|
+
} as const;
|
|
28
|
+
|
|
29
|
+
function parsePx(value: string): number | null {
|
|
30
|
+
const match = /^(-?\d+(?:\.\d+)?)px$/.exec(value.trim());
|
|
31
|
+
return match ? Number(match[1]) : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function detectByPixelValue(nextSize: number, min: string, max: string) {
|
|
35
|
+
const minPx = parsePx(min);
|
|
36
|
+
const maxPx = parsePx(max);
|
|
37
|
+
const hitMin = minPx !== null && nextSize < minPx;
|
|
38
|
+
const hitMax = maxPx !== null && nextSize > maxPx;
|
|
39
|
+
return {
|
|
40
|
+
hitMin,
|
|
41
|
+
hitMax,
|
|
42
|
+
clamped: hitMin || hitMax,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function detectResizeBounds({
|
|
47
|
+
element,
|
|
48
|
+
axis,
|
|
49
|
+
nextSize,
|
|
50
|
+
epsilon = 0.5,
|
|
51
|
+
}: DetectResizeBoundsParams): DetectResizeBoundsResult {
|
|
52
|
+
const keys = styleKeys[axis];
|
|
53
|
+
|
|
54
|
+
const computed = getComputedStyle(element);
|
|
55
|
+
const fastPath = detectByPixelValue(nextSize, computed[keys.min], computed[keys.max]);
|
|
56
|
+
if (fastPath.clamped) return fastPath;
|
|
57
|
+
|
|
58
|
+
const style = element.style;
|
|
59
|
+
const prevInlineSize = style[keys.size]; // Save the previous size to revert later.
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
// Temporarily apply the new size to the element to offload bound test to browser.
|
|
63
|
+
style[keys.size] = `${nextSize}px`;
|
|
64
|
+
|
|
65
|
+
// Force layout so browser resolves min/max/intrinsic constraints.
|
|
66
|
+
const rect = element.getBoundingClientRect();
|
|
67
|
+
const renderedSize = rect[keys.size];
|
|
68
|
+
|
|
69
|
+
const delta = renderedSize - nextSize;
|
|
70
|
+
|
|
71
|
+
const hitMin = delta > epsilon;
|
|
72
|
+
const hitMax = delta < -epsilon;
|
|
73
|
+
const clamped = hitMin || hitMax;
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
hitMin,
|
|
77
|
+
hitMax,
|
|
78
|
+
clamped,
|
|
79
|
+
};
|
|
80
|
+
} finally {
|
|
81
|
+
// revert the style change
|
|
82
|
+
style[keys.size] = prevInlineSize;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type OnHeightResize,
|
|
5
5
|
type WidthResizeOrigin,
|
|
6
6
|
} from './types';
|
|
7
|
+
import { detectResizeBounds } from './detect_resize_bounds.js';
|
|
7
8
|
|
|
8
9
|
function createVeil() {
|
|
9
10
|
const veil = window.document.createElement('div');
|
|
@@ -25,9 +26,7 @@ export function createHorizontalResizeHandler(
|
|
|
25
26
|
return function startHorizontalResize(event: React.MouseEvent) {
|
|
26
27
|
const box = targetRef.current;
|
|
27
28
|
|
|
28
|
-
if (box == null)
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
29
|
+
if (box == null) return;
|
|
31
30
|
|
|
32
31
|
const veil = createVeil();
|
|
33
32
|
box.appendChild(veil);
|
|
@@ -42,18 +41,24 @@ export function createHorizontalResizeHandler(
|
|
|
42
41
|
let width = startRect.width;
|
|
43
42
|
|
|
44
43
|
const drag = (event: MouseEvent) => {
|
|
45
|
-
const beforeWidth = box.getBoundingClientRect().width;
|
|
46
44
|
const totalDelta = direction * (event.clientX - startX);
|
|
47
45
|
const newWidth = startRect.width + totalDelta;
|
|
46
|
+
|
|
47
|
+
const result = detectResizeBounds({
|
|
48
|
+
element: box,
|
|
49
|
+
axis: 'width',
|
|
50
|
+
nextSize: newWidth,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (result.clamped) return;
|
|
54
|
+
|
|
48
55
|
const currentDelta = newWidth - width;
|
|
49
56
|
|
|
57
|
+
// apply the new width
|
|
50
58
|
width = newWidth;
|
|
51
|
-
|
|
52
59
|
box.style.width = `${newWidth}px`;
|
|
53
60
|
|
|
54
|
-
|
|
55
|
-
const atLimit = afterWidth === beforeWidth;
|
|
56
|
-
onWidthResize?.(newWidth, origin, totalDelta, currentDelta, atLimit);
|
|
61
|
+
onWidthResize?.({ width: newWidth, origin, totalDelta, currentDelta });
|
|
57
62
|
event.stopPropagation();
|
|
58
63
|
event.preventDefault();
|
|
59
64
|
};
|
|
@@ -95,9 +100,7 @@ export function createVerticalResizeHandler(
|
|
|
95
100
|
return function startVerticalResize(event: React.MouseEvent) {
|
|
96
101
|
const box = targetRef.current;
|
|
97
102
|
|
|
98
|
-
if (box == null)
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
103
|
+
if (box == null) return;
|
|
101
104
|
|
|
102
105
|
const veil = createVeil();
|
|
103
106
|
box.appendChild(veil);
|
|
@@ -107,15 +110,24 @@ export function createVerticalResizeHandler(
|
|
|
107
110
|
let height = startRect.height;
|
|
108
111
|
|
|
109
112
|
const drag = (event: MouseEvent) => {
|
|
110
|
-
const beforeHeight = box.getBoundingClientRect().height;
|
|
111
113
|
const totalDelta = direction * (event.clientY - startY);
|
|
112
114
|
const newHeight = startRect.height + totalDelta;
|
|
115
|
+
|
|
116
|
+
const result = detectResizeBounds({
|
|
117
|
+
element: box,
|
|
118
|
+
axis: 'height',
|
|
119
|
+
nextSize: newHeight,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (result.clamped) return;
|
|
123
|
+
|
|
113
124
|
const currentDelta = newHeight - height;
|
|
125
|
+
|
|
126
|
+
// apply the new height
|
|
114
127
|
height = newHeight;
|
|
115
128
|
box.style.height = `${newHeight}px`;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
onHeightResize?.(newHeight, origin, totalDelta, currentDelta, atLimit);
|
|
129
|
+
|
|
130
|
+
onHeightResize?.({ height: newHeight, origin, totalDelta, currentDelta });
|
|
119
131
|
event.stopPropagation();
|
|
120
132
|
event.preventDefault();
|
|
121
133
|
};
|
|
@@ -2,6 +2,7 @@ import { clsx } from 'clsx';
|
|
|
2
2
|
import { createHorizontalResizeHandler } from './resize_handlers.js';
|
|
3
3
|
import styles from './start_resize_handle.module.css';
|
|
4
4
|
import type { HorizontalResizeHandleProps } from './types.js';
|
|
5
|
+
import { CSSProperties } from 'react';
|
|
5
6
|
|
|
6
7
|
export type StartResizeHandleProps = HorizontalResizeHandleProps;
|
|
7
8
|
|
|
@@ -15,15 +16,17 @@ export function StartResizeHandle({
|
|
|
15
16
|
targetRef,
|
|
16
17
|
onWidthResize,
|
|
17
18
|
onWidthResizeEnd,
|
|
18
|
-
'left'
|
|
19
|
+
'left',
|
|
20
|
+
true
|
|
19
21
|
);
|
|
22
|
+
|
|
20
23
|
const offset = handleProps?.offset ? handleProps.offset : -8;
|
|
21
24
|
|
|
22
|
-
const startResizeHandleStyle
|
|
25
|
+
const startResizeHandleStyle = {
|
|
23
26
|
...handleProps?.style,
|
|
24
27
|
'--resize-offset': `${offset}px`,
|
|
25
28
|
width: handleProps?.size || '16px',
|
|
26
|
-
};
|
|
29
|
+
} as CSSProperties;
|
|
27
30
|
|
|
28
31
|
return (
|
|
29
32
|
<div
|
package/src/stacks/box/types.ts
CHANGED
|
@@ -3,42 +3,40 @@ import type { HandleProps } from './handle_props.js';
|
|
|
3
3
|
export type WidthResizeOrigin = 'left' | 'right';
|
|
4
4
|
export type HeightResizeOrigin = 'top' | 'bottom';
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
origin: WidthResizeOrigin,
|
|
6
|
+
export interface BaseOnResizePayload<
|
|
7
|
+
Origin extends WidthResizeOrigin | HeightResizeOrigin,
|
|
8
|
+
> {
|
|
9
|
+
origin: Origin;
|
|
11
10
|
// Total delta of the resize (the sum of all deltas before end event)
|
|
12
|
-
totalDelta: number
|
|
11
|
+
totalDelta: number;
|
|
13
12
|
// Current delta of the resize (the delta of the current event)
|
|
14
|
-
currentDelta: number
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
) => void;
|
|
13
|
+
currentDelta: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface OnWidthResizePayload extends BaseOnResizePayload<WidthResizeOrigin> {
|
|
17
|
+
width: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface OnHeightResizePayload extends BaseOnResizePayload<HeightResizeOrigin> {
|
|
21
|
+
height: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type OnWidthResizeEnd = (width: number, origin: WidthResizeOrigin) => void;
|
|
25
|
+
export type OnHeightResizeEnd = (height: number, origin: HeightResizeOrigin) => void;
|
|
26
|
+
|
|
27
|
+
export type OnWidthResize = (payload: OnWidthResizePayload) => void;
|
|
28
|
+
export type OnHeightResize = (payload: OnHeightResizePayload) => void;
|
|
31
29
|
|
|
32
30
|
export interface HorizontalResizeHandleProps {
|
|
33
31
|
targetRef: React.MutableRefObject<HTMLElement | null>;
|
|
34
32
|
handleProps?: HandleProps;
|
|
35
33
|
onWidthResize?: OnWidthResize;
|
|
36
|
-
onWidthResizeEnd?:
|
|
34
|
+
onWidthResizeEnd?: OnWidthResizeEnd;
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
export interface VerticalResizeHandleProps {
|
|
40
38
|
targetRef: React.MutableRefObject<HTMLElement | null>;
|
|
41
39
|
handleProps?: HandleProps;
|
|
42
40
|
onHeightResize?: OnHeightResize;
|
|
43
|
-
onHeightResizeEnd?:
|
|
41
|
+
onHeightResizeEnd?: OnHeightResizeEnd;
|
|
44
42
|
}
|