@vitus-labs/elements 1.2.1 → 1.2.3-alpha.3
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/LICENSE +1 -1
- package/README.md +279 -66
- package/lib/analysis/index.js.html +18 -18
- package/lib/analysis/vitus-labs-elements.native.js.html +18 -18
- package/lib/index.d.ts +832 -850
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +1290 -1160
- package/lib/index.js.map +1 -1
- package/lib/vitus-labs-elements.native.js +1176 -1060
- package/lib/vitus-labs-elements.native.js.map +1 -1
- package/package.json +24 -24
- package/lib/types/Element/component.d.ts +0 -4
- package/lib/types/Element/component.d.ts.map +0 -1
- package/lib/types/Element/constants.d.ts +0 -51
- package/lib/types/Element/constants.d.ts.map +0 -1
- package/lib/types/Element/index.d.ts +0 -7
- package/lib/types/Element/index.d.ts.map +0 -1
- package/lib/types/Element/types.d.ts +0 -423
- package/lib/types/Element/types.d.ts.map +0 -1
- package/lib/types/Element/utils.d.ts +0 -5
- package/lib/types/Element/utils.d.ts.map +0 -1
- package/lib/types/Element/withEqualSizeBeforeAfter.d.ts +0 -8
- package/lib/types/Element/withEqualSizeBeforeAfter.d.ts.map +0 -1
- package/lib/types/List/component.d.ts +0 -23
- package/lib/types/List/component.d.ts.map +0 -1
- package/lib/types/List/index.d.ts +0 -7
- package/lib/types/List/index.d.ts.map +0 -1
- package/lib/types/List/withActiveState.d.ts +0 -11
- package/lib/types/List/withActiveState.d.ts.map +0 -1
- package/lib/types/Overlay/component.d.ts +0 -52
- package/lib/types/Overlay/component.d.ts.map +0 -1
- package/lib/types/Overlay/context.d.ts +0 -12
- package/lib/types/Overlay/context.d.ts.map +0 -1
- package/lib/types/Overlay/index.d.ts +0 -7
- package/lib/types/Overlay/index.d.ts.map +0 -1
- package/lib/types/Overlay/useOverlay.d.ts +0 -109
- package/lib/types/Overlay/useOverlay.d.ts.map +0 -1
- package/lib/types/Portal/component.d.ts +0 -20
- package/lib/types/Portal/component.d.ts.map +0 -1
- package/lib/types/Portal/index.d.ts +0 -5
- package/lib/types/Portal/index.d.ts.map +0 -1
- package/lib/types/Text/component.d.ts +0 -30
- package/lib/types/Text/component.d.ts.map +0 -1
- package/lib/types/Text/index.d.ts +0 -5
- package/lib/types/Text/index.d.ts.map +0 -1
- package/lib/types/Text/styled.d.ts +0 -3
- package/lib/types/Text/styled.d.ts.map +0 -1
- package/lib/types/Util/component.d.ts +0 -19
- package/lib/types/Util/component.d.ts.map +0 -1
- package/lib/types/Util/index.d.ts +0 -5
- package/lib/types/Util/index.d.ts.map +0 -1
- package/lib/types/constants.d.ts +0 -2
- package/lib/types/constants.d.ts.map +0 -1
- package/lib/types/helpers/Content/component.d.ts +0 -5
- package/lib/types/helpers/Content/component.d.ts.map +0 -1
- package/lib/types/helpers/Content/index.d.ts +0 -3
- package/lib/types/helpers/Content/index.d.ts.map +0 -1
- package/lib/types/helpers/Content/styled.d.ts +0 -3
- package/lib/types/helpers/Content/styled.d.ts.map +0 -1
- package/lib/types/helpers/Content/types.d.ts +0 -31
- package/lib/types/helpers/Content/types.d.ts.map +0 -1
- package/lib/types/helpers/Iterator/component.d.ts +0 -10
- package/lib/types/helpers/Iterator/component.d.ts.map +0 -1
- package/lib/types/helpers/Iterator/index.d.ts +0 -5
- package/lib/types/helpers/Iterator/index.d.ts.map +0 -1
- package/lib/types/helpers/Iterator/types.d.ts +0 -60
- package/lib/types/helpers/Iterator/types.d.ts.map +0 -1
- package/lib/types/helpers/Wrapper/component.d.ts +0 -5
- package/lib/types/helpers/Wrapper/component.d.ts.map +0 -1
- package/lib/types/helpers/Wrapper/constants.d.ts +0 -6
- package/lib/types/helpers/Wrapper/constants.d.ts.map +0 -1
- package/lib/types/helpers/Wrapper/index.d.ts +0 -3
- package/lib/types/helpers/Wrapper/index.d.ts.map +0 -1
- package/lib/types/helpers/Wrapper/styled.d.ts +0 -3
- package/lib/types/helpers/Wrapper/styled.d.ts.map +0 -1
- package/lib/types/helpers/Wrapper/types.d.ts +0 -42
- package/lib/types/helpers/Wrapper/types.d.ts.map +0 -1
- package/lib/types/helpers/Wrapper/utils.d.ts +0 -4
- package/lib/types/helpers/Wrapper/utils.d.ts.map +0 -1
- package/lib/types/helpers/index.d.ts +0 -4
- package/lib/types/helpers/index.d.ts.map +0 -1
- package/lib/types/index.d.ts +0 -18
- package/lib/types/index.d.ts.map +0 -1
- package/lib/types/types.d.ts +0 -51
- package/lib/types/types.d.ts.map +0 -1
- package/lib/types/utils.d.ts +0 -2
- package/lib/types/utils.d.ts.map +0 -1
package/lib/index.js
CHANGED
|
@@ -1,30 +1,125 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { isFragment } from
|
|
6
|
-
import { createPortal } from
|
|
1
|
+
import { Provider, alignContent, extendCss, makeItResponsive, value } from "@vitus-labs/unistyle";
|
|
2
|
+
import { config, context, isEmpty, omit, pick, render, throttle } from "@vitus-labs/core";
|
|
3
|
+
import { Children, createContext, forwardRef, memo, useCallback, useContext, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { isFragment } from "react-is";
|
|
6
|
+
import { createPortal } from "react-dom";
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
//#region src/constants.ts
|
|
9
|
+
const PKG_NAME = "@vitus-labs/elements";
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/utils.ts
|
|
13
|
+
const IS_DEVELOPMENT = process.env.NODE_ENV !== "production";
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/helpers/Content/styled.ts
|
|
17
|
+
/**
|
|
18
|
+
* Styled component for content areas (before/content/after). Applies
|
|
19
|
+
* responsive flex alignment, gap spacing between slots based on parent
|
|
20
|
+
* direction (margin-right for inline, margin-bottom for rows), and
|
|
21
|
+
* equalCols flex distribution. The "content" slot gets `flex: 1` to
|
|
22
|
+
* fill remaining space between before and after.
|
|
23
|
+
*/
|
|
24
|
+
const { styled: styled$2, css: css$2, component: component$2 } = config;
|
|
25
|
+
const equalColsCSS = `
|
|
26
|
+
flex: 1;
|
|
27
|
+
`;
|
|
28
|
+
const typeContentCSS = `
|
|
29
|
+
flex: 1;
|
|
30
|
+
`;
|
|
31
|
+
const gapDimensions = {
|
|
32
|
+
inline: {
|
|
33
|
+
before: "margin-right",
|
|
34
|
+
after: "margin-left"
|
|
35
|
+
},
|
|
36
|
+
reverseInline: {
|
|
37
|
+
before: "margin-right",
|
|
38
|
+
after: "margin-left"
|
|
39
|
+
},
|
|
40
|
+
rows: {
|
|
41
|
+
before: "margin-bottom",
|
|
42
|
+
after: "margin-top"
|
|
43
|
+
},
|
|
44
|
+
reverseRows: {
|
|
45
|
+
before: "margin-bottom",
|
|
46
|
+
after: "margin-top"
|
|
47
|
+
}
|
|
18
48
|
};
|
|
49
|
+
const calculateGap = ({ direction, type, value }) => {
|
|
50
|
+
if (!direction || !type || type === "content") return void 0;
|
|
51
|
+
return `${gapDimensions[direction][type]}: ${value};`;
|
|
52
|
+
};
|
|
53
|
+
const styles$2 = ({ css, theme: t, rootSize }) => css`
|
|
54
|
+
${alignContent({
|
|
55
|
+
direction: t.direction,
|
|
56
|
+
alignX: t.alignX,
|
|
57
|
+
alignY: t.alignY
|
|
58
|
+
})};
|
|
19
59
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
60
|
+
${t.equalCols && equalColsCSS};
|
|
61
|
+
|
|
62
|
+
${t.gap && t.contentType && calculateGap({
|
|
63
|
+
direction: t.parentDirection,
|
|
64
|
+
type: t.contentType,
|
|
65
|
+
value: value(t.gap, rootSize)
|
|
66
|
+
})};
|
|
67
|
+
|
|
68
|
+
${t.extraStyles && extendCss(t.extraStyles)};
|
|
69
|
+
`;
|
|
70
|
+
const StyledComponent = styled$2(component$2)`
|
|
71
|
+
${`box-sizing: border-box;`};
|
|
72
|
+
|
|
73
|
+
display: flex;
|
|
74
|
+
align-self: stretch;
|
|
75
|
+
flex-wrap: wrap;
|
|
76
|
+
|
|
77
|
+
${({ $contentType }) => $contentType === "content" && typeContentCSS};
|
|
78
|
+
|
|
79
|
+
${makeItResponsive({
|
|
80
|
+
key: "$element",
|
|
81
|
+
styles: styles$2,
|
|
82
|
+
css: css$2,
|
|
83
|
+
normalize: true
|
|
84
|
+
})};
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/helpers/Content/component.tsx
|
|
89
|
+
const Component$9 = ({ contentType, tag, parentDirection, direction, alignX, alignY, equalCols, gap, extendCss, ...props }) => {
|
|
90
|
+
return /* @__PURE__ */ jsx(StyledComponent, {
|
|
91
|
+
as: tag,
|
|
92
|
+
$contentType: contentType,
|
|
93
|
+
$element: {
|
|
94
|
+
contentType,
|
|
95
|
+
parentDirection,
|
|
96
|
+
direction,
|
|
97
|
+
alignX,
|
|
98
|
+
alignY,
|
|
99
|
+
equalCols,
|
|
100
|
+
gap,
|
|
101
|
+
extraStyles: extendCss
|
|
102
|
+
},
|
|
103
|
+
...IS_DEVELOPMENT ? { "data-vl-element": contentType } : {},
|
|
104
|
+
...props
|
|
105
|
+
});
|
|
25
106
|
};
|
|
107
|
+
var component_default = memo(Component$9);
|
|
26
108
|
|
|
27
|
-
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/helpers/Content/index.ts
|
|
111
|
+
var Content_default = component_default;
|
|
112
|
+
|
|
113
|
+
//#endregion
|
|
114
|
+
//#region src/helpers/Wrapper/styled.ts
|
|
115
|
+
/**
|
|
116
|
+
* Styled component for the Element wrapper layer. Handles responsive
|
|
117
|
+
* block/inline-flex display, direction, alignment, and custom CSS injection.
|
|
118
|
+
* Includes special handling for the `parentFix` / `childFix` flags that
|
|
119
|
+
* split flex behavior across two DOM nodes for button/fieldset/legend
|
|
120
|
+
* elements where a single flex container is insufficient.
|
|
121
|
+
*/
|
|
122
|
+
const { styled: styled$1, css: css$1, component: component$1 } = config;
|
|
28
123
|
const childFixCSS = `
|
|
29
124
|
display: flex;
|
|
30
125
|
flex: 1;
|
|
@@ -43,14 +138,14 @@ const fullHeightCSS = `
|
|
|
43
138
|
const blockCSS = `
|
|
44
139
|
align-self: stretch;
|
|
45
140
|
`;
|
|
46
|
-
const childFixPosition = (isBlock) => `display: ${isBlock ?
|
|
47
|
-
const styles$
|
|
48
|
-
${t.alignY ===
|
|
141
|
+
const childFixPosition = (isBlock) => `display: ${isBlock ? "flex" : "inline-flex"};`;
|
|
142
|
+
const styles$1 = ({ theme: t, css }) => css`
|
|
143
|
+
${t.alignY === "block" && fullHeightCSS};
|
|
49
144
|
|
|
50
145
|
${alignContent({
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
146
|
+
direction: t.direction,
|
|
147
|
+
alignX: t.alignX,
|
|
148
|
+
alignY: t.alignY
|
|
54
149
|
})};
|
|
55
150
|
|
|
56
151
|
${t.block && blockCSS};
|
|
@@ -61,1223 +156,1258 @@ const styles$2 = ({ theme: t, css, }) => css `
|
|
|
61
156
|
|
|
62
157
|
${t.extraStyles && extendCss(t.extraStyles)};
|
|
63
158
|
`;
|
|
64
|
-
const platformCSS
|
|
65
|
-
var
|
|
159
|
+
const platformCSS = `box-sizing: border-box;`;
|
|
160
|
+
var styled_default$1 = styled$1(component$1)`
|
|
66
161
|
position: relative;
|
|
67
|
-
${platformCSS
|
|
162
|
+
${platformCSS};
|
|
68
163
|
|
|
69
164
|
${({ $childFix }) => $childFix && childFixCSS};
|
|
70
165
|
|
|
71
166
|
${makeItResponsive({
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
167
|
+
key: "$element",
|
|
168
|
+
styles: styles$1,
|
|
169
|
+
css: css$1,
|
|
170
|
+
normalize: true
|
|
76
171
|
})};
|
|
77
172
|
`;
|
|
78
173
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
as: tag,
|
|
91
|
-
};
|
|
92
|
-
const needsFix = !props.dangerouslySetInnerHTML && isWebFixNeeded(tag)
|
|
93
|
-
;
|
|
94
|
-
if (!needsFix || false) {
|
|
95
|
-
return (React.createElement(Styled$1, { ...COMMON_PROPS, "$element": {
|
|
96
|
-
block,
|
|
97
|
-
direction,
|
|
98
|
-
alignX,
|
|
99
|
-
alignY,
|
|
100
|
-
equalCols,
|
|
101
|
-
extraStyles: extendCss,
|
|
102
|
-
} }, children));
|
|
103
|
-
}
|
|
104
|
-
// eslint-disable-next-line no-nested-ternary
|
|
105
|
-
const asTag = (isInline ? 'span' : 'div') ;
|
|
106
|
-
return (React.createElement(Styled$1, { ...COMMON_PROPS, "$element": {
|
|
107
|
-
parentFix: true,
|
|
108
|
-
block,
|
|
109
|
-
extraStyles: extendCss,
|
|
110
|
-
} },
|
|
111
|
-
React.createElement(Styled$1, { as: asTag, "$childFix": true, "$element": {
|
|
112
|
-
childFix: true,
|
|
113
|
-
direction,
|
|
114
|
-
alignX,
|
|
115
|
-
alignY,
|
|
116
|
-
equalCols,
|
|
117
|
-
} }, children)));
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
const { styled: styled$1, css: css$1, component: component$2 } = config;
|
|
121
|
-
const equalColsCSS = `
|
|
122
|
-
flex: 1;
|
|
123
|
-
`;
|
|
124
|
-
const typeContentCSS = `
|
|
125
|
-
flex: 1;
|
|
126
|
-
`;
|
|
127
|
-
// --------------------------------------------------------
|
|
128
|
-
// calculate spacing between before / content / after
|
|
129
|
-
// --------------------------------------------------------
|
|
130
|
-
const gapDimensions = {
|
|
131
|
-
inline: {
|
|
132
|
-
before: 'margin-right',
|
|
133
|
-
after: 'margin-left',
|
|
134
|
-
},
|
|
135
|
-
reverseInline: {
|
|
136
|
-
before: 'margin-right',
|
|
137
|
-
after: 'margin-left',
|
|
138
|
-
},
|
|
139
|
-
rows: {
|
|
140
|
-
before: 'margin-bottom',
|
|
141
|
-
after: 'margin-top',
|
|
142
|
-
},
|
|
143
|
-
reverseRows: {
|
|
144
|
-
before: 'margin-bottom',
|
|
145
|
-
after: 'margin-top',
|
|
146
|
-
},
|
|
147
|
-
};
|
|
148
|
-
const calculateGap = ({ direction, type, value, }) => {
|
|
149
|
-
if (!direction || !type)
|
|
150
|
-
return undefined;
|
|
151
|
-
const finalStyles = `${gapDimensions[direction][type]}: ${value};`;
|
|
152
|
-
return finalStyles;
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region src/helpers/Wrapper/constants.ts
|
|
176
|
+
/**
|
|
177
|
+
* HTML elements that need a two-layer DOM workaround because browsers do not
|
|
178
|
+
* fully support flexbox layout on button, fieldset, and legend elements.
|
|
179
|
+
* @see https://stackoverflow.com/questions/35464067/flexbox-not-working-on-button-or-fieldset-elements
|
|
180
|
+
*/
|
|
181
|
+
const INLINE_ELEMENTS_FLEX_FIX = {
|
|
182
|
+
button: true,
|
|
183
|
+
fieldset: true,
|
|
184
|
+
legend: true
|
|
153
185
|
};
|
|
154
|
-
// --------------------------------------------------------
|
|
155
|
-
// calculations of styles to be rendered
|
|
156
|
-
// --------------------------------------------------------
|
|
157
|
-
const styles$1 = ({ css, theme: t, rootSize, }) => css `
|
|
158
|
-
${alignContent({
|
|
159
|
-
direction: t.direction,
|
|
160
|
-
alignX: t.alignX,
|
|
161
|
-
alignY: t.alignY,
|
|
162
|
-
})};
|
|
163
|
-
|
|
164
|
-
${t.equalCols && equalColsCSS};
|
|
165
|
-
|
|
166
|
-
${t.gap &&
|
|
167
|
-
t.contentType &&
|
|
168
|
-
calculateGap({
|
|
169
|
-
direction: t.parentDirection,
|
|
170
|
-
type: t.contentType,
|
|
171
|
-
value: value(t.gap, rootSize),
|
|
172
|
-
})};
|
|
173
|
-
|
|
174
|
-
${t.extraStyles && extendCss(t.extraStyles)};
|
|
175
|
-
`;
|
|
176
|
-
const platformCSS = `box-sizing: border-box;` ;
|
|
177
|
-
const StyledComponent = styled$1(component$2) `
|
|
178
|
-
${platformCSS};
|
|
179
186
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
187
|
+
//#endregion
|
|
188
|
+
//#region src/helpers/Wrapper/utils.ts
|
|
189
|
+
const isWebFixNeeded = (tag) => {
|
|
190
|
+
if (tag && tag in INLINE_ELEMENTS_FLEX_FIX) return true;
|
|
191
|
+
return false;
|
|
192
|
+
};
|
|
185
193
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/helpers/Wrapper/component.tsx
|
|
196
|
+
/**
|
|
197
|
+
* Wrapper component that serves as the outermost styled container for Element.
|
|
198
|
+
* Uses forwardRef for ref forwarding to the underlying DOM node. On web, it
|
|
199
|
+
* detects button/fieldset/legend tags and applies a two-layer flex fix
|
|
200
|
+
* (parent + child Styled) because these HTML elements do not natively
|
|
201
|
+
* support `display: flex` consistently across browsers.
|
|
202
|
+
*/
|
|
203
|
+
const DEV_PROPS = IS_DEVELOPMENT ? { "data-vl-element": "Element" } : {};
|
|
204
|
+
const Component$8 = forwardRef(({ children, tag, block, extendCss, direction, alignX, alignY, equalCols, isInline, ...props }, ref) => {
|
|
205
|
+
const COMMON_PROPS = {
|
|
206
|
+
...props,
|
|
207
|
+
...DEV_PROPS,
|
|
208
|
+
ref,
|
|
209
|
+
as: tag
|
|
210
|
+
};
|
|
211
|
+
if (!(!props.dangerouslySetInnerHTML && isWebFixNeeded(tag)) || false) return /* @__PURE__ */ jsx(styled_default$1, {
|
|
212
|
+
...COMMON_PROPS,
|
|
213
|
+
$element: {
|
|
214
|
+
block,
|
|
215
|
+
direction,
|
|
216
|
+
alignX,
|
|
217
|
+
alignY,
|
|
218
|
+
equalCols,
|
|
219
|
+
extraStyles: extendCss
|
|
220
|
+
},
|
|
221
|
+
children
|
|
222
|
+
});
|
|
223
|
+
const asTag = isInline ? "span" : "div";
|
|
224
|
+
return /* @__PURE__ */ jsx(styled_default$1, {
|
|
225
|
+
...COMMON_PROPS,
|
|
226
|
+
$element: {
|
|
227
|
+
parentFix: true,
|
|
228
|
+
block,
|
|
229
|
+
extraStyles: extendCss
|
|
230
|
+
},
|
|
231
|
+
children: /* @__PURE__ */ jsx(styled_default$1, {
|
|
232
|
+
as: asTag,
|
|
233
|
+
$childFix: true,
|
|
234
|
+
$element: {
|
|
235
|
+
childFix: true,
|
|
236
|
+
direction,
|
|
237
|
+
alignX,
|
|
238
|
+
alignY,
|
|
239
|
+
equalCols
|
|
240
|
+
},
|
|
241
|
+
children
|
|
242
|
+
})
|
|
243
|
+
});
|
|
244
|
+
});
|
|
193
245
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
'data-vl-element': contentType,
|
|
198
|
-
}
|
|
199
|
-
: {};
|
|
200
|
-
const stylingProps = {
|
|
201
|
-
contentType,
|
|
202
|
-
parentDirection,
|
|
203
|
-
direction,
|
|
204
|
-
alignX,
|
|
205
|
-
alignY,
|
|
206
|
-
equalCols,
|
|
207
|
-
gap,
|
|
208
|
-
extraStyles: extendCss,
|
|
209
|
-
};
|
|
210
|
-
return (React.createElement(StyledComponent, { as: tag, "$contentType": contentType, "$element": stylingProps, ...debugProps, ...props }));
|
|
211
|
-
};
|
|
212
|
-
var component$1 = memo(Component$8);
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/helpers/Wrapper/index.ts
|
|
248
|
+
var Wrapper_default = Component$8;
|
|
213
249
|
|
|
250
|
+
//#endregion
|
|
251
|
+
//#region src/Element/constants.ts
|
|
252
|
+
/**
|
|
253
|
+
* HTML tags that are inline-level by default. When Element renders one of
|
|
254
|
+
* these tags, child Content wrappers use `span` instead of `div` to
|
|
255
|
+
* preserve valid HTML nesting.
|
|
256
|
+
*/
|
|
214
257
|
const INLINE_ELEMENTS = {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
258
|
+
span: true,
|
|
259
|
+
a: true,
|
|
260
|
+
button: true,
|
|
261
|
+
input: true,
|
|
262
|
+
label: true,
|
|
263
|
+
select: true,
|
|
264
|
+
textarea: true,
|
|
265
|
+
br: true,
|
|
266
|
+
img: true,
|
|
267
|
+
strong: true,
|
|
268
|
+
small: true,
|
|
269
|
+
code: true,
|
|
270
|
+
b: true,
|
|
271
|
+
big: true,
|
|
272
|
+
i: true,
|
|
273
|
+
tt: true,
|
|
274
|
+
abbr: true,
|
|
275
|
+
acronym: true,
|
|
276
|
+
cite: true,
|
|
277
|
+
dfn: true,
|
|
278
|
+
em: true,
|
|
279
|
+
kbd: true,
|
|
280
|
+
samp: true,
|
|
281
|
+
var: true,
|
|
282
|
+
bdo: true,
|
|
283
|
+
map: true,
|
|
284
|
+
object: true,
|
|
285
|
+
q: true,
|
|
286
|
+
script: true,
|
|
287
|
+
sub: true,
|
|
288
|
+
sup: true
|
|
246
289
|
};
|
|
290
|
+
/**
|
|
291
|
+
* HTML void/self-closing elements that cannot have children. When Element
|
|
292
|
+
* detects one of these tags, it skips rendering beforeContent/content/afterContent
|
|
293
|
+
* and returns the Wrapper alone.
|
|
294
|
+
*/
|
|
247
295
|
const EMPTY_ELEMENTS = {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
track: true,
|
|
263
|
-
wbr: true,
|
|
296
|
+
area: true,
|
|
297
|
+
base: true,
|
|
298
|
+
br: true,
|
|
299
|
+
col: true,
|
|
300
|
+
embed: true,
|
|
301
|
+
hr: true,
|
|
302
|
+
img: true,
|
|
303
|
+
input: true,
|
|
304
|
+
keygen: true,
|
|
305
|
+
link: true,
|
|
306
|
+
textarea: true,
|
|
307
|
+
source: true,
|
|
308
|
+
track: true,
|
|
309
|
+
wbr: true
|
|
264
310
|
};
|
|
265
311
|
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region src/Element/utils.ts
|
|
314
|
+
/** Checks whether the given HTML tag is an inline-level element, used to determine sub-tag nesting. */
|
|
266
315
|
const isInlineElement = (tag) => {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
return false;
|
|
316
|
+
if (tag && tag in INLINE_ELEMENTS) return true;
|
|
317
|
+
return false;
|
|
270
318
|
};
|
|
319
|
+
/** Checks whether the given HTML tag is a void element that cannot have children. */
|
|
271
320
|
const getShouldBeEmpty = (tag) => {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
return false;
|
|
321
|
+
if (tag && tag in EMPTY_ELEMENTS) return true;
|
|
322
|
+
return false;
|
|
275
323
|
};
|
|
276
324
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
325
|
+
//#endregion
|
|
326
|
+
//#region src/Element/component.tsx
|
|
327
|
+
/**
|
|
328
|
+
* Core building block of the elements package. Renders a three-section layout
|
|
329
|
+
* (beforeContent / content / afterContent) inside a flex Wrapper. When only
|
|
330
|
+
* content is present, the Wrapper inherits content-level alignment directly
|
|
331
|
+
* to avoid an unnecessary nesting layer. Handles HTML-specific edge cases
|
|
332
|
+
* like void elements (input, img) and inline elements (span, a) by
|
|
333
|
+
* skipping children or switching sub-tags accordingly.
|
|
334
|
+
*/
|
|
335
|
+
const defaultDirection = "inline";
|
|
336
|
+
const defaultContentDirection = "rows";
|
|
337
|
+
const defaultAlignX = "left";
|
|
338
|
+
const defaultAlignY = "center";
|
|
281
339
|
const Component$7 = forwardRef(({ innerRef, tag, label, content, children, beforeContent, afterContent, block, equalCols, gap, direction, alignX = defaultAlignX, alignY = defaultAlignY, css, contentCss, beforeContentCss, afterContentCss, contentDirection = defaultContentDirection, contentAlignX = defaultAlignX, contentAlignY = defaultAlignY, beforeContentDirection = defaultDirection, beforeContentAlignX = defaultAlignX, beforeContentAlignY = defaultAlignY, afterContentDirection = defaultDirection, afterContentAlignX = defaultAlignX, afterContentAlignY = defaultAlignY, ...props }, ref) => {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
340
|
+
const shouldBeEmpty = !!props.dangerouslySetInnerHTML || getShouldBeEmpty(tag);
|
|
341
|
+
const isSimpleElement = !beforeContent && !afterContent;
|
|
342
|
+
const CHILDREN = children ?? content ?? label;
|
|
343
|
+
const isInline = isInlineElement(tag);
|
|
344
|
+
const SUB_TAG = isInline ? "span" : void 0;
|
|
345
|
+
const { wrapperDirection, wrapperAlignX, wrapperAlignY } = useMemo(() => {
|
|
346
|
+
let wrapperDirection = direction;
|
|
347
|
+
let wrapperAlignX = alignX;
|
|
348
|
+
let wrapperAlignY = alignY;
|
|
349
|
+
if (isSimpleElement) {
|
|
350
|
+
if (contentDirection) wrapperDirection = contentDirection;
|
|
351
|
+
if (contentAlignX) wrapperAlignX = contentAlignX;
|
|
352
|
+
if (contentAlignY) wrapperAlignY = contentAlignY;
|
|
353
|
+
} else if (direction) wrapperDirection = direction;
|
|
354
|
+
else wrapperDirection = defaultDirection;
|
|
355
|
+
return {
|
|
356
|
+
wrapperDirection,
|
|
357
|
+
wrapperAlignX,
|
|
358
|
+
wrapperAlignY
|
|
359
|
+
};
|
|
360
|
+
}, [
|
|
361
|
+
isSimpleElement,
|
|
362
|
+
contentDirection,
|
|
363
|
+
contentAlignX,
|
|
364
|
+
contentAlignY,
|
|
365
|
+
alignX,
|
|
366
|
+
alignY,
|
|
367
|
+
direction
|
|
368
|
+
]);
|
|
369
|
+
const WRAPPER_PROPS = {
|
|
370
|
+
ref: ref ?? innerRef,
|
|
371
|
+
extendCss: css,
|
|
372
|
+
tag,
|
|
373
|
+
block,
|
|
374
|
+
direction: wrapperDirection,
|
|
375
|
+
alignX: wrapperAlignX,
|
|
376
|
+
alignY: wrapperAlignY,
|
|
377
|
+
as: void 0
|
|
378
|
+
};
|
|
379
|
+
if (shouldBeEmpty) return /* @__PURE__ */ jsx(Wrapper_default, {
|
|
380
|
+
...props,
|
|
381
|
+
...WRAPPER_PROPS
|
|
382
|
+
});
|
|
383
|
+
const contentRenderOutput = render(CHILDREN);
|
|
384
|
+
return /* @__PURE__ */ jsxs(Wrapper_default, {
|
|
385
|
+
...props,
|
|
386
|
+
...WRAPPER_PROPS,
|
|
387
|
+
isInline,
|
|
388
|
+
children: [
|
|
389
|
+
beforeContent && /* @__PURE__ */ jsx(Content_default, {
|
|
390
|
+
tag: SUB_TAG,
|
|
391
|
+
contentType: "before",
|
|
392
|
+
parentDirection: wrapperDirection,
|
|
393
|
+
extendCss: beforeContentCss,
|
|
394
|
+
direction: beforeContentDirection,
|
|
395
|
+
alignX: beforeContentAlignX,
|
|
396
|
+
alignY: beforeContentAlignY,
|
|
397
|
+
equalCols,
|
|
398
|
+
gap,
|
|
399
|
+
children: render(beforeContent)
|
|
400
|
+
}),
|
|
401
|
+
isSimpleElement ? contentRenderOutput : /* @__PURE__ */ jsx(Content_default, {
|
|
402
|
+
tag: SUB_TAG,
|
|
403
|
+
contentType: "content",
|
|
404
|
+
parentDirection: wrapperDirection,
|
|
405
|
+
extendCss: contentCss,
|
|
406
|
+
direction: contentDirection,
|
|
407
|
+
alignX: contentAlignX,
|
|
408
|
+
alignY: contentAlignY,
|
|
409
|
+
equalCols,
|
|
410
|
+
children: contentRenderOutput
|
|
411
|
+
}),
|
|
412
|
+
afterContent && /* @__PURE__ */ jsx(Content_default, {
|
|
413
|
+
tag: SUB_TAG,
|
|
414
|
+
contentType: "after",
|
|
415
|
+
parentDirection: wrapperDirection,
|
|
416
|
+
extendCss: afterContentCss,
|
|
417
|
+
direction: afterContentDirection,
|
|
418
|
+
alignX: afterContentAlignX,
|
|
419
|
+
alignY: afterContentAlignY,
|
|
420
|
+
equalCols,
|
|
421
|
+
gap,
|
|
422
|
+
children: render(afterContent)
|
|
423
|
+
})
|
|
424
|
+
]
|
|
425
|
+
});
|
|
349
426
|
});
|
|
350
427
|
const name$5 = `${PKG_NAME}/Element`;
|
|
351
428
|
Component$7.displayName = name$5;
|
|
352
429
|
Component$7.pkgName = PKG_NAME;
|
|
353
430
|
Component$7.VITUS_LABS__COMPONENT = name$5;
|
|
354
431
|
|
|
355
|
-
|
|
432
|
+
//#endregion
|
|
433
|
+
//#region src/Element/withEqualSizeBeforeAfter.tsx
|
|
434
|
+
/**
|
|
435
|
+
* HOC that equalizes the dimensions of beforeContent and afterContent areas.
|
|
436
|
+
* After render, it measures both DOM nodes via useLayoutEffect and sets the
|
|
437
|
+
* larger dimension on both so they match. Uses width for inline direction
|
|
438
|
+
* and height for rows direction. This is useful for centering the main
|
|
439
|
+
* content when before/after slots have different intrinsic sizes.
|
|
440
|
+
*/
|
|
356
441
|
const types = {
|
|
357
|
-
|
|
358
|
-
|
|
442
|
+
height: "offsetHeight",
|
|
443
|
+
width: "offsetWidth"
|
|
359
444
|
};
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
beforeContent.style[type] = `${afterContentSize}px`;
|
|
370
|
-
afterContent.style[type] = `${afterContentSize}px`;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
445
|
+
const equalize = (beforeEl, afterEl, type) => {
|
|
446
|
+
const prop = types[type];
|
|
447
|
+
const beforeSize = beforeEl[prop];
|
|
448
|
+
const afterSize = afterEl[prop];
|
|
449
|
+
if (Number.isInteger(beforeSize) && Number.isInteger(afterSize)) {
|
|
450
|
+
const maxSize = `${Math.max(beforeSize, afterSize)}px`;
|
|
451
|
+
beforeEl.style[type] = maxSize;
|
|
452
|
+
afterEl.style[type] = maxSize;
|
|
453
|
+
}
|
|
373
454
|
};
|
|
374
455
|
const withEqualBeforeAfter = (WrappedComponent) => {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
Enhanced.displayName = `withEqualSizeBeforeAfter(${displayName})`;
|
|
397
|
-
return Enhanced;
|
|
456
|
+
const displayName = WrappedComponent.displayName ?? WrappedComponent.name ?? "Component";
|
|
457
|
+
const Enhanced = ({ equalBeforeAfter, direction, afterContent, beforeContent, ref, ...rest }) => {
|
|
458
|
+
const internalRef = useRef(null);
|
|
459
|
+
useImperativeHandle(ref, () => internalRef.current);
|
|
460
|
+
useLayoutEffect(() => {
|
|
461
|
+
if (!equalBeforeAfter || !beforeContent || !afterContent) return;
|
|
462
|
+
if (!internalRef.current) return;
|
|
463
|
+
const el = internalRef.current;
|
|
464
|
+
const beforeEl = el.firstElementChild;
|
|
465
|
+
const afterEl = el.lastElementChild;
|
|
466
|
+
if (beforeEl && afterEl && beforeEl !== afterEl) equalize(beforeEl, afterEl, direction === "rows" ? "height" : "width");
|
|
467
|
+
});
|
|
468
|
+
return /* @__PURE__ */ jsx(WrappedComponent, {
|
|
469
|
+
...rest,
|
|
470
|
+
afterContent,
|
|
471
|
+
beforeContent,
|
|
472
|
+
ref: internalRef
|
|
473
|
+
});
|
|
474
|
+
};
|
|
475
|
+
Enhanced.displayName = `withEqualSizeBeforeAfter(${displayName})`;
|
|
476
|
+
return Enhanced;
|
|
398
477
|
};
|
|
399
478
|
|
|
479
|
+
//#endregion
|
|
480
|
+
//#region src/Element/index.ts
|
|
481
|
+
var Element_default = Component$7;
|
|
482
|
+
|
|
483
|
+
//#endregion
|
|
484
|
+
//#region src/helpers/Iterator/component.tsx
|
|
485
|
+
/**
|
|
486
|
+
* Data-driven list renderer that supports three input modes: React children
|
|
487
|
+
* (including fragments), an array of primitives, or an array of objects.
|
|
488
|
+
* Each item receives positional metadata (first, last, odd, even, position)
|
|
489
|
+
* and optional injected props via `itemProps`. Items can be individually
|
|
490
|
+
* wrapped with `wrapComponent`. Children always take priority over the
|
|
491
|
+
* component+data prop pattern.
|
|
492
|
+
*/
|
|
493
|
+
const classifyData = (data) => {
|
|
494
|
+
const items = data.filter((item) => item != null && !(typeof item === "object" && isEmpty(item)));
|
|
495
|
+
if (items.length === 0) return null;
|
|
496
|
+
let isSimple = true;
|
|
497
|
+
let isComplex = true;
|
|
498
|
+
for (const item of items) if (typeof item === "string" || typeof item === "number") isComplex = false;
|
|
499
|
+
else if (typeof item === "object") isSimple = false;
|
|
500
|
+
else {
|
|
501
|
+
isSimple = false;
|
|
502
|
+
isComplex = false;
|
|
503
|
+
}
|
|
504
|
+
if (isSimple) return {
|
|
505
|
+
type: "simple",
|
|
506
|
+
data: items
|
|
507
|
+
};
|
|
508
|
+
if (isComplex) return {
|
|
509
|
+
type: "complex",
|
|
510
|
+
data: items
|
|
511
|
+
};
|
|
512
|
+
return null;
|
|
513
|
+
};
|
|
400
514
|
const RESERVED_PROPS = [
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
515
|
+
"children",
|
|
516
|
+
"component",
|
|
517
|
+
"wrapComponent",
|
|
518
|
+
"data",
|
|
519
|
+
"itemKey",
|
|
520
|
+
"valueName",
|
|
521
|
+
"itemProps",
|
|
522
|
+
"wrapProps"
|
|
409
523
|
];
|
|
410
|
-
const attachItemProps = ({ i, length
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
524
|
+
const attachItemProps = ({ i, length }) => {
|
|
525
|
+
const position = i + 1;
|
|
526
|
+
return {
|
|
527
|
+
index: i,
|
|
528
|
+
first: position === 1,
|
|
529
|
+
last: position === length,
|
|
530
|
+
odd: position % 2 === 1,
|
|
531
|
+
even: position % 2 === 0,
|
|
532
|
+
position
|
|
533
|
+
};
|
|
420
534
|
};
|
|
421
535
|
const Component$6 = (props) => {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
const extendedProps = attachItemProps({
|
|
522
|
-
i,
|
|
523
|
-
length,
|
|
524
|
-
});
|
|
525
|
-
const finalItemProps = {
|
|
526
|
-
...(itemProps ? injectItemProps(item, extendedProps) : {}),
|
|
527
|
-
...restItem,
|
|
528
|
-
};
|
|
529
|
-
if (Wrapper && !itemComponent) {
|
|
530
|
-
const finalWrapProps = wrapProps
|
|
531
|
-
? injectWrapItemProps(item, extendedProps)
|
|
532
|
-
: {};
|
|
533
|
-
return (React.createElement(Wrapper, { key: key, ...finalWrapProps }, render(renderItem, finalItemProps)));
|
|
534
|
-
}
|
|
535
|
-
return render(renderItem, { key, ...finalItemProps });
|
|
536
|
-
});
|
|
537
|
-
};
|
|
538
|
-
// --------------------------------------------------------
|
|
539
|
-
// render list items
|
|
540
|
-
// --------------------------------------------------------
|
|
541
|
-
const renderItems = () => {
|
|
542
|
-
// --------------------------------------------------------
|
|
543
|
-
// children have priority over props component + data
|
|
544
|
-
// --------------------------------------------------------
|
|
545
|
-
if (children)
|
|
546
|
-
return renderChildren();
|
|
547
|
-
// --------------------------------------------------------
|
|
548
|
-
// render props component + data
|
|
549
|
-
// --------------------------------------------------------
|
|
550
|
-
if (component && Array.isArray(data)) {
|
|
551
|
-
const clearData = data.filter((item) => item !== null && item !== undefined);
|
|
552
|
-
const isSimpleArray = clearData.every((item) => typeof item === 'string' || typeof item === 'number');
|
|
553
|
-
if (isSimpleArray)
|
|
554
|
-
return renderSimpleArray(clearData);
|
|
555
|
-
const isComplexArray = clearData.every((item) => typeof item === 'object');
|
|
556
|
-
if (isComplexArray)
|
|
557
|
-
return renderComplexArray(clearData);
|
|
558
|
-
return null;
|
|
559
|
-
}
|
|
560
|
-
// --------------------------------------------------------
|
|
561
|
-
// if there are no children or valid react component and data as an array,
|
|
562
|
-
// return null to prevent error
|
|
563
|
-
// --------------------------------------------------------
|
|
564
|
-
return null;
|
|
565
|
-
};
|
|
566
|
-
return renderItems();
|
|
536
|
+
const { itemKey, valueName, children, component, data, wrapComponent: Wrapper, wrapProps, itemProps } = props;
|
|
537
|
+
const injectItemProps = useMemo(() => typeof itemProps === "function" ? itemProps : () => itemProps, [itemProps]);
|
|
538
|
+
const injectWrapItemProps = useMemo(() => typeof wrapProps === "function" ? wrapProps : () => wrapProps, [wrapProps]);
|
|
539
|
+
const getKey = useCallback((item, index) => {
|
|
540
|
+
if (typeof itemKey === "function") return itemKey(item, index);
|
|
541
|
+
return index;
|
|
542
|
+
}, [itemKey]);
|
|
543
|
+
const renderChild = (child, total = 1, i = 0) => {
|
|
544
|
+
if (!itemProps && !Wrapper) return child;
|
|
545
|
+
const extendedProps = attachItemProps({
|
|
546
|
+
i,
|
|
547
|
+
length: total
|
|
548
|
+
});
|
|
549
|
+
const finalItemProps = itemProps ? injectItemProps({}, extendedProps) : {};
|
|
550
|
+
if (Wrapper) return /* @__PURE__ */ jsx(Wrapper, {
|
|
551
|
+
...wrapProps ? injectWrapItemProps({}, extendedProps) : {},
|
|
552
|
+
children: render(child, finalItemProps)
|
|
553
|
+
}, i);
|
|
554
|
+
return render(child, {
|
|
555
|
+
key: i,
|
|
556
|
+
...finalItemProps
|
|
557
|
+
});
|
|
558
|
+
};
|
|
559
|
+
const renderChildren = () => {
|
|
560
|
+
if (!children) return null;
|
|
561
|
+
if (Array.isArray(children)) return Children.map(children, (item, i) => renderChild(item, children.length, i));
|
|
562
|
+
if (isFragment(children)) {
|
|
563
|
+
const fragmentChildren = children.props.children;
|
|
564
|
+
const childrenLength = fragmentChildren.length;
|
|
565
|
+
return fragmentChildren.map((item, i) => renderChild(item, childrenLength, i));
|
|
566
|
+
}
|
|
567
|
+
return renderChild(children);
|
|
568
|
+
};
|
|
569
|
+
const renderSimpleArray = (data) => {
|
|
570
|
+
const { length } = data;
|
|
571
|
+
if (length === 0) return null;
|
|
572
|
+
return data.map((item, i) => {
|
|
573
|
+
const key = getKey(item, i);
|
|
574
|
+
const keyName = valueName ?? "children";
|
|
575
|
+
const extendedProps = attachItemProps({
|
|
576
|
+
i,
|
|
577
|
+
length
|
|
578
|
+
});
|
|
579
|
+
const finalItemProps = {
|
|
580
|
+
...itemProps ? injectItemProps({ [keyName]: item }, extendedProps) : {},
|
|
581
|
+
[keyName]: item
|
|
582
|
+
};
|
|
583
|
+
if (Wrapper) return /* @__PURE__ */ jsx(Wrapper, {
|
|
584
|
+
...wrapProps ? injectWrapItemProps({ [keyName]: item }, extendedProps) : {},
|
|
585
|
+
children: render(component, finalItemProps)
|
|
586
|
+
}, key);
|
|
587
|
+
return render(component, {
|
|
588
|
+
key,
|
|
589
|
+
...finalItemProps
|
|
590
|
+
});
|
|
591
|
+
});
|
|
592
|
+
};
|
|
593
|
+
const getObjectKey = (item, index) => {
|
|
594
|
+
if (!itemKey) return item.key ?? item.id ?? item.itemId ?? index;
|
|
595
|
+
if (typeof itemKey === "function") return itemKey(item, index);
|
|
596
|
+
if (typeof itemKey === "string") return item[itemKey];
|
|
597
|
+
return index;
|
|
598
|
+
};
|
|
599
|
+
const renderComplexArray = (data) => {
|
|
600
|
+
const { length } = data;
|
|
601
|
+
if (length === 0) return null;
|
|
602
|
+
return data.map((item, i) => {
|
|
603
|
+
const { component: itemComponent, ...restItem } = item;
|
|
604
|
+
const renderItem = itemComponent ?? component;
|
|
605
|
+
const key = getObjectKey(restItem, i);
|
|
606
|
+
const extendedProps = attachItemProps({
|
|
607
|
+
i,
|
|
608
|
+
length
|
|
609
|
+
});
|
|
610
|
+
const finalItemProps = {
|
|
611
|
+
...itemProps ? injectItemProps(item, extendedProps) : {},
|
|
612
|
+
...restItem
|
|
613
|
+
};
|
|
614
|
+
if (Wrapper && !itemComponent) return /* @__PURE__ */ jsx(Wrapper, {
|
|
615
|
+
...wrapProps ? injectWrapItemProps(item, extendedProps) : {},
|
|
616
|
+
children: render(renderItem, finalItemProps)
|
|
617
|
+
}, key);
|
|
618
|
+
return render(renderItem, {
|
|
619
|
+
key,
|
|
620
|
+
...finalItemProps
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
};
|
|
624
|
+
const renderItems = () => {
|
|
625
|
+
if (children) return renderChildren();
|
|
626
|
+
if (component && Array.isArray(data)) {
|
|
627
|
+
const classified = classifyData(data);
|
|
628
|
+
if (!classified) return null;
|
|
629
|
+
if (classified.type === "simple") return renderSimpleArray(classified.data);
|
|
630
|
+
return renderComplexArray(classified.data);
|
|
631
|
+
}
|
|
632
|
+
return null;
|
|
633
|
+
};
|
|
634
|
+
return renderItems();
|
|
567
635
|
};
|
|
568
636
|
Component$6.isIterator = true;
|
|
569
637
|
Component$6.RESERVED_PROPS = RESERVED_PROPS;
|
|
570
638
|
|
|
639
|
+
//#endregion
|
|
640
|
+
//#region src/helpers/Iterator/index.ts
|
|
641
|
+
var Iterator_default = Component$6;
|
|
642
|
+
|
|
643
|
+
//#endregion
|
|
644
|
+
//#region src/List/component.tsx
|
|
645
|
+
/**
|
|
646
|
+
* List component that combines Iterator (data-driven rendering) with an
|
|
647
|
+
* optional Element root wrapper. When `rootElement` is false (default),
|
|
648
|
+
* it renders a bare Iterator as a fragment. When true, the Iterator output
|
|
649
|
+
* is wrapped in an Element that receives all non-iterator props (e.g.,
|
|
650
|
+
* layout, alignment, css), allowing the list to be styled as a single block.
|
|
651
|
+
*/
|
|
571
652
|
const Component$5 = forwardRef(({ rootElement = false, ...props }, ref) => {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
653
|
+
const renderedList = /* @__PURE__ */ jsx(Iterator_default, { ...pick(props, Iterator_default.RESERVED_PROPS) });
|
|
654
|
+
if (!rootElement) return renderedList;
|
|
655
|
+
return /* @__PURE__ */ jsx(Element_default, {
|
|
656
|
+
ref,
|
|
657
|
+
...omit(props, Iterator_default.RESERVED_PROPS),
|
|
658
|
+
children: renderedList
|
|
659
|
+
});
|
|
576
660
|
});
|
|
577
661
|
const name$4 = `${PKG_NAME}/List`;
|
|
578
662
|
Component$5.displayName = name$4;
|
|
579
663
|
Component$5.pkgName = PKG_NAME;
|
|
580
664
|
Component$5.VITUS_LABS__COMPONENT = name$4;
|
|
581
665
|
|
|
582
|
-
|
|
666
|
+
//#endregion
|
|
667
|
+
//#region src/List/withActiveState.tsx
|
|
668
|
+
/**
|
|
669
|
+
* HOC that adds single or multi selection state management to a list component.
|
|
670
|
+
* Tracks which items are active via a scalar key (single mode) or a Map of
|
|
671
|
+
* key-to-boolean entries (multi mode). Injects `itemProps` callback that
|
|
672
|
+
* provides each item with `active`, `handleItemActive`, `toggleItemActive`,
|
|
673
|
+
* and other selection helpers. Supports `activeItemRequired` to prevent
|
|
674
|
+
* deselecting the last active item.
|
|
675
|
+
*/
|
|
583
676
|
const RESERVED_KEYS = [
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
677
|
+
"type",
|
|
678
|
+
"activeItems",
|
|
679
|
+
"itemProps",
|
|
680
|
+
"activeItemRequired"
|
|
588
681
|
];
|
|
589
682
|
const component = (WrappedComponent) => {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
if (type === 'single')
|
|
674
|
-
return innerActiveItems === key;
|
|
675
|
-
if (type === 'multi' && innerActiveItems instanceof Map) {
|
|
676
|
-
return !!innerActiveItems.get(key);
|
|
677
|
-
}
|
|
678
|
-
return false;
|
|
679
|
-
};
|
|
680
|
-
const attachMultipleProps = {
|
|
681
|
-
unsetAllItemsActive,
|
|
682
|
-
};
|
|
683
|
-
const attachItemProps = (props) => {
|
|
684
|
-
const { key } = props;
|
|
685
|
-
const defaultItemProps = typeof itemProps === 'object' ? itemProps : itemProps(props);
|
|
686
|
-
const result = {
|
|
687
|
-
...defaultItemProps,
|
|
688
|
-
active: isItemActive(key),
|
|
689
|
-
handleItemActive: () => handleItemActive(key),
|
|
690
|
-
setItemActive,
|
|
691
|
-
unsetItemActive,
|
|
692
|
-
toggleItemActive,
|
|
693
|
-
...(type === 'multi' ? attachMultipleProps : {}),
|
|
694
|
-
};
|
|
695
|
-
return result;
|
|
696
|
-
};
|
|
697
|
-
useEffect(() => {
|
|
698
|
-
if (type === 'single' && Array.isArray(activeItems)) {
|
|
699
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
700
|
-
// eslint-disable-next-line no-console
|
|
701
|
-
console.error('When type=`single` activeItems must be a single value, not an array');
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
}, [type, activeItems]);
|
|
705
|
-
return React.createElement(WrappedComponent, { ...rest, itemProps: attachItemProps });
|
|
706
|
-
};
|
|
707
|
-
Enhanced.RESERVED_KEYS = RESERVED_KEYS;
|
|
708
|
-
Enhanced.displayName = `@vitus-labs/elements/List/withActiveState(${displayName})`;
|
|
709
|
-
return Enhanced;
|
|
683
|
+
const displayName = WrappedComponent.displayName || WrappedComponent.name || "Component";
|
|
684
|
+
const Enhanced = (props) => {
|
|
685
|
+
const { type = "single", activeItemRequired, activeItems, itemProps = {}, ...rest } = props;
|
|
686
|
+
const initActiveItems = () => {
|
|
687
|
+
if (type === "single") {
|
|
688
|
+
if (!Array.isArray(activeItems)) return activeItems;
|
|
689
|
+
} else if (type === "multi") {
|
|
690
|
+
const activeItemsHelper = Array.isArray(activeItems) ? activeItems : [activeItems];
|
|
691
|
+
return new Map(activeItemsHelper.map((id) => [id, true]));
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
const [innerActiveItems, setActiveItems] = useState(initActiveItems());
|
|
695
|
+
const countActiveItems = (data) => {
|
|
696
|
+
let result = 0;
|
|
697
|
+
data.forEach((value) => {
|
|
698
|
+
if (value) result += 1;
|
|
699
|
+
});
|
|
700
|
+
return result;
|
|
701
|
+
};
|
|
702
|
+
const updateItemState = (key) => {
|
|
703
|
+
if (type === "single") setActiveItems((prevState) => {
|
|
704
|
+
if (activeItemRequired) return key;
|
|
705
|
+
if (prevState === key) return void 0;
|
|
706
|
+
return key;
|
|
707
|
+
});
|
|
708
|
+
else if (type === "multi") setActiveItems((prevState) => {
|
|
709
|
+
const activeItems = new Map(prevState);
|
|
710
|
+
if (activeItemRequired && activeItems.get(key) && countActiveItems(activeItems) === 1) return activeItems;
|
|
711
|
+
activeItems.set(key, !activeItems.get(key));
|
|
712
|
+
return activeItems;
|
|
713
|
+
});
|
|
714
|
+
else setActiveItems(void 0);
|
|
715
|
+
};
|
|
716
|
+
const handleItemActive = (key) => {
|
|
717
|
+
updateItemState(key);
|
|
718
|
+
};
|
|
719
|
+
const updateAllItemsState = (status) => {
|
|
720
|
+
if (!status) setActiveItems(/* @__PURE__ */ new Map());
|
|
721
|
+
};
|
|
722
|
+
const setItemActive = (key) => {
|
|
723
|
+
updateItemState(key);
|
|
724
|
+
};
|
|
725
|
+
const unsetItemActive = (key) => {
|
|
726
|
+
updateItemState(key);
|
|
727
|
+
};
|
|
728
|
+
const toggleItemActive = (key) => {
|
|
729
|
+
updateItemState(key);
|
|
730
|
+
};
|
|
731
|
+
const unsetAllItemsActive = () => {
|
|
732
|
+
updateAllItemsState(false);
|
|
733
|
+
};
|
|
734
|
+
const isItemActive = (key) => {
|
|
735
|
+
if (!innerActiveItems) return false;
|
|
736
|
+
if (type === "single") return innerActiveItems === key;
|
|
737
|
+
if (type === "multi" && innerActiveItems instanceof Map) return !!innerActiveItems.get(key);
|
|
738
|
+
return false;
|
|
739
|
+
};
|
|
740
|
+
const attachMultipleProps = { unsetAllItemsActive };
|
|
741
|
+
const attachItemProps = (props) => {
|
|
742
|
+
const { key } = props;
|
|
743
|
+
return {
|
|
744
|
+
...typeof itemProps === "object" ? itemProps : itemProps(props),
|
|
745
|
+
active: isItemActive(key),
|
|
746
|
+
handleItemActive: () => handleItemActive(key),
|
|
747
|
+
setItemActive,
|
|
748
|
+
unsetItemActive,
|
|
749
|
+
toggleItemActive,
|
|
750
|
+
...type === "multi" ? attachMultipleProps : {}
|
|
751
|
+
};
|
|
752
|
+
};
|
|
753
|
+
useEffect(() => {
|
|
754
|
+
if (type === "single" && Array.isArray(activeItems)) {
|
|
755
|
+
if (process.env.NODE_ENV !== "production") console.warn("[@vitus-labs/elements] List/withActiveState: `activeItems` was passed as an array but `type` is \"single\". In single selection mode, `activeItems` should be a single key (string | number). The array value will be ignored.");
|
|
756
|
+
}
|
|
757
|
+
}, [type, activeItems]);
|
|
758
|
+
return /* @__PURE__ */ jsx(WrappedComponent, {
|
|
759
|
+
...rest,
|
|
760
|
+
itemProps: attachItemProps
|
|
761
|
+
});
|
|
762
|
+
};
|
|
763
|
+
Enhanced.RESERVED_KEYS = RESERVED_KEYS;
|
|
764
|
+
Enhanced.displayName = `@vitus-labs/elements/List/withActiveState(${displayName})`;
|
|
765
|
+
return Enhanced;
|
|
710
766
|
};
|
|
711
767
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
768
|
+
//#endregion
|
|
769
|
+
//#region src/List/index.ts
|
|
770
|
+
var List_default = Component$5;
|
|
771
|
+
|
|
772
|
+
//#endregion
|
|
773
|
+
//#region src/Portal/component.ts
|
|
774
|
+
/**
|
|
775
|
+
* Portal component that creates a new DOM element on mount, appends it to
|
|
776
|
+
* the target location (defaults to document.body), and uses React's
|
|
777
|
+
* createPortal to render children into it. The DOM element is cleaned up
|
|
778
|
+
* on unmount. Accepts a custom DOMLocation for rendering into specific
|
|
779
|
+
* containers (e.g., a modal root).
|
|
780
|
+
*/
|
|
781
|
+
const Component$4 = ({ DOMLocation, tag = "div", children }) => {
|
|
782
|
+
const [element, setElement] = useState();
|
|
783
|
+
useEffect(() => {
|
|
784
|
+
if (!tag) return void 0;
|
|
785
|
+
const position = DOMLocation ?? document.body;
|
|
786
|
+
const element = document.createElement(tag);
|
|
787
|
+
setElement(element);
|
|
788
|
+
position.appendChild(element);
|
|
789
|
+
return () => {
|
|
790
|
+
position.removeChild(element);
|
|
791
|
+
};
|
|
792
|
+
}, [tag, DOMLocation]);
|
|
793
|
+
if (!tag || !element) return null;
|
|
794
|
+
return createPortal(children, element);
|
|
728
795
|
};
|
|
729
|
-
// ----------------------------------------------
|
|
730
|
-
// DEFINE STATICS
|
|
731
|
-
// ----------------------------------------------
|
|
732
796
|
const name$3 = `${PKG_NAME}/Portal`;
|
|
733
797
|
Component$4.displayName = name$3;
|
|
734
798
|
Component$4.pkgName = PKG_NAME;
|
|
735
799
|
Component$4.VITUS_LABS__COMPONENT = name$3;
|
|
736
800
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
801
|
+
//#endregion
|
|
802
|
+
//#region src/Portal/index.ts
|
|
803
|
+
var Portal_default = Component$4;
|
|
804
|
+
|
|
805
|
+
//#endregion
|
|
806
|
+
//#region src/Overlay/context.tsx
|
|
807
|
+
/**
|
|
808
|
+
* Context for nested overlay coordination. When a child overlay opens, it
|
|
809
|
+
* sets the parent's blocked state to true, preventing the parent from
|
|
810
|
+
* closing in response to click/hover events that belong to the child.
|
|
811
|
+
*/
|
|
812
|
+
const context$1 = createContext({});
|
|
813
|
+
const { Provider: Provider$1 } = context$1;
|
|
814
|
+
const useOverlayContext = () => useContext(context$1);
|
|
815
|
+
const Component = ({ children, blocked, setBlocked, setUnblocked }) => {
|
|
816
|
+
return /* @__PURE__ */ jsx(Provider$1, {
|
|
817
|
+
value: useMemo(() => ({
|
|
818
|
+
blocked,
|
|
819
|
+
setBlocked,
|
|
820
|
+
setUnblocked
|
|
821
|
+
}), [
|
|
822
|
+
blocked,
|
|
823
|
+
setBlocked,
|
|
824
|
+
setUnblocked
|
|
825
|
+
]),
|
|
826
|
+
children
|
|
827
|
+
});
|
|
747
828
|
};
|
|
748
829
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
830
|
+
//#endregion
|
|
831
|
+
//#region src/Overlay/useOverlay.tsx
|
|
832
|
+
/**
|
|
833
|
+
* Core hook powering the Overlay component. Manages open/close state, DOM
|
|
834
|
+
* event listeners (click, hover, scroll, resize, ESC key), and dynamic
|
|
835
|
+
* positioning of overlay content relative to its trigger. Supports dropdown,
|
|
836
|
+
* tooltip, popover, and modal types with automatic edge-of-viewport flipping.
|
|
837
|
+
* Event handlers are throttled for performance, and nested overlay blocking
|
|
838
|
+
* is coordinated through the overlay context.
|
|
839
|
+
*/
|
|
840
|
+
const sel = (cond, a, b) => cond ? a : b;
|
|
841
|
+
const devWarn = (msg) => {
|
|
842
|
+
if (!IS_DEVELOPMENT) return;
|
|
843
|
+
console.warn(msg);
|
|
844
|
+
};
|
|
845
|
+
const calcDropdownVertical = (c, t, align, alignX, offsetX, offsetY) => {
|
|
846
|
+
const pos = {};
|
|
847
|
+
const topPos = t.top - offsetY - c.height;
|
|
848
|
+
const bottomPos = t.bottom + offsetY;
|
|
849
|
+
const leftPos = t.left + offsetX;
|
|
850
|
+
const rightPos = t.right - offsetX - c.width;
|
|
851
|
+
const fitsTop = topPos >= 0;
|
|
852
|
+
const fitsBottom = bottomPos + c.height <= window.innerHeight;
|
|
853
|
+
const fitsLeft = leftPos + c.width <= window.innerWidth;
|
|
854
|
+
const fitsRight = rightPos >= 0;
|
|
855
|
+
const useTop = sel(align === "top", fitsTop, !fitsBottom);
|
|
856
|
+
pos.top = sel(useTop, topPos, bottomPos);
|
|
857
|
+
const resolvedAlignY = sel(useTop, "top", "bottom");
|
|
858
|
+
let resolvedAlignX = alignX;
|
|
859
|
+
if (alignX === "left") {
|
|
860
|
+
pos.left = sel(fitsLeft, leftPos, rightPos);
|
|
861
|
+
resolvedAlignX = sel(fitsLeft, "left", "right");
|
|
862
|
+
} else if (alignX === "right") {
|
|
863
|
+
pos.left = sel(fitsRight, rightPos, leftPos);
|
|
864
|
+
resolvedAlignX = sel(fitsRight, "right", "left");
|
|
865
|
+
} else {
|
|
866
|
+
const center = t.left + (t.right - t.left) / 2 - c.width / 2;
|
|
867
|
+
const fitsCL = center >= 0;
|
|
868
|
+
const fitsCR = center + c.width <= window.innerWidth;
|
|
869
|
+
if (fitsCL && fitsCR) {
|
|
870
|
+
resolvedAlignX = "center";
|
|
871
|
+
pos.left = center;
|
|
872
|
+
} else if (fitsCL) {
|
|
873
|
+
resolvedAlignX = "left";
|
|
874
|
+
pos.left = leftPos;
|
|
875
|
+
} else if (fitsCR) {
|
|
876
|
+
resolvedAlignX = "right";
|
|
877
|
+
pos.left = rightPos;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return {
|
|
881
|
+
pos,
|
|
882
|
+
resolvedAlignX,
|
|
883
|
+
resolvedAlignY
|
|
884
|
+
};
|
|
885
|
+
};
|
|
886
|
+
const calcDropdownHorizontal = (c, t, align, alignY, offsetX, offsetY) => {
|
|
887
|
+
const pos = {};
|
|
888
|
+
const leftPos = t.left - offsetX - c.width;
|
|
889
|
+
const rightPos = t.right + offsetX;
|
|
890
|
+
const topPos = t.top + offsetY;
|
|
891
|
+
const bottomPos = t.bottom - offsetY - c.height;
|
|
892
|
+
const fitsLeft = leftPos >= 0;
|
|
893
|
+
const fitsRight = rightPos + c.width <= window.innerWidth;
|
|
894
|
+
const fitsTop = topPos + c.height <= window.innerHeight;
|
|
895
|
+
const fitsBottom = bottomPos >= 0;
|
|
896
|
+
const useLeft = sel(align === "left", fitsLeft, !fitsRight);
|
|
897
|
+
pos.left = sel(useLeft, leftPos, rightPos);
|
|
898
|
+
const resolvedAlignX = sel(useLeft, "left", "right");
|
|
899
|
+
let resolvedAlignY = alignY;
|
|
900
|
+
if (alignY === "top") {
|
|
901
|
+
pos.top = sel(fitsTop, topPos, bottomPos);
|
|
902
|
+
resolvedAlignY = sel(fitsTop, "top", "bottom");
|
|
903
|
+
} else if (alignY === "bottom") {
|
|
904
|
+
pos.top = sel(fitsBottom, bottomPos, topPos);
|
|
905
|
+
resolvedAlignY = sel(fitsBottom, "bottom", "top");
|
|
906
|
+
} else {
|
|
907
|
+
const center = t.top + (t.bottom - t.top) / 2 - c.height / 2;
|
|
908
|
+
const fitsCT = center >= 0;
|
|
909
|
+
const fitsCB = center + c.height <= window.innerHeight;
|
|
910
|
+
if (fitsCT && fitsCB) {
|
|
911
|
+
resolvedAlignY = "center";
|
|
912
|
+
pos.top = center;
|
|
913
|
+
} else if (fitsCT) {
|
|
914
|
+
resolvedAlignY = "top";
|
|
915
|
+
pos.top = topPos;
|
|
916
|
+
} else if (fitsCB) {
|
|
917
|
+
resolvedAlignY = "bottom";
|
|
918
|
+
pos.top = bottomPos;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return {
|
|
922
|
+
pos,
|
|
923
|
+
resolvedAlignX,
|
|
924
|
+
resolvedAlignY
|
|
925
|
+
};
|
|
926
|
+
};
|
|
927
|
+
const calcModalPos = (c, alignX, alignY, offsetX, offsetY) => {
|
|
928
|
+
const pos = {};
|
|
929
|
+
switch (alignX) {
|
|
930
|
+
case "right":
|
|
931
|
+
pos.right = offsetX;
|
|
932
|
+
break;
|
|
933
|
+
case "left":
|
|
934
|
+
pos.left = offsetX;
|
|
935
|
+
break;
|
|
936
|
+
case "center":
|
|
937
|
+
pos.left = window.innerWidth / 2 - c.width / 2;
|
|
938
|
+
break;
|
|
939
|
+
default: pos.right = offsetX;
|
|
940
|
+
}
|
|
941
|
+
switch (alignY) {
|
|
942
|
+
case "top":
|
|
943
|
+
pos.top = offsetY;
|
|
944
|
+
break;
|
|
945
|
+
case "center":
|
|
946
|
+
pos.top = window.innerHeight / 2 - c.height / 2;
|
|
947
|
+
break;
|
|
948
|
+
case "bottom":
|
|
949
|
+
pos.bottom = offsetY;
|
|
950
|
+
break;
|
|
951
|
+
default: pos.top = offsetY;
|
|
952
|
+
}
|
|
953
|
+
return pos;
|
|
954
|
+
};
|
|
955
|
+
const adjustForAncestor = (pos, ancestor) => {
|
|
956
|
+
if (ancestor.top === 0 && ancestor.left === 0) return pos;
|
|
957
|
+
const result = { ...pos };
|
|
958
|
+
if (typeof result.top === "number") result.top -= ancestor.top;
|
|
959
|
+
if (typeof result.bottom === "number") result.bottom += ancestor.top;
|
|
960
|
+
if (typeof result.left === "number") result.left -= ancestor.left;
|
|
961
|
+
if (typeof result.right === "number") result.right += ancestor.left;
|
|
962
|
+
return result;
|
|
963
|
+
};
|
|
964
|
+
const computePosition = (type, align, alignX, alignY, offsetX, offsetY, triggerEl, contentEl, ancestorOffset) => {
|
|
965
|
+
const isDropdown = [
|
|
966
|
+
"dropdown",
|
|
967
|
+
"tooltip",
|
|
968
|
+
"popover"
|
|
969
|
+
].includes(type);
|
|
970
|
+
if (isDropdown && (!triggerEl || !contentEl)) {
|
|
971
|
+
devWarn(`[@vitus-labs/elements] Overlay (${type}): ${triggerEl ? "contentRef" : "triggerRef"} is not attached. Position cannot be calculated without both refs.`);
|
|
972
|
+
return { pos: {} };
|
|
973
|
+
}
|
|
974
|
+
if (isDropdown && triggerEl && contentEl) {
|
|
975
|
+
const c = contentEl.getBoundingClientRect();
|
|
976
|
+
const t = triggerEl.getBoundingClientRect();
|
|
977
|
+
const result = align === "top" || align === "bottom" ? calcDropdownVertical(c, t, align, alignX, offsetX, offsetY) : calcDropdownHorizontal(c, t, align, alignY, offsetX, offsetY);
|
|
978
|
+
return {
|
|
979
|
+
pos: adjustForAncestor(result.pos, ancestorOffset),
|
|
980
|
+
resolvedAlignX: result.resolvedAlignX,
|
|
981
|
+
resolvedAlignY: result.resolvedAlignY
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
if (type === "modal") {
|
|
985
|
+
if (!contentEl) {
|
|
986
|
+
devWarn("[@vitus-labs/elements] Overlay (modal): contentRef is not attached. Modal position cannot be calculated without a content element.");
|
|
987
|
+
return { pos: {} };
|
|
988
|
+
}
|
|
989
|
+
return { pos: adjustForAncestor(calcModalPos(contentEl.getBoundingClientRect(), alignX, alignY, offsetX, offsetY), ancestorOffset) };
|
|
990
|
+
}
|
|
991
|
+
return { pos: {} };
|
|
992
|
+
};
|
|
993
|
+
const processVisibilityEvent = (e, active, openOn, closeOn, isTrigger, isContent, showContent, hideContent) => {
|
|
994
|
+
if (!active && openOn === "click" && e.type === "click" && isTrigger(e)) {
|
|
995
|
+
showContent();
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
if (!active) return;
|
|
999
|
+
if (closeOn === "hover" && e.type === "scroll") {
|
|
1000
|
+
hideContent();
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
if (e.type !== "click") return;
|
|
1004
|
+
if (closeOn === "click") hideContent();
|
|
1005
|
+
else if (closeOn === "clickOnTrigger" && isTrigger(e)) hideContent();
|
|
1006
|
+
else if (closeOn === "clickOutsideContent" && !isContent(e)) hideContent();
|
|
1007
|
+
};
|
|
1008
|
+
const useOverlay = ({ isOpen = false, openOn = "click", closeOn = "click", type = "dropdown", position = "fixed", align = "bottom", alignX = "left", alignY = "bottom", offsetX = 0, offsetY = 0, throttleDelay = 200, parentContainer, closeOnEsc = true, disabled, onOpen, onClose } = {}) => {
|
|
1009
|
+
const { rootSize } = useContext(context);
|
|
1010
|
+
const ctx = useOverlayContext();
|
|
1011
|
+
const [isContentLoaded, setContentLoaded] = useState(false);
|
|
1012
|
+
const [innerAlignX, setInnerAlignX] = useState(alignX);
|
|
1013
|
+
const [innerAlignY, setInnerAlignY] = useState(alignY);
|
|
1014
|
+
const [blocked, handleBlocked] = useState(false);
|
|
1015
|
+
const [active, handleActive] = useState(isOpen);
|
|
1016
|
+
const triggerRef = useRef(null);
|
|
1017
|
+
const contentRef = useRef(null);
|
|
1018
|
+
const setBlocked = useCallback(() => handleBlocked(true), []);
|
|
1019
|
+
const setUnblocked = useCallback(() => handleBlocked(false), []);
|
|
1020
|
+
const showContent = useCallback(() => {
|
|
1021
|
+
handleActive(true);
|
|
1022
|
+
}, []);
|
|
1023
|
+
const hideContent = useCallback(() => {
|
|
1024
|
+
handleActive(false);
|
|
1025
|
+
}, []);
|
|
1026
|
+
const getAncestorOffset = useCallback(() => {
|
|
1027
|
+
if (position !== "absolute" || !contentRef.current) return {
|
|
1028
|
+
top: 0,
|
|
1029
|
+
left: 0
|
|
1030
|
+
};
|
|
1031
|
+
const offsetParent = contentRef.current.offsetParent;
|
|
1032
|
+
if (!offsetParent || offsetParent === document.body) return {
|
|
1033
|
+
top: 0,
|
|
1034
|
+
left: 0
|
|
1035
|
+
};
|
|
1036
|
+
const rect = offsetParent.getBoundingClientRect();
|
|
1037
|
+
return {
|
|
1038
|
+
top: rect.top,
|
|
1039
|
+
left: rect.left
|
|
1040
|
+
};
|
|
1041
|
+
}, [position]);
|
|
1042
|
+
const calculateContentPosition = useCallback(() => {
|
|
1043
|
+
if (!active || !isContentLoaded) return {};
|
|
1044
|
+
const result = computePosition(type, align, alignX, alignY, offsetX, offsetY, triggerRef.current, contentRef.current, getAncestorOffset());
|
|
1045
|
+
if (result.resolvedAlignX) setInnerAlignX(result.resolvedAlignX);
|
|
1046
|
+
if (result.resolvedAlignY) setInnerAlignY(result.resolvedAlignY);
|
|
1047
|
+
return result.pos;
|
|
1048
|
+
}, [
|
|
1049
|
+
isContentLoaded,
|
|
1050
|
+
active,
|
|
1051
|
+
align,
|
|
1052
|
+
alignX,
|
|
1053
|
+
alignY,
|
|
1054
|
+
offsetX,
|
|
1055
|
+
offsetY,
|
|
1056
|
+
type,
|
|
1057
|
+
getAncestorOffset
|
|
1058
|
+
]);
|
|
1059
|
+
const assignContentPosition = useCallback((values = {}) => {
|
|
1060
|
+
if (!contentRef.current) return;
|
|
1061
|
+
const el = contentRef.current;
|
|
1062
|
+
const setValue = (param) => value(param, rootSize);
|
|
1063
|
+
el.style.position = position;
|
|
1064
|
+
el.style.top = values.top != null ? setValue(values.top) : "";
|
|
1065
|
+
el.style.bottom = values.bottom != null ? setValue(values.bottom) : "";
|
|
1066
|
+
el.style.left = values.left != null ? setValue(values.left) : "";
|
|
1067
|
+
el.style.right = values.right != null ? setValue(values.right) : "";
|
|
1068
|
+
}, [position, rootSize]);
|
|
1069
|
+
const setContentPosition = useCallback(() => {
|
|
1070
|
+
assignContentPosition(calculateContentPosition());
|
|
1071
|
+
}, [assignContentPosition, calculateContentPosition]);
|
|
1072
|
+
const isNodeOrChild = useCallback((ref) => (e) => {
|
|
1073
|
+
if (e?.target && ref.current) return ref.current.contains(e.target) || e.target === ref.current;
|
|
1074
|
+
return false;
|
|
1075
|
+
}, []);
|
|
1076
|
+
const handleVisibilityByEventType = useCallback((e) => {
|
|
1077
|
+
if (blocked || disabled) return;
|
|
1078
|
+
processVisibilityEvent(e, active, openOn, closeOn, isNodeOrChild(triggerRef), isNodeOrChild(contentRef), showContent, hideContent);
|
|
1079
|
+
}, [
|
|
1080
|
+
active,
|
|
1081
|
+
blocked,
|
|
1082
|
+
disabled,
|
|
1083
|
+
openOn,
|
|
1084
|
+
closeOn,
|
|
1085
|
+
hideContent,
|
|
1086
|
+
showContent,
|
|
1087
|
+
isNodeOrChild
|
|
1088
|
+
]);
|
|
1089
|
+
const latestSetContentPosition = useRef(setContentPosition);
|
|
1090
|
+
latestSetContentPosition.current = setContentPosition;
|
|
1091
|
+
const latestHandleVisibility = useRef(handleVisibilityByEventType);
|
|
1092
|
+
latestHandleVisibility.current = handleVisibilityByEventType;
|
|
1093
|
+
const handleContentPosition = useMemo(() => throttle(() => latestSetContentPosition.current(), throttleDelay), [throttleDelay]);
|
|
1094
|
+
const handleClick = handleVisibilityByEventType;
|
|
1095
|
+
const handleVisibility = useMemo(() => throttle((e) => latestHandleVisibility.current(e), throttleDelay), [throttleDelay]);
|
|
1096
|
+
useEffect(() => {
|
|
1097
|
+
setInnerAlignX(alignX);
|
|
1098
|
+
setInnerAlignY(alignY);
|
|
1099
|
+
if (disabled) hideContent();
|
|
1100
|
+
}, [
|
|
1101
|
+
disabled,
|
|
1102
|
+
alignX,
|
|
1103
|
+
alignY,
|
|
1104
|
+
hideContent
|
|
1105
|
+
]);
|
|
1106
|
+
useEffect(() => {
|
|
1107
|
+
if (!active || !isContentLoaded) return void 0;
|
|
1108
|
+
setContentPosition();
|
|
1109
|
+
const rafId = requestAnimationFrame(() => setContentPosition());
|
|
1110
|
+
return () => cancelAnimationFrame(rafId);
|
|
1111
|
+
}, [
|
|
1112
|
+
active,
|
|
1113
|
+
isContentLoaded,
|
|
1114
|
+
setContentPosition
|
|
1115
|
+
]);
|
|
1116
|
+
const prevActiveRef = useRef(false);
|
|
1117
|
+
useEffect(() => {
|
|
1118
|
+
const wasActive = prevActiveRef.current;
|
|
1119
|
+
prevActiveRef.current = active;
|
|
1120
|
+
if (active && !wasActive) {
|
|
1121
|
+
onOpen?.();
|
|
1122
|
+
ctx.setBlocked?.();
|
|
1123
|
+
} else if (!active && wasActive) {
|
|
1124
|
+
setContentLoaded(false);
|
|
1125
|
+
onClose?.();
|
|
1126
|
+
ctx.setUnblocked?.();
|
|
1127
|
+
} else if (!active) setContentLoaded(false);
|
|
1128
|
+
return () => {
|
|
1129
|
+
if (active) {
|
|
1130
|
+
onClose?.();
|
|
1131
|
+
ctx.setUnblocked?.();
|
|
1132
|
+
}
|
|
1133
|
+
};
|
|
1134
|
+
}, [
|
|
1135
|
+
active,
|
|
1136
|
+
ctx,
|
|
1137
|
+
onClose,
|
|
1138
|
+
onOpen
|
|
1139
|
+
]);
|
|
1140
|
+
useEffect(() => {
|
|
1141
|
+
if (!closeOnEsc || !active || blocked) return void 0;
|
|
1142
|
+
const handleEscKey = (e) => {
|
|
1143
|
+
if (e.key === "Escape") hideContent();
|
|
1144
|
+
};
|
|
1145
|
+
window.addEventListener("keydown", handleEscKey);
|
|
1146
|
+
return () => {
|
|
1147
|
+
window.removeEventListener("keydown", handleEscKey);
|
|
1148
|
+
};
|
|
1149
|
+
}, [
|
|
1150
|
+
active,
|
|
1151
|
+
blocked,
|
|
1152
|
+
closeOnEsc,
|
|
1153
|
+
hideContent
|
|
1154
|
+
]);
|
|
1155
|
+
useEffect(() => {
|
|
1156
|
+
if (!active) return void 0;
|
|
1157
|
+
const shouldSetOverflow = type === "modal";
|
|
1158
|
+
const onScroll = (e) => {
|
|
1159
|
+
handleContentPosition();
|
|
1160
|
+
handleVisibility(e);
|
|
1161
|
+
};
|
|
1162
|
+
if (shouldSetOverflow) document.body.style.overflow = "hidden";
|
|
1163
|
+
window.addEventListener("resize", handleContentPosition);
|
|
1164
|
+
window.addEventListener("scroll", onScroll, { passive: true });
|
|
1165
|
+
return () => {
|
|
1166
|
+
if (shouldSetOverflow) document.body.style.overflow = "";
|
|
1167
|
+
window.removeEventListener("resize", handleContentPosition);
|
|
1168
|
+
window.removeEventListener("scroll", onScroll);
|
|
1169
|
+
};
|
|
1170
|
+
}, [
|
|
1171
|
+
active,
|
|
1172
|
+
type,
|
|
1173
|
+
handleVisibility,
|
|
1174
|
+
handleContentPosition
|
|
1175
|
+
]);
|
|
1176
|
+
useEffect(() => {
|
|
1177
|
+
if (!active || !parentContainer) return void 0;
|
|
1178
|
+
if (closeOn !== "hover") parentContainer.style.overflow = "hidden";
|
|
1179
|
+
const onScroll = (e) => {
|
|
1180
|
+
handleContentPosition();
|
|
1181
|
+
handleVisibility(e);
|
|
1182
|
+
};
|
|
1183
|
+
parentContainer.addEventListener("scroll", onScroll, { passive: true });
|
|
1184
|
+
return () => {
|
|
1185
|
+
parentContainer.style.overflow = "";
|
|
1186
|
+
parentContainer.removeEventListener("scroll", onScroll);
|
|
1187
|
+
};
|
|
1188
|
+
}, [
|
|
1189
|
+
active,
|
|
1190
|
+
parentContainer,
|
|
1191
|
+
closeOn,
|
|
1192
|
+
handleContentPosition,
|
|
1193
|
+
handleVisibility
|
|
1194
|
+
]);
|
|
1195
|
+
useEffect(() => {
|
|
1196
|
+
if (blocked || disabled) return void 0;
|
|
1197
|
+
if (openOn === "click" || [
|
|
1198
|
+
"click",
|
|
1199
|
+
"clickOnTrigger",
|
|
1200
|
+
"clickOutsideContent"
|
|
1201
|
+
].includes(closeOn)) window.addEventListener("click", handleClick);
|
|
1202
|
+
return () => {
|
|
1203
|
+
window.removeEventListener("click", handleClick);
|
|
1204
|
+
};
|
|
1205
|
+
}, [
|
|
1206
|
+
openOn,
|
|
1207
|
+
closeOn,
|
|
1208
|
+
blocked,
|
|
1209
|
+
disabled,
|
|
1210
|
+
handleClick
|
|
1211
|
+
]);
|
|
1212
|
+
const hoverTimeoutRef = useRef(null);
|
|
1213
|
+
useEffect(() => {
|
|
1214
|
+
if (blocked || disabled || !(openOn === "hover" || closeOn === "hover")) return void 0;
|
|
1215
|
+
const trigger = triggerRef.current;
|
|
1216
|
+
const content = contentRef.current;
|
|
1217
|
+
const clearHoverTimeout = () => {
|
|
1218
|
+
if (hoverTimeoutRef.current != null) {
|
|
1219
|
+
clearTimeout(hoverTimeoutRef.current);
|
|
1220
|
+
hoverTimeoutRef.current = null;
|
|
1221
|
+
}
|
|
1222
|
+
};
|
|
1223
|
+
const scheduleHide = () => {
|
|
1224
|
+
clearHoverTimeout();
|
|
1225
|
+
hoverTimeoutRef.current = setTimeout(hideContent, 100);
|
|
1226
|
+
};
|
|
1227
|
+
const onTriggerEnter = () => {
|
|
1228
|
+
clearHoverTimeout();
|
|
1229
|
+
if (openOn === "hover" && !active) showContent();
|
|
1230
|
+
};
|
|
1231
|
+
const onTriggerLeave = () => {
|
|
1232
|
+
if (closeOn === "hover" && active) scheduleHide();
|
|
1233
|
+
};
|
|
1234
|
+
const onContentEnter = () => {
|
|
1235
|
+
clearHoverTimeout();
|
|
1236
|
+
};
|
|
1237
|
+
const onContentLeave = () => {
|
|
1238
|
+
if (closeOn === "hover" && active) scheduleHide();
|
|
1239
|
+
};
|
|
1240
|
+
if (trigger) {
|
|
1241
|
+
trigger.addEventListener("mouseenter", onTriggerEnter);
|
|
1242
|
+
trigger.addEventListener("mouseleave", onTriggerLeave);
|
|
1243
|
+
}
|
|
1244
|
+
if (content) {
|
|
1245
|
+
content.addEventListener("mouseenter", onContentEnter);
|
|
1246
|
+
content.addEventListener("mouseleave", onContentLeave);
|
|
1247
|
+
}
|
|
1248
|
+
return () => {
|
|
1249
|
+
clearHoverTimeout();
|
|
1250
|
+
if (trigger) {
|
|
1251
|
+
trigger.removeEventListener("mouseenter", onTriggerEnter);
|
|
1252
|
+
trigger.removeEventListener("mouseleave", onTriggerLeave);
|
|
1253
|
+
}
|
|
1254
|
+
if (content) {
|
|
1255
|
+
content.removeEventListener("mouseenter", onContentEnter);
|
|
1256
|
+
content.removeEventListener("mouseleave", onContentLeave);
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
}, [
|
|
1260
|
+
active,
|
|
1261
|
+
isContentLoaded,
|
|
1262
|
+
blocked,
|
|
1263
|
+
disabled,
|
|
1264
|
+
openOn,
|
|
1265
|
+
closeOn,
|
|
1266
|
+
showContent,
|
|
1267
|
+
hideContent
|
|
1268
|
+
]);
|
|
1269
|
+
return {
|
|
1270
|
+
triggerRef,
|
|
1271
|
+
contentRef: useCallback((node) => {
|
|
1272
|
+
if (node) {
|
|
1273
|
+
contentRef.current = node;
|
|
1274
|
+
setContentLoaded(true);
|
|
1275
|
+
}
|
|
1276
|
+
}, []),
|
|
1277
|
+
active,
|
|
1278
|
+
align,
|
|
1279
|
+
alignX: innerAlignX,
|
|
1280
|
+
alignY: innerAlignY,
|
|
1281
|
+
showContent,
|
|
1282
|
+
hideContent,
|
|
1283
|
+
blocked,
|
|
1284
|
+
setBlocked,
|
|
1285
|
+
setUnblocked,
|
|
1286
|
+
Provider: Component
|
|
1287
|
+
};
|
|
1200
1288
|
};
|
|
1201
1289
|
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1290
|
+
//#endregion
|
|
1291
|
+
//#region src/Overlay/component.tsx
|
|
1292
|
+
/**
|
|
1293
|
+
* Overlay component that renders a trigger element and conditionally shows
|
|
1294
|
+
* content via a Portal. The trigger receives a ref and optional show/hide
|
|
1295
|
+
* callbacks; the content is positioned and managed by the useOverlay hook.
|
|
1296
|
+
* A context Provider wraps the content to support nested overlays (e.g.,
|
|
1297
|
+
* a dropdown inside another dropdown) via blocked-state propagation.
|
|
1298
|
+
*/
|
|
1299
|
+
const IS_BROWSER = typeof window !== "undefined";
|
|
1300
|
+
const Component$3 = ({ children, trigger, DOMLocation, triggerRefName = "ref", contentRefName = "ref", ...props }) => {
|
|
1301
|
+
const { active, triggerRef, contentRef, showContent, hideContent, align, alignX, alignY, Provider, ...ctx } = useOverlay(props);
|
|
1302
|
+
const { openOn, closeOn } = props;
|
|
1303
|
+
const passHandlers = useMemo(() => openOn === "manual" || closeOn === "manual" || closeOn === "clickOutsideContent", [openOn, closeOn]);
|
|
1304
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [render(trigger, {
|
|
1305
|
+
[triggerRefName]: triggerRef,
|
|
1306
|
+
active,
|
|
1307
|
+
...passHandlers ? {
|
|
1308
|
+
showContent,
|
|
1309
|
+
hideContent
|
|
1310
|
+
} : {}
|
|
1311
|
+
}), IS_BROWSER && active && /* @__PURE__ */ jsx(Portal_default, {
|
|
1312
|
+
DOMLocation,
|
|
1313
|
+
children: /* @__PURE__ */ jsx(Provider, {
|
|
1314
|
+
...ctx,
|
|
1315
|
+
children: render(children, {
|
|
1316
|
+
[contentRefName]: contentRef,
|
|
1317
|
+
active,
|
|
1318
|
+
align,
|
|
1319
|
+
alignX,
|
|
1320
|
+
alignY,
|
|
1321
|
+
...passHandlers ? {
|
|
1322
|
+
showContent,
|
|
1323
|
+
hideContent
|
|
1324
|
+
} : {}
|
|
1325
|
+
})
|
|
1326
|
+
})
|
|
1327
|
+
})] });
|
|
1224
1328
|
};
|
|
1225
1329
|
const name$2 = `${PKG_NAME}/Overlay`;
|
|
1226
|
-
Component$
|
|
1227
|
-
Component$
|
|
1228
|
-
Component$
|
|
1330
|
+
Component$3.displayName = name$2;
|
|
1331
|
+
Component$3.pkgName = PKG_NAME;
|
|
1332
|
+
Component$3.VITUS_LABS__COMPONENT = name$2;
|
|
1333
|
+
|
|
1334
|
+
//#endregion
|
|
1335
|
+
//#region src/Overlay/index.ts
|
|
1336
|
+
var Overlay_default = Component$3;
|
|
1229
1337
|
|
|
1338
|
+
//#endregion
|
|
1339
|
+
//#region src/Text/styled.ts
|
|
1340
|
+
/**
|
|
1341
|
+
* Styled text primitive that inherits color, font-weight, and line-height
|
|
1342
|
+
* from its parent so it blends seamlessly into any context. Additional
|
|
1343
|
+
* styles can be injected via the responsive `extraStyles` prop processed
|
|
1344
|
+
* through makeItResponsive.
|
|
1345
|
+
*/
|
|
1230
1346
|
const { styled, css, textComponent } = config;
|
|
1231
|
-
const styles = ({ css, theme: t }) => css
|
|
1347
|
+
const styles = ({ css, theme: t }) => css`
|
|
1232
1348
|
${t.extraStyles && extendCss(t.extraStyles)};
|
|
1233
1349
|
`;
|
|
1234
|
-
var
|
|
1350
|
+
var styled_default = styled(textComponent)`
|
|
1235
1351
|
color: inherit;
|
|
1236
1352
|
font-weight: inherit;
|
|
1237
1353
|
line-height: 1;
|
|
1238
1354
|
|
|
1239
1355
|
${makeItResponsive({
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1356
|
+
key: "$text",
|
|
1357
|
+
styles,
|
|
1358
|
+
css,
|
|
1359
|
+
normalize: false
|
|
1244
1360
|
})};
|
|
1245
1361
|
`;
|
|
1246
1362
|
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1363
|
+
//#endregion
|
|
1364
|
+
//#region src/Text/component.tsx
|
|
1365
|
+
const Component$2 = forwardRef(({ paragraph, label, children, tag, css, ...props }, ref) => {
|
|
1366
|
+
const renderContent = (as = void 0) => /* @__PURE__ */ jsx(styled_default, {
|
|
1367
|
+
ref,
|
|
1368
|
+
as,
|
|
1369
|
+
$text: { extraStyles: css },
|
|
1370
|
+
...props,
|
|
1371
|
+
children: children ?? label
|
|
1372
|
+
});
|
|
1373
|
+
let finalTag;
|
|
1374
|
+
if (paragraph) finalTag = "p";
|
|
1375
|
+
else finalTag = tag;
|
|
1376
|
+
return renderContent(finalTag);
|
|
1258
1377
|
});
|
|
1259
|
-
// ----------------------------------------------
|
|
1260
|
-
// DEFINE STATICS
|
|
1261
|
-
// ----------------------------------------------
|
|
1262
1378
|
const name$1 = `${PKG_NAME}/Text`;
|
|
1263
|
-
Component$
|
|
1264
|
-
Component$
|
|
1265
|
-
Component$
|
|
1266
|
-
Component$
|
|
1379
|
+
Component$2.displayName = name$1;
|
|
1380
|
+
Component$2.pkgName = PKG_NAME;
|
|
1381
|
+
Component$2.VITUS_LABS__COMPONENT = name$1;
|
|
1382
|
+
Component$2.isText = true;
|
|
1383
|
+
|
|
1384
|
+
//#endregion
|
|
1385
|
+
//#region src/Text/index.ts
|
|
1386
|
+
var Text_default = Component$2;
|
|
1267
1387
|
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1388
|
+
//#endregion
|
|
1389
|
+
//#region src/Util/component.tsx
|
|
1390
|
+
/**
|
|
1391
|
+
* Utility wrapper that injects className and/or style props into its
|
|
1392
|
+
* children without adding any DOM nodes of its own. Uses the core `render`
|
|
1393
|
+
* helper to clone children with the merged props.
|
|
1394
|
+
*/
|
|
1395
|
+
const Component$1 = ({ children, className, style }) => {
|
|
1396
|
+
const mergedClasses = useMemo(() => Array.isArray(className) ? className.join(" ") : className, [className]);
|
|
1397
|
+
const finalProps = {};
|
|
1398
|
+
if (style) finalProps.style = style;
|
|
1399
|
+
if (mergedClasses) finalProps.className = mergedClasses;
|
|
1400
|
+
return render(children, finalProps);
|
|
1276
1401
|
};
|
|
1277
1402
|
const name = `${PKG_NAME}/Util`;
|
|
1278
|
-
Component.displayName = name;
|
|
1279
|
-
Component.pkgName = PKG_NAME;
|
|
1280
|
-
Component.VITUS_LABS__COMPONENT = name;
|
|
1403
|
+
Component$1.displayName = name;
|
|
1404
|
+
Component$1.pkgName = PKG_NAME;
|
|
1405
|
+
Component$1.VITUS_LABS__COMPONENT = name;
|
|
1406
|
+
|
|
1407
|
+
//#endregion
|
|
1408
|
+
//#region src/Util/index.ts
|
|
1409
|
+
var Util_default = Component$1;
|
|
1281
1410
|
|
|
1282
|
-
|
|
1283
|
-
|
|
1411
|
+
//#endregion
|
|
1412
|
+
export { Element_default as Element, List_default as List, Overlay_default as Overlay, Component as OverlayProvider, Portal_default as Portal, Provider, Text_default as Text, Util_default as Util, useOverlay, component as withActiveState, withEqualBeforeAfter as withEqualSizeBeforeAfter };
|
|
1413
|
+
//# sourceMappingURL=index.js.map
|