@mezzanine-ui/react 1.2.0 → 1.3.1
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/Dropdown/Dropdown.js +1 -11
- package/Table/components/TableResizeHandle.js +54 -18
- package/package.json +2 -3
- package/utils/getElementRef.d.ts +27 -0
- package/utils/getElementRef.js +52 -0
package/Dropdown/Dropdown.js
CHANGED
|
@@ -10,20 +10,10 @@ import Button from '../Button/Button.js';
|
|
|
10
10
|
import { useDocumentEvents } from '../hooks/useDocumentEvents.js';
|
|
11
11
|
import Translate from '../Transition/Translate.js';
|
|
12
12
|
import { composeRefs } from '../utils/composeRefs.js';
|
|
13
|
+
import { getElementRef } from '../utils/getElementRef.js';
|
|
13
14
|
import DropdownItem from './DropdownItem.js';
|
|
14
15
|
import Popper from '../Popper/Popper.js';
|
|
15
16
|
|
|
16
|
-
/**
|
|
17
|
-
* Extracts ref from a ReactElement, supporting both React 18 and 19.
|
|
18
|
-
* In React 18, ref is on the element itself; in React 19, ref is in props.
|
|
19
|
-
*/
|
|
20
|
-
function getElementRef(element) {
|
|
21
|
-
var _a;
|
|
22
|
-
// React 19: ref is in props
|
|
23
|
-
const propsRef = (_a = element.props) === null || _a === void 0 ? void 0 : _a.ref;
|
|
24
|
-
// React 18: ref is on the element itself
|
|
25
|
-
return propsRef !== null && propsRef !== void 0 ? propsRef : element.ref;
|
|
26
|
-
}
|
|
27
17
|
/**
|
|
28
18
|
* 下拉選單元件,以 `Button` 或 `Input` 作為觸發元素,點擊後展開選項列表。
|
|
29
19
|
*
|
|
@@ -53,10 +53,19 @@ const TableResizeHandle = memo(function TableResizeHandle({ column, columnIndex,
|
|
|
53
53
|
// If there's no next column, we can't do adjacent resize
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
+
const lastColumnIndex = columns.length - 1;
|
|
57
|
+
const lastColumn = columns[lastColumnIndex];
|
|
58
|
+
// When resizing the second-to-last column, nextColumn === lastColumn.
|
|
59
|
+
// We collapse to the legacy adjacent-compensation path to avoid
|
|
60
|
+
// double-writing the same key.
|
|
61
|
+
const isAdjacentToLast = columnIndex + 1 === lastColumnIndex;
|
|
56
62
|
// Get the actual rendered widths from the DOM
|
|
57
63
|
const currentWidth = getColumnActualWidth(columnIndex);
|
|
58
64
|
const nextWidth = getColumnActualWidth(columnIndex + 1);
|
|
59
|
-
|
|
65
|
+
const lastWidth = isAdjacentToLast
|
|
66
|
+
? nextWidth
|
|
67
|
+
: getColumnActualWidth(lastColumnIndex);
|
|
68
|
+
if (currentWidth === 0 || nextWidth === 0 || lastWidth === 0) {
|
|
60
69
|
return;
|
|
61
70
|
}
|
|
62
71
|
startXRef.current = event.clientX;
|
|
@@ -66,32 +75,59 @@ const TableResizeHandle = memo(function TableResizeHandle({ column, columnIndex,
|
|
|
66
75
|
const maxWidth = column.maxWidth;
|
|
67
76
|
const nextMinWidth = nextColumn.minWidth;
|
|
68
77
|
const nextMaxWidth = nextColumn.maxWidth;
|
|
78
|
+
const lastMinWidth = lastColumn.minWidth;
|
|
79
|
+
const lastMaxWidth = lastColumn.maxWidth;
|
|
80
|
+
// Slack the last column can absorb (signed against `diff`):
|
|
81
|
+
// - positive `diff` shrinks last → capped by lastShrinkBudget
|
|
82
|
+
// - negative `diff` grows last → capped by lastGrowBudget
|
|
83
|
+
const lastShrinkBudget = lastWidth - (lastMinWidth !== null && lastMinWidth !== void 0 ? lastMinWidth : 0);
|
|
84
|
+
const lastGrowBudget = lastMaxWidth !== undefined ? lastMaxWidth - lastWidth : Infinity;
|
|
69
85
|
const handleMouseMove = (moveEvent) => {
|
|
70
86
|
const diff = moveEvent.clientX - startXRef.current;
|
|
71
87
|
const newWidth = startWidthRef.current + diff;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
88
|
+
// Current column constraint checks
|
|
89
|
+
if (minWidth !== undefined && newWidth < minWidth)
|
|
90
|
+
return;
|
|
91
|
+
if (maxWidth !== undefined && newWidth > maxWidth)
|
|
92
|
+
return;
|
|
93
|
+
if (newWidth < 0)
|
|
94
|
+
return;
|
|
95
|
+
if (isAdjacentToLast) {
|
|
96
|
+
// Legacy adjacent compensation: donor === N+1 === last
|
|
97
|
+
const newNextWidth = nextStartWidthRef.current - diff;
|
|
98
|
+
if (nextMinWidth !== undefined && newNextWidth < nextMinWidth)
|
|
99
|
+
return;
|
|
100
|
+
if (nextMaxWidth !== undefined && newNextWidth > nextMaxWidth)
|
|
101
|
+
return;
|
|
102
|
+
if (newNextWidth < 0)
|
|
103
|
+
return;
|
|
104
|
+
setResizedColumnWidth === null || setResizedColumnWidth === void 0 ? void 0 : setResizedColumnWidth(column.key, newWidth);
|
|
105
|
+
setResizedColumnWidth === null || setResizedColumnWidth === void 0 ? void 0 : setResizedColumnWidth(nextColumn.key, newNextWidth);
|
|
106
|
+
return;
|
|
80
107
|
}
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
108
|
+
// Last-column donor strategy:
|
|
109
|
+
// Project `diff` onto the last column first (clamped to its budget).
|
|
110
|
+
// Any overflow falls back to the adjacent column (N+1).
|
|
111
|
+
let toLast;
|
|
112
|
+
if (diff >= 0) {
|
|
113
|
+
toLast = Math.min(diff, lastShrinkBudget);
|
|
84
114
|
}
|
|
85
|
-
|
|
86
|
-
|
|
115
|
+
else {
|
|
116
|
+
toLast = Math.max(diff, -lastGrowBudget);
|
|
87
117
|
}
|
|
88
|
-
|
|
118
|
+
const overflow = diff - toLast;
|
|
119
|
+
const newLastWidth = lastWidth - toLast;
|
|
120
|
+
// Always write N+1 too — when overflow returns to 0 on the return
|
|
121
|
+
// drag, this restores N+1 to its start width (LIFO donor restoration).
|
|
122
|
+
const newNextWidth = nextStartWidthRef.current - overflow;
|
|
123
|
+
if (nextMinWidth !== undefined && newNextWidth < nextMinWidth)
|
|
89
124
|
return;
|
|
90
|
-
|
|
91
|
-
|
|
125
|
+
if (nextMaxWidth !== undefined && newNextWidth > nextMaxWidth)
|
|
126
|
+
return;
|
|
127
|
+
if (newNextWidth < 0)
|
|
92
128
|
return;
|
|
93
|
-
}
|
|
94
129
|
setResizedColumnWidth === null || setResizedColumnWidth === void 0 ? void 0 : setResizedColumnWidth(column.key, newWidth);
|
|
130
|
+
setResizedColumnWidth === null || setResizedColumnWidth === void 0 ? void 0 : setResizedColumnWidth(lastColumn.key, newLastWidth);
|
|
95
131
|
setResizedColumnWidth === null || setResizedColumnWidth === void 0 ? void 0 : setResizedColumnWidth(nextColumn.key, newNextWidth);
|
|
96
132
|
};
|
|
97
133
|
const handleMouseUp = () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mezzanine-ui/react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "React components for mezzanine-ui",
|
|
5
5
|
"author": "Mezzanine",
|
|
6
6
|
"repository": {
|
|
@@ -57,6 +57,5 @@
|
|
|
57
57
|
"moment": "^2.30.1",
|
|
58
58
|
"react": "^19.2.0",
|
|
59
59
|
"react-dom": "^19.2.0"
|
|
60
|
-
}
|
|
61
|
-
"gitHead": "f5fa136dac936a0debafff9ea5a3815ff0b5c040"
|
|
60
|
+
}
|
|
62
61
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ReactElement, Ref } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Helper type to extract ref from a ReactElement.
|
|
4
|
+
* Models `ref` on the element itself, which is compatible with React 18 and 19.
|
|
5
|
+
*/
|
|
6
|
+
export type ReactElementWithRef<P, E extends Element = HTMLElement> = ReactElement<P> & {
|
|
7
|
+
ref?: Ref<E>;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Extracts ref from a ReactElement, supporting both React 18 and 19.
|
|
11
|
+
* In React 18, ref is on the element itself; in React 19, ref is in props.
|
|
12
|
+
*
|
|
13
|
+
* Reading the "wrong" location in dev mode triggers React warning getters:
|
|
14
|
+
*
|
|
15
|
+
* - React 18 installs a warning getter on `props.ref` when the element was
|
|
16
|
+
* created with a ref — accessing it logs
|
|
17
|
+
* "`ref` is not a prop. Trying to access it will result in `undefined` being returned."
|
|
18
|
+
* - React 19 installs a deprecation getter on `element.ref` when the element
|
|
19
|
+
* was created with a ref — accessing it logs
|
|
20
|
+
* "Accessing element.ref was removed in React 19."
|
|
21
|
+
*
|
|
22
|
+
* So instead of unconditionally reading `props.ref` first, detect the
|
|
23
|
+
* dev-mode warning getters (marked with `isReactWarning`) and read the ref
|
|
24
|
+
* from the location where it actually lives. Same approach as
|
|
25
|
+
* `getElementRef` in radix-ui/primitives.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getElementRef<E extends Element = HTMLElement>(element: ReactElementWithRef<unknown, E>): Ref<E> | undefined;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the given property getter is a React dev-mode warning getter.
|
|
3
|
+
* React marks them with `isReactWarning = true`
|
|
4
|
+
* (see `defineRefPropWarningGetter` in the React source).
|
|
5
|
+
*/
|
|
6
|
+
function isReactWarningGetter(getter) {
|
|
7
|
+
return (typeof getter === 'function' &&
|
|
8
|
+
Boolean(getter.isReactWarning));
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Extracts ref from a ReactElement, supporting both React 18 and 19.
|
|
12
|
+
* In React 18, ref is on the element itself; in React 19, ref is in props.
|
|
13
|
+
*
|
|
14
|
+
* Reading the "wrong" location in dev mode triggers React warning getters:
|
|
15
|
+
*
|
|
16
|
+
* - React 18 installs a warning getter on `props.ref` when the element was
|
|
17
|
+
* created with a ref — accessing it logs
|
|
18
|
+
* "`ref` is not a prop. Trying to access it will result in `undefined` being returned."
|
|
19
|
+
* - React 19 installs a deprecation getter on `element.ref` when the element
|
|
20
|
+
* was created with a ref — accessing it logs
|
|
21
|
+
* "Accessing element.ref was removed in React 19."
|
|
22
|
+
*
|
|
23
|
+
* So instead of unconditionally reading `props.ref` first, detect the
|
|
24
|
+
* dev-mode warning getters (marked with `isReactWarning`) and read the ref
|
|
25
|
+
* from the location where it actually lives. Same approach as
|
|
26
|
+
* `getElementRef` in radix-ui/primitives.
|
|
27
|
+
*/
|
|
28
|
+
function getElementRef(element) {
|
|
29
|
+
var _a, _b, _c;
|
|
30
|
+
const props = element.props;
|
|
31
|
+
// React 18 dev mode: `props.ref` is a warning getter; the actual ref
|
|
32
|
+
// lives on the element itself.
|
|
33
|
+
const propsRefGetter = props
|
|
34
|
+
? (_a = Object.getOwnPropertyDescriptor(props, 'ref')) === null || _a === void 0 ? void 0 : _a.get
|
|
35
|
+
: undefined;
|
|
36
|
+
if (isReactWarningGetter(propsRefGetter)) {
|
|
37
|
+
return element.ref;
|
|
38
|
+
}
|
|
39
|
+
// React 19 dev mode: `element.ref` may be a deprecation warning getter;
|
|
40
|
+
// the actual ref lives in props as a regular property.
|
|
41
|
+
const elementRefGetter = (_b = Object.getOwnPropertyDescriptor(element, 'ref')) === null || _b === void 0 ? void 0 : _b.get;
|
|
42
|
+
if (isReactWarningGetter(elementRefGetter)) {
|
|
43
|
+
return props === null || props === void 0 ? void 0 : props.ref;
|
|
44
|
+
}
|
|
45
|
+
// No warning getters (production builds, or no ref was given):
|
|
46
|
+
// prefer `props.ref` (React 19), fall back to `element.ref` (React 18).
|
|
47
|
+
// Safe on React 19 dev — its `element.ref` deprecation getter is only
|
|
48
|
+
// installed when a ref exists, in which case `props.ref` is returned here.
|
|
49
|
+
return (_c = props === null || props === void 0 ? void 0 : props.ref) !== null && _c !== void 0 ? _c : element.ref;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export { getElementRef };
|