@pyreon/elements 0.0.2
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 +21 -0
- package/README.md +194 -0
- package/lib/index.d.ts +426 -0
- package/lib/index.js +1147 -0
- package/package.json +48 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,1147 @@
|
|
|
1
|
+
import { Provider, alignContent, extendCss, makeItResponsive, value } from "@pyreon/unistyle";
|
|
2
|
+
import { Fragment, Portal, createContext, onMount, onUnmount, popContext, pushContext, useContext } from "@pyreon/core";
|
|
3
|
+
import { config, isEmpty, omit, pick, render, throttle } from "@pyreon/ui-core";
|
|
4
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "@pyreon/core/jsx-runtime";
|
|
5
|
+
import { signal } from "@pyreon/reactivity";
|
|
6
|
+
|
|
7
|
+
//#region src/constants.ts
|
|
8
|
+
const PKG_NAME = "@pyreon/elements";
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/utils.ts
|
|
12
|
+
const IS_DEVELOPMENT = process.env.NODE_ENV !== "production";
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/helpers/Content/styled.ts
|
|
16
|
+
/**
|
|
17
|
+
* Styled component for content areas (before/content/after). Applies
|
|
18
|
+
* responsive flex alignment, gap spacing between slots based on parent
|
|
19
|
+
* direction (margin-right for inline, margin-bottom for rows), and
|
|
20
|
+
* equalCols flex distribution. The "content" slot gets `flex: 1` to
|
|
21
|
+
* fill remaining space between before and after.
|
|
22
|
+
*/
|
|
23
|
+
const { styled: styled$2, css: css$2, component: component$1 } = config;
|
|
24
|
+
const equalColsCSS = `
|
|
25
|
+
flex: 1;
|
|
26
|
+
`;
|
|
27
|
+
const typeContentCSS = `
|
|
28
|
+
flex: 1;
|
|
29
|
+
`;
|
|
30
|
+
const gapDimensions = {
|
|
31
|
+
inline: {
|
|
32
|
+
before: "margin-right",
|
|
33
|
+
after: "margin-left"
|
|
34
|
+
},
|
|
35
|
+
reverseInline: {
|
|
36
|
+
before: "margin-right",
|
|
37
|
+
after: "margin-left"
|
|
38
|
+
},
|
|
39
|
+
rows: {
|
|
40
|
+
before: "margin-bottom",
|
|
41
|
+
after: "margin-top"
|
|
42
|
+
},
|
|
43
|
+
reverseRows: {
|
|
44
|
+
before: "margin-bottom",
|
|
45
|
+
after: "margin-top"
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const calculateGap = ({ direction, type, value: gapValue }) => {
|
|
49
|
+
if (!direction || !type || type === "content") return void 0;
|
|
50
|
+
return `${gapDimensions[direction][type]}: ${gapValue};`;
|
|
51
|
+
};
|
|
52
|
+
const styles$2 = ({ css: cssFn, theme: t, rootSize }) => cssFn`
|
|
53
|
+
${alignContent({
|
|
54
|
+
direction: t.direction,
|
|
55
|
+
alignX: t.alignX,
|
|
56
|
+
alignY: t.alignY
|
|
57
|
+
})};
|
|
58
|
+
|
|
59
|
+
${t.equalCols && equalColsCSS};
|
|
60
|
+
|
|
61
|
+
${t.gap && t.contentType && calculateGap({
|
|
62
|
+
direction: t.parentDirection,
|
|
63
|
+
type: t.contentType,
|
|
64
|
+
value: value(t.gap, rootSize)
|
|
65
|
+
})};
|
|
66
|
+
|
|
67
|
+
${t.extraStyles && extendCss(t.extraStyles)};
|
|
68
|
+
`;
|
|
69
|
+
const StyledComponent = styled$2(component$1)`
|
|
70
|
+
${`box-sizing: border-box;`};
|
|
71
|
+
|
|
72
|
+
display: flex;
|
|
73
|
+
align-self: stretch;
|
|
74
|
+
flex-wrap: wrap;
|
|
75
|
+
|
|
76
|
+
${(({ $contentType }) => $contentType === "content" && typeContentCSS)};
|
|
77
|
+
|
|
78
|
+
${makeItResponsive({
|
|
79
|
+
key: "$element",
|
|
80
|
+
styles: styles$2,
|
|
81
|
+
css: css$2,
|
|
82
|
+
normalize: true
|
|
83
|
+
})};
|
|
84
|
+
`;
|
|
85
|
+
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/helpers/Content/component.tsx
|
|
88
|
+
/**
|
|
89
|
+
* Content area used inside Element to render one of the three
|
|
90
|
+
* layout slots (before, content, after). Passes alignment, direction,
|
|
91
|
+
* gap, and equalCols styling props to the underlying styled component.
|
|
92
|
+
* Adds a `data-pyr-element` attribute in development for debugging.
|
|
93
|
+
*
|
|
94
|
+
* Children are rendered via core `render()`.
|
|
95
|
+
*/
|
|
96
|
+
const Component$9 = ({ contentType, tag, parentDirection, direction, alignX, alignY, equalCols, gap, extendCss, children, ...props }) => {
|
|
97
|
+
return /* @__PURE__ */ jsx(StyledComponent, {
|
|
98
|
+
as: tag,
|
|
99
|
+
$contentType: contentType,
|
|
100
|
+
$element: {
|
|
101
|
+
contentType,
|
|
102
|
+
parentDirection,
|
|
103
|
+
direction,
|
|
104
|
+
alignX,
|
|
105
|
+
alignY,
|
|
106
|
+
equalCols,
|
|
107
|
+
gap,
|
|
108
|
+
extraStyles: extendCss
|
|
109
|
+
},
|
|
110
|
+
...IS_DEVELOPMENT ? { "data-pyr-element": contentType } : {},
|
|
111
|
+
...props,
|
|
112
|
+
children: render(children)
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/helpers/Content/index.ts
|
|
118
|
+
var Content_default = Component$9;
|
|
119
|
+
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/helpers/Wrapper/styled.ts
|
|
122
|
+
/**
|
|
123
|
+
* Styled component for the Element wrapper layer. Handles responsive
|
|
124
|
+
* block/inline-flex display, direction, alignment, and custom CSS injection.
|
|
125
|
+
* Includes special handling for the `parentFix` / `childFix` flags that
|
|
126
|
+
* split flex behavior across two DOM nodes for button/fieldset/legend
|
|
127
|
+
* elements where a single flex container is insufficient.
|
|
128
|
+
*/
|
|
129
|
+
const { styled: styled$1, css: css$1, component } = config;
|
|
130
|
+
const childFixCSS = `
|
|
131
|
+
display: flex;
|
|
132
|
+
flex: 1;
|
|
133
|
+
width: 100%;
|
|
134
|
+
height: 100%;
|
|
135
|
+
`;
|
|
136
|
+
const parentFixCSS = `
|
|
137
|
+
flex-direction: column;
|
|
138
|
+
`;
|
|
139
|
+
const fullHeightCSS = `
|
|
140
|
+
height: 100%;
|
|
141
|
+
`;
|
|
142
|
+
const blockCSS = `
|
|
143
|
+
align-self: stretch;
|
|
144
|
+
width: 100%;
|
|
145
|
+
`;
|
|
146
|
+
const childFixPosition = (isBlock) => `display: ${isBlock ? "flex" : "inline-flex"};`;
|
|
147
|
+
const styles$1 = ({ theme: t, css: cssFn }) => cssFn`
|
|
148
|
+
${t.alignY === "block" && fullHeightCSS};
|
|
149
|
+
|
|
150
|
+
${alignContent({
|
|
151
|
+
direction: t.direction,
|
|
152
|
+
alignX: t.alignX,
|
|
153
|
+
alignY: t.alignY
|
|
154
|
+
})};
|
|
155
|
+
|
|
156
|
+
${t.block && blockCSS};
|
|
157
|
+
${t.alignY === "block" && t.block && fullHeightCSS};
|
|
158
|
+
|
|
159
|
+
${!t.childFix && childFixPosition(t.block)};
|
|
160
|
+
${t.parentFix && parentFixCSS};
|
|
161
|
+
|
|
162
|
+
${t.extraStyles && extendCss(t.extraStyles)};
|
|
163
|
+
`;
|
|
164
|
+
const platformCSS = `box-sizing: border-box;`;
|
|
165
|
+
var styled_default$1 = styled$1(component)`
|
|
166
|
+
position: relative;
|
|
167
|
+
${platformCSS};
|
|
168
|
+
|
|
169
|
+
${(({ $childFix }) => $childFix && childFixCSS)};
|
|
170
|
+
|
|
171
|
+
${makeItResponsive({
|
|
172
|
+
key: "$element",
|
|
173
|
+
styles: styles$1,
|
|
174
|
+
css: css$1,
|
|
175
|
+
normalize: true
|
|
176
|
+
})};
|
|
177
|
+
`;
|
|
178
|
+
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/helpers/Wrapper/constants.ts
|
|
181
|
+
/**
|
|
182
|
+
* HTML elements that need a two-layer DOM workaround because browsers do not
|
|
183
|
+
* fully support flexbox layout on button, fieldset, and legend elements.
|
|
184
|
+
* @see https://stackoverflow.com/questions/35464067/flexbox-not-working-on-button-or-fieldset-elements
|
|
185
|
+
*/
|
|
186
|
+
const INLINE_ELEMENTS_FLEX_FIX = {
|
|
187
|
+
button: true,
|
|
188
|
+
fieldset: true,
|
|
189
|
+
legend: true
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
//#endregion
|
|
193
|
+
//#region src/helpers/Wrapper/utils.ts
|
|
194
|
+
const isWebFixNeeded = (tag) => {
|
|
195
|
+
if (tag && tag in INLINE_ELEMENTS_FLEX_FIX) return true;
|
|
196
|
+
return false;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/helpers/Wrapper/component.tsx
|
|
201
|
+
/**
|
|
202
|
+
* Wrapper component that serves as the outermost styled container for Element.
|
|
203
|
+
* On web, it detects button/fieldset/legend tags and applies a two-layer flex
|
|
204
|
+
* fix (parent + child Styled) because these HTML elements do not natively
|
|
205
|
+
* support `display: flex` consistently across browsers.
|
|
206
|
+
*/
|
|
207
|
+
const DEV_PROPS = IS_DEVELOPMENT ? { "data-pyr-element": "Element" } : {};
|
|
208
|
+
const Component$8 = ({ children, tag, block, extendCss, direction, alignX, alignY, equalCols, isInline, ref, ...props }) => {
|
|
209
|
+
const COMMON_PROPS = {
|
|
210
|
+
...props,
|
|
211
|
+
...DEV_PROPS,
|
|
212
|
+
ref,
|
|
213
|
+
as: tag
|
|
214
|
+
};
|
|
215
|
+
const needsFix = !props.dangerouslySetInnerHTML && isWebFixNeeded(tag);
|
|
216
|
+
const normalElement = {
|
|
217
|
+
block,
|
|
218
|
+
direction,
|
|
219
|
+
alignX,
|
|
220
|
+
alignY,
|
|
221
|
+
equalCols,
|
|
222
|
+
extraStyles: extendCss
|
|
223
|
+
};
|
|
224
|
+
const parentFixElement = {
|
|
225
|
+
parentFix: true,
|
|
226
|
+
block,
|
|
227
|
+
extraStyles: extendCss
|
|
228
|
+
};
|
|
229
|
+
const childFixElement = {
|
|
230
|
+
childFix: true,
|
|
231
|
+
direction,
|
|
232
|
+
alignX,
|
|
233
|
+
alignY,
|
|
234
|
+
equalCols
|
|
235
|
+
};
|
|
236
|
+
if (!needsFix) return /* @__PURE__ */ jsx(styled_default$1, {
|
|
237
|
+
...COMMON_PROPS,
|
|
238
|
+
$element: normalElement,
|
|
239
|
+
children
|
|
240
|
+
});
|
|
241
|
+
const asTag = isInline ? "span" : "div";
|
|
242
|
+
return /* @__PURE__ */ jsx(styled_default$1, {
|
|
243
|
+
...COMMON_PROPS,
|
|
244
|
+
$element: parentFixElement,
|
|
245
|
+
children: /* @__PURE__ */ jsx(styled_default$1, {
|
|
246
|
+
as: asTag,
|
|
247
|
+
$childFix: true,
|
|
248
|
+
$element: childFixElement,
|
|
249
|
+
children
|
|
250
|
+
})
|
|
251
|
+
});
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
//#endregion
|
|
255
|
+
//#region src/helpers/Wrapper/index.ts
|
|
256
|
+
var Wrapper_default = Component$8;
|
|
257
|
+
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region src/Element/constants.ts
|
|
260
|
+
/**
|
|
261
|
+
* HTML tags that are inline-level by default. When Element renders one of
|
|
262
|
+
* these tags, child Content wrappers use `span` instead of `div` to
|
|
263
|
+
* preserve valid HTML nesting.
|
|
264
|
+
*/
|
|
265
|
+
const INLINE_ELEMENTS = {
|
|
266
|
+
span: true,
|
|
267
|
+
a: true,
|
|
268
|
+
button: true,
|
|
269
|
+
input: true,
|
|
270
|
+
label: true,
|
|
271
|
+
select: true,
|
|
272
|
+
textarea: true,
|
|
273
|
+
br: true,
|
|
274
|
+
img: true,
|
|
275
|
+
strong: true,
|
|
276
|
+
small: true,
|
|
277
|
+
code: true,
|
|
278
|
+
b: true,
|
|
279
|
+
big: true,
|
|
280
|
+
i: true,
|
|
281
|
+
tt: true,
|
|
282
|
+
abbr: true,
|
|
283
|
+
acronym: true,
|
|
284
|
+
cite: true,
|
|
285
|
+
dfn: true,
|
|
286
|
+
em: true,
|
|
287
|
+
kbd: true,
|
|
288
|
+
samp: true,
|
|
289
|
+
var: true,
|
|
290
|
+
bdo: true,
|
|
291
|
+
map: true,
|
|
292
|
+
object: true,
|
|
293
|
+
q: true,
|
|
294
|
+
script: true,
|
|
295
|
+
sub: true,
|
|
296
|
+
sup: true
|
|
297
|
+
};
|
|
298
|
+
/**
|
|
299
|
+
* HTML void/self-closing elements that cannot have children. When Element
|
|
300
|
+
* detects one of these tags, it skips rendering beforeContent/content/afterContent
|
|
301
|
+
* and returns the Wrapper alone.
|
|
302
|
+
*/
|
|
303
|
+
const EMPTY_ELEMENTS = {
|
|
304
|
+
area: true,
|
|
305
|
+
base: true,
|
|
306
|
+
br: true,
|
|
307
|
+
col: true,
|
|
308
|
+
embed: true,
|
|
309
|
+
hr: true,
|
|
310
|
+
img: true,
|
|
311
|
+
input: true,
|
|
312
|
+
keygen: true,
|
|
313
|
+
link: true,
|
|
314
|
+
textarea: true,
|
|
315
|
+
source: true,
|
|
316
|
+
track: true,
|
|
317
|
+
wbr: true
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
//#endregion
|
|
321
|
+
//#region src/Element/utils.ts
|
|
322
|
+
/** Checks whether the given HTML tag is an inline-level element, used to determine sub-tag nesting. */
|
|
323
|
+
const isInlineElement = (tag) => {
|
|
324
|
+
if (tag && tag in INLINE_ELEMENTS) return true;
|
|
325
|
+
return false;
|
|
326
|
+
};
|
|
327
|
+
/** Checks whether the given HTML tag is a void element that cannot have children. */
|
|
328
|
+
const getShouldBeEmpty = (tag) => {
|
|
329
|
+
if (tag && tag in EMPTY_ELEMENTS) return true;
|
|
330
|
+
return false;
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
//#endregion
|
|
334
|
+
//#region src/Element/component.tsx
|
|
335
|
+
/**
|
|
336
|
+
* Core building block of the elements package. Renders a three-section layout
|
|
337
|
+
* (beforeContent / content / afterContent) inside a flex Wrapper. When only
|
|
338
|
+
* content is present, the Wrapper inherits content-level alignment directly
|
|
339
|
+
* to avoid an unnecessary nesting layer. Handles HTML-specific edge cases
|
|
340
|
+
* like void elements (input, img) and inline elements (span, a) by
|
|
341
|
+
* skipping children or switching sub-tags accordingly.
|
|
342
|
+
*/
|
|
343
|
+
const equalize = (el, direction) => {
|
|
344
|
+
const beforeEl = el.firstElementChild;
|
|
345
|
+
const afterEl = el.lastElementChild;
|
|
346
|
+
if (beforeEl && afterEl && beforeEl !== afterEl) {
|
|
347
|
+
const type = direction === "rows" ? "height" : "width";
|
|
348
|
+
const prop = type === "height" ? "offsetHeight" : "offsetWidth";
|
|
349
|
+
const beforeSize = beforeEl[prop];
|
|
350
|
+
const afterSize = afterEl[prop];
|
|
351
|
+
if (Number.isInteger(beforeSize) && Number.isInteger(afterSize)) {
|
|
352
|
+
const maxSize = `${Math.max(beforeSize, afterSize)}px`;
|
|
353
|
+
beforeEl.style[type] = maxSize;
|
|
354
|
+
afterEl.style[type] = maxSize;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
const defaultDirection = "inline";
|
|
359
|
+
const defaultContentDirection = "rows";
|
|
360
|
+
const defaultAlignX = "left";
|
|
361
|
+
const defaultAlignY = "center";
|
|
362
|
+
const Component = ({ innerRef, tag, label, content, children, beforeContent, afterContent, equalBeforeAfter, 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, ref, ...props }) => {
|
|
363
|
+
const shouldBeEmpty = !!props.dangerouslySetInnerHTML || getShouldBeEmpty(tag);
|
|
364
|
+
const isSimpleElement = !beforeContent && !afterContent;
|
|
365
|
+
const CHILDREN = children ?? content ?? label;
|
|
366
|
+
const isInline = isInlineElement(tag);
|
|
367
|
+
const SUB_TAG = isInline ? "span" : void 0;
|
|
368
|
+
let wrapperDirection = direction;
|
|
369
|
+
let wrapperAlignX = alignX;
|
|
370
|
+
let wrapperAlignY = alignY;
|
|
371
|
+
if (isSimpleElement) {
|
|
372
|
+
if (contentDirection) wrapperDirection = contentDirection;
|
|
373
|
+
if (contentAlignX) wrapperAlignX = contentAlignX;
|
|
374
|
+
if (contentAlignY) wrapperAlignY = contentAlignY;
|
|
375
|
+
} else if (direction) wrapperDirection = direction;
|
|
376
|
+
else wrapperDirection = defaultDirection;
|
|
377
|
+
let equalizeRef = null;
|
|
378
|
+
const externalRef = ref ?? innerRef;
|
|
379
|
+
const mergedRef = (node) => {
|
|
380
|
+
equalizeRef = node;
|
|
381
|
+
if (typeof externalRef === "function") externalRef(node);
|
|
382
|
+
else if (externalRef != null) externalRef.current = node;
|
|
383
|
+
};
|
|
384
|
+
if (equalBeforeAfter && beforeContent && afterContent) onMount(() => {
|
|
385
|
+
if (equalizeRef) equalize(equalizeRef, direction);
|
|
386
|
+
});
|
|
387
|
+
const WRAPPER_PROPS = {
|
|
388
|
+
ref: mergedRef,
|
|
389
|
+
extendCss: css,
|
|
390
|
+
tag,
|
|
391
|
+
block,
|
|
392
|
+
direction: wrapperDirection,
|
|
393
|
+
alignX: wrapperAlignX,
|
|
394
|
+
alignY: wrapperAlignY,
|
|
395
|
+
as: void 0
|
|
396
|
+
};
|
|
397
|
+
if (shouldBeEmpty) return /* @__PURE__ */ jsx(Wrapper_default, {
|
|
398
|
+
...props,
|
|
399
|
+
...WRAPPER_PROPS
|
|
400
|
+
});
|
|
401
|
+
return /* @__PURE__ */ jsxs(Wrapper_default, {
|
|
402
|
+
...props,
|
|
403
|
+
...WRAPPER_PROPS,
|
|
404
|
+
isInline,
|
|
405
|
+
children: [
|
|
406
|
+
beforeContent && /* @__PURE__ */ jsx(Content_default, {
|
|
407
|
+
tag: SUB_TAG,
|
|
408
|
+
contentType: "before",
|
|
409
|
+
parentDirection: wrapperDirection,
|
|
410
|
+
extendCss: beforeContentCss,
|
|
411
|
+
direction: beforeContentDirection,
|
|
412
|
+
alignX: beforeContentAlignX,
|
|
413
|
+
alignY: beforeContentAlignY,
|
|
414
|
+
equalCols,
|
|
415
|
+
gap,
|
|
416
|
+
children: beforeContent
|
|
417
|
+
}),
|
|
418
|
+
isSimpleElement ? render(CHILDREN) : /* @__PURE__ */ jsx(Content_default, {
|
|
419
|
+
tag: SUB_TAG,
|
|
420
|
+
contentType: "content",
|
|
421
|
+
parentDirection: wrapperDirection,
|
|
422
|
+
extendCss: contentCss,
|
|
423
|
+
direction: contentDirection,
|
|
424
|
+
alignX: contentAlignX,
|
|
425
|
+
alignY: contentAlignY,
|
|
426
|
+
equalCols,
|
|
427
|
+
children: CHILDREN
|
|
428
|
+
}),
|
|
429
|
+
afterContent && /* @__PURE__ */ jsx(Content_default, {
|
|
430
|
+
tag: SUB_TAG,
|
|
431
|
+
contentType: "after",
|
|
432
|
+
parentDirection: wrapperDirection,
|
|
433
|
+
extendCss: afterContentCss,
|
|
434
|
+
direction: afterContentDirection,
|
|
435
|
+
alignX: afterContentAlignX,
|
|
436
|
+
alignY: afterContentAlignY,
|
|
437
|
+
equalCols,
|
|
438
|
+
gap,
|
|
439
|
+
children: afterContent
|
|
440
|
+
})
|
|
441
|
+
]
|
|
442
|
+
});
|
|
443
|
+
};
|
|
444
|
+
const name$5 = `${PKG_NAME}/Element`;
|
|
445
|
+
Component.displayName = name$5;
|
|
446
|
+
Component.pkgName = PKG_NAME;
|
|
447
|
+
Component.PYREON__COMPONENT = name$5;
|
|
448
|
+
|
|
449
|
+
//#endregion
|
|
450
|
+
//#region src/helpers/Iterator/component.tsx
|
|
451
|
+
const classifyData = (data) => {
|
|
452
|
+
const items = data.filter((item) => item != null && !(typeof item === "object" && isEmpty(item)));
|
|
453
|
+
if (items.length === 0) return null;
|
|
454
|
+
let isSimple = true;
|
|
455
|
+
let isComplex = true;
|
|
456
|
+
for (const item of items) if (typeof item === "string" || typeof item === "number") isComplex = false;
|
|
457
|
+
else if (typeof item === "object") isSimple = false;
|
|
458
|
+
else {
|
|
459
|
+
isSimple = false;
|
|
460
|
+
isComplex = false;
|
|
461
|
+
}
|
|
462
|
+
if (isSimple) return {
|
|
463
|
+
type: "simple",
|
|
464
|
+
data: items
|
|
465
|
+
};
|
|
466
|
+
if (isComplex) return {
|
|
467
|
+
type: "complex",
|
|
468
|
+
data: items
|
|
469
|
+
};
|
|
470
|
+
return null;
|
|
471
|
+
};
|
|
472
|
+
const RESERVED_PROPS = [
|
|
473
|
+
"children",
|
|
474
|
+
"component",
|
|
475
|
+
"wrapComponent",
|
|
476
|
+
"data",
|
|
477
|
+
"itemKey",
|
|
478
|
+
"valueName",
|
|
479
|
+
"itemProps",
|
|
480
|
+
"wrapProps"
|
|
481
|
+
];
|
|
482
|
+
const attachItemProps = ({ i, length }) => {
|
|
483
|
+
const position = i + 1;
|
|
484
|
+
return {
|
|
485
|
+
index: i,
|
|
486
|
+
first: position === 1,
|
|
487
|
+
last: position === length,
|
|
488
|
+
odd: position % 2 === 1,
|
|
489
|
+
even: position % 2 === 0,
|
|
490
|
+
position
|
|
491
|
+
};
|
|
492
|
+
};
|
|
493
|
+
const Component$7 = (props) => {
|
|
494
|
+
const { itemKey, valueName, children, component, data, wrapComponent: Wrapper, wrapProps, itemProps } = props;
|
|
495
|
+
const injectItemProps = typeof itemProps === "function" ? itemProps : () => itemProps;
|
|
496
|
+
const injectWrapItemProps = typeof wrapProps === "function" ? wrapProps : () => wrapProps;
|
|
497
|
+
const getKey = (item, index) => {
|
|
498
|
+
if (typeof itemKey === "function") return itemKey(item, index);
|
|
499
|
+
return index;
|
|
500
|
+
};
|
|
501
|
+
const renderChild = (child, total = 1, i = 0) => {
|
|
502
|
+
if (!itemProps && !Wrapper) return child;
|
|
503
|
+
const extendedProps = attachItemProps({
|
|
504
|
+
i,
|
|
505
|
+
length: total
|
|
506
|
+
});
|
|
507
|
+
const finalItemProps = itemProps ? injectItemProps({}, extendedProps) : {};
|
|
508
|
+
if (Wrapper) return /* @__PURE__ */ jsx(Wrapper, {
|
|
509
|
+
...wrapProps ? injectWrapItemProps({}, extendedProps) : {},
|
|
510
|
+
children: render(child, finalItemProps)
|
|
511
|
+
}, i);
|
|
512
|
+
return render(child, {
|
|
513
|
+
key: i,
|
|
514
|
+
...finalItemProps
|
|
515
|
+
});
|
|
516
|
+
};
|
|
517
|
+
const renderChildren = () => {
|
|
518
|
+
if (!children) return null;
|
|
519
|
+
if (Array.isArray(children)) return children.map((item, i) => renderChild(item, children.length, i));
|
|
520
|
+
if (children && typeof children === "object" && "type" in children && children.type === Fragment) {
|
|
521
|
+
const fragmentChildren = children.children;
|
|
522
|
+
const childrenLength = fragmentChildren.length;
|
|
523
|
+
return fragmentChildren.map((item, i) => renderChild(item, childrenLength, i));
|
|
524
|
+
}
|
|
525
|
+
return renderChild(children);
|
|
526
|
+
};
|
|
527
|
+
const renderSimpleArray = (simpleData) => {
|
|
528
|
+
const { length } = simpleData;
|
|
529
|
+
if (length === 0) return null;
|
|
530
|
+
return simpleData.map((item, i) => {
|
|
531
|
+
const key = getKey(item, i);
|
|
532
|
+
const keyName = valueName ?? "children";
|
|
533
|
+
const extendedProps = attachItemProps({
|
|
534
|
+
i,
|
|
535
|
+
length
|
|
536
|
+
});
|
|
537
|
+
const finalItemProps = {
|
|
538
|
+
...itemProps ? injectItemProps({ [keyName]: item }, extendedProps) : {},
|
|
539
|
+
[keyName]: item
|
|
540
|
+
};
|
|
541
|
+
if (Wrapper) return /* @__PURE__ */ jsx(Wrapper, {
|
|
542
|
+
...wrapProps ? injectWrapItemProps({ [keyName]: item }, extendedProps) : {},
|
|
543
|
+
children: render(component, finalItemProps)
|
|
544
|
+
}, key);
|
|
545
|
+
return render(component, {
|
|
546
|
+
key,
|
|
547
|
+
...finalItemProps
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
};
|
|
551
|
+
const getObjectKey = (item, index) => {
|
|
552
|
+
if (!itemKey) return item.key ?? item.id ?? item.itemId ?? index;
|
|
553
|
+
if (typeof itemKey === "function") return itemKey(item, index);
|
|
554
|
+
if (typeof itemKey === "string") return item[itemKey];
|
|
555
|
+
return index;
|
|
556
|
+
};
|
|
557
|
+
const renderComplexArray = (complexData) => {
|
|
558
|
+
const { length } = complexData;
|
|
559
|
+
if (length === 0) return null;
|
|
560
|
+
return complexData.map((item, i) => {
|
|
561
|
+
const { component: itemComponent, ...restItem } = item;
|
|
562
|
+
const renderItem = itemComponent ?? component;
|
|
563
|
+
const key = getObjectKey(restItem, i);
|
|
564
|
+
const extendedProps = attachItemProps({
|
|
565
|
+
i,
|
|
566
|
+
length
|
|
567
|
+
});
|
|
568
|
+
const finalItemProps = {
|
|
569
|
+
...itemProps ? injectItemProps(item, extendedProps) : {},
|
|
570
|
+
...restItem
|
|
571
|
+
};
|
|
572
|
+
if (Wrapper && !itemComponent) return /* @__PURE__ */ jsx(Wrapper, {
|
|
573
|
+
...wrapProps ? injectWrapItemProps(item, extendedProps) : {},
|
|
574
|
+
children: render(renderItem, finalItemProps)
|
|
575
|
+
}, key);
|
|
576
|
+
return render(renderItem, {
|
|
577
|
+
key,
|
|
578
|
+
...finalItemProps
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
};
|
|
582
|
+
const renderItems = () => {
|
|
583
|
+
if (children) return renderChildren();
|
|
584
|
+
if (component && Array.isArray(data)) {
|
|
585
|
+
const classified = classifyData(data);
|
|
586
|
+
if (!classified) return null;
|
|
587
|
+
if (classified.type === "simple") return renderSimpleArray(classified.data);
|
|
588
|
+
return renderComplexArray(classified.data);
|
|
589
|
+
}
|
|
590
|
+
return null;
|
|
591
|
+
};
|
|
592
|
+
return renderItems();
|
|
593
|
+
};
|
|
594
|
+
var component_default = Object.assign(Component$7, {
|
|
595
|
+
isIterator: true,
|
|
596
|
+
RESERVED_PROPS
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
//#endregion
|
|
600
|
+
//#region src/helpers/Iterator/index.ts
|
|
601
|
+
var Iterator_default = component_default;
|
|
602
|
+
|
|
603
|
+
//#endregion
|
|
604
|
+
//#region src/List/component.tsx
|
|
605
|
+
/**
|
|
606
|
+
* List component that combines Iterator (data-driven rendering) with an
|
|
607
|
+
* optional Element root wrapper. When `rootElement` is false (default),
|
|
608
|
+
* it renders a bare Iterator as a fragment. When true, the Iterator output
|
|
609
|
+
* is wrapped in an Element that receives all non-iterator props (e.g.,
|
|
610
|
+
* layout, alignment, css), allowing the list to be styled as a single block.
|
|
611
|
+
*/
|
|
612
|
+
const Component$1 = (({ rootElement = false, ref, ...props }) => {
|
|
613
|
+
const renderedList = /* @__PURE__ */ jsx(Iterator_default, { ...pick(props, Iterator_default.RESERVED_PROPS) });
|
|
614
|
+
if (!rootElement) return renderedList;
|
|
615
|
+
return /* @__PURE__ */ jsx(Component, {
|
|
616
|
+
ref,
|
|
617
|
+
...omit(props, Iterator_default.RESERVED_PROPS),
|
|
618
|
+
children: renderedList
|
|
619
|
+
});
|
|
620
|
+
});
|
|
621
|
+
const name$4 = `${PKG_NAME}/List`;
|
|
622
|
+
Component$1.displayName = name$4;
|
|
623
|
+
Component$1.pkgName = PKG_NAME;
|
|
624
|
+
Component$1.PYREON__COMPONENT = name$4;
|
|
625
|
+
|
|
626
|
+
//#endregion
|
|
627
|
+
//#region src/Overlay/context.tsx
|
|
628
|
+
const context = createContext({});
|
|
629
|
+
const useOverlayContext = () => useContext(context);
|
|
630
|
+
const Component$3 = ({ children, blocked, setBlocked, setUnblocked }) => {
|
|
631
|
+
const ctx = {
|
|
632
|
+
blocked,
|
|
633
|
+
setBlocked,
|
|
634
|
+
setUnblocked
|
|
635
|
+
};
|
|
636
|
+
pushContext(new Map([[context.id, ctx]]));
|
|
637
|
+
onUnmount(() => popContext());
|
|
638
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children });
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
//#endregion
|
|
642
|
+
//#region src/Overlay/useOverlay.tsx
|
|
643
|
+
/**
|
|
644
|
+
* Core hook powering the Overlay component. Manages open/close state, DOM
|
|
645
|
+
* event listeners (click, hover, scroll, resize, ESC key), and dynamic
|
|
646
|
+
* positioning of overlay content relative to its trigger. Supports dropdown,
|
|
647
|
+
* tooltip, popover, and modal types with automatic edge-of-viewport flipping.
|
|
648
|
+
* Event handlers are throttled for performance, and nested overlay blocking
|
|
649
|
+
* is coordinated through the overlay context.
|
|
650
|
+
*/
|
|
651
|
+
let modalOverflowCount = 0;
|
|
652
|
+
const sel = (cond, a, b) => cond ? a : b;
|
|
653
|
+
const devWarn = (msg) => {
|
|
654
|
+
if (!IS_DEVELOPMENT) return;
|
|
655
|
+
console.warn(msg);
|
|
656
|
+
};
|
|
657
|
+
const calcDropdownVertical = (c, t, align, alignX, offsetX, offsetY) => {
|
|
658
|
+
const pos = {};
|
|
659
|
+
const topPos = t.top - offsetY - c.height;
|
|
660
|
+
const bottomPos = t.bottom + offsetY;
|
|
661
|
+
const leftPos = t.left + offsetX;
|
|
662
|
+
const rightPos = t.right - offsetX - c.width;
|
|
663
|
+
const fitsTop = topPos >= 0;
|
|
664
|
+
const fitsBottom = bottomPos + c.height <= window.innerHeight;
|
|
665
|
+
const fitsLeft = leftPos + c.width <= window.innerWidth;
|
|
666
|
+
const fitsRight = rightPos >= 0;
|
|
667
|
+
const useTop = sel(align === "top", fitsTop, !fitsBottom);
|
|
668
|
+
pos.top = sel(useTop, topPos, bottomPos);
|
|
669
|
+
const resolvedAlignY = sel(useTop, "top", "bottom");
|
|
670
|
+
let resolvedAlignX = alignX;
|
|
671
|
+
if (alignX === "left") {
|
|
672
|
+
pos.left = sel(fitsLeft, leftPos, rightPos);
|
|
673
|
+
resolvedAlignX = sel(fitsLeft, "left", "right");
|
|
674
|
+
} else if (alignX === "right") {
|
|
675
|
+
pos.left = sel(fitsRight, rightPos, leftPos);
|
|
676
|
+
resolvedAlignX = sel(fitsRight, "right", "left");
|
|
677
|
+
} else {
|
|
678
|
+
const center = t.left + (t.right - t.left) / 2 - c.width / 2;
|
|
679
|
+
const fitsCL = center >= 0;
|
|
680
|
+
const fitsCR = center + c.width <= window.innerWidth;
|
|
681
|
+
if (fitsCL && fitsCR) {
|
|
682
|
+
resolvedAlignX = "center";
|
|
683
|
+
pos.left = center;
|
|
684
|
+
} else if (fitsCL) {
|
|
685
|
+
resolvedAlignX = "left";
|
|
686
|
+
pos.left = leftPos;
|
|
687
|
+
} else if (fitsCR) {
|
|
688
|
+
resolvedAlignX = "right";
|
|
689
|
+
pos.left = rightPos;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
return {
|
|
693
|
+
pos,
|
|
694
|
+
resolvedAlignX,
|
|
695
|
+
resolvedAlignY
|
|
696
|
+
};
|
|
697
|
+
};
|
|
698
|
+
const calcDropdownHorizontal = (c, t, align, alignY, offsetX, offsetY) => {
|
|
699
|
+
const pos = {};
|
|
700
|
+
const leftPos = t.left - offsetX - c.width;
|
|
701
|
+
const rightPos = t.right + offsetX;
|
|
702
|
+
const topPos = t.top + offsetY;
|
|
703
|
+
const bottomPos = t.bottom - offsetY - c.height;
|
|
704
|
+
const fitsLeft = leftPos >= 0;
|
|
705
|
+
const fitsRight = rightPos + c.width <= window.innerWidth;
|
|
706
|
+
const fitsTop = topPos + c.height <= window.innerHeight;
|
|
707
|
+
const fitsBottom = bottomPos >= 0;
|
|
708
|
+
const useLeft = sel(align === "left", fitsLeft, !fitsRight);
|
|
709
|
+
pos.left = sel(useLeft, leftPos, rightPos);
|
|
710
|
+
const resolvedAlignX = sel(useLeft, "left", "right");
|
|
711
|
+
let resolvedAlignY = alignY;
|
|
712
|
+
if (alignY === "top") {
|
|
713
|
+
pos.top = sel(fitsTop, topPos, bottomPos);
|
|
714
|
+
resolvedAlignY = sel(fitsTop, "top", "bottom");
|
|
715
|
+
} else if (alignY === "bottom") {
|
|
716
|
+
pos.top = sel(fitsBottom, bottomPos, topPos);
|
|
717
|
+
resolvedAlignY = sel(fitsBottom, "bottom", "top");
|
|
718
|
+
} else {
|
|
719
|
+
const center = t.top + (t.bottom - t.top) / 2 - c.height / 2;
|
|
720
|
+
const fitsCT = center >= 0;
|
|
721
|
+
const fitsCB = center + c.height <= window.innerHeight;
|
|
722
|
+
if (fitsCT && fitsCB) {
|
|
723
|
+
resolvedAlignY = "center";
|
|
724
|
+
pos.top = center;
|
|
725
|
+
} else if (fitsCT) {
|
|
726
|
+
resolvedAlignY = "top";
|
|
727
|
+
pos.top = topPos;
|
|
728
|
+
} else if (fitsCB) {
|
|
729
|
+
resolvedAlignY = "bottom";
|
|
730
|
+
pos.top = bottomPos;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return {
|
|
734
|
+
pos,
|
|
735
|
+
resolvedAlignX,
|
|
736
|
+
resolvedAlignY
|
|
737
|
+
};
|
|
738
|
+
};
|
|
739
|
+
const calcModalPos = (c, alignX, alignY, offsetX, offsetY) => {
|
|
740
|
+
const pos = {};
|
|
741
|
+
switch (alignX) {
|
|
742
|
+
case "right":
|
|
743
|
+
pos.right = offsetX;
|
|
744
|
+
break;
|
|
745
|
+
case "left":
|
|
746
|
+
pos.left = offsetX;
|
|
747
|
+
break;
|
|
748
|
+
case "center":
|
|
749
|
+
pos.left = window.innerWidth / 2 - c.width / 2;
|
|
750
|
+
break;
|
|
751
|
+
default: pos.right = offsetX;
|
|
752
|
+
}
|
|
753
|
+
switch (alignY) {
|
|
754
|
+
case "top":
|
|
755
|
+
pos.top = offsetY;
|
|
756
|
+
break;
|
|
757
|
+
case "center":
|
|
758
|
+
pos.top = window.innerHeight / 2 - c.height / 2;
|
|
759
|
+
break;
|
|
760
|
+
case "bottom":
|
|
761
|
+
pos.bottom = offsetY;
|
|
762
|
+
break;
|
|
763
|
+
default: pos.top = offsetY;
|
|
764
|
+
}
|
|
765
|
+
return pos;
|
|
766
|
+
};
|
|
767
|
+
const adjustForAncestor = (pos, ancestor) => {
|
|
768
|
+
if (ancestor.top === 0 && ancestor.left === 0) return pos;
|
|
769
|
+
const result = { ...pos };
|
|
770
|
+
if (typeof result.top === "number") result.top -= ancestor.top;
|
|
771
|
+
if (typeof result.bottom === "number") result.bottom += ancestor.top;
|
|
772
|
+
if (typeof result.left === "number") result.left -= ancestor.left;
|
|
773
|
+
if (typeof result.right === "number") result.right += ancestor.left;
|
|
774
|
+
return result;
|
|
775
|
+
};
|
|
776
|
+
const computePosition = (type, align, alignX, alignY, offsetX, offsetY, triggerEl, contentEl, ancestorOffset) => {
|
|
777
|
+
const isDropdown = [
|
|
778
|
+
"dropdown",
|
|
779
|
+
"tooltip",
|
|
780
|
+
"popover"
|
|
781
|
+
].includes(type);
|
|
782
|
+
if (isDropdown && (!triggerEl || !contentEl)) {
|
|
783
|
+
devWarn(`[@pyreon/elements] Overlay (${type}): ${triggerEl ? "contentRef" : "triggerRef"} is not attached. Position cannot be calculated without both refs.`);
|
|
784
|
+
return { pos: {} };
|
|
785
|
+
}
|
|
786
|
+
if (isDropdown && triggerEl && contentEl) {
|
|
787
|
+
const c = contentEl.getBoundingClientRect();
|
|
788
|
+
const t = triggerEl.getBoundingClientRect();
|
|
789
|
+
const result = align === "top" || align === "bottom" ? calcDropdownVertical(c, t, align, alignX, offsetX, offsetY) : calcDropdownHorizontal(c, t, align, alignY, offsetX, offsetY);
|
|
790
|
+
return {
|
|
791
|
+
pos: adjustForAncestor(result.pos, ancestorOffset),
|
|
792
|
+
resolvedAlignX: result.resolvedAlignX,
|
|
793
|
+
resolvedAlignY: result.resolvedAlignY
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
if (type === "modal") {
|
|
797
|
+
if (!contentEl) {
|
|
798
|
+
devWarn("[@pyreon/elements] Overlay (modal): contentRef is not attached. Modal position cannot be calculated without a content element.");
|
|
799
|
+
return { pos: {} };
|
|
800
|
+
}
|
|
801
|
+
return { pos: adjustForAncestor(calcModalPos(contentEl.getBoundingClientRect(), alignX, alignY, offsetX, offsetY), ancestorOffset) };
|
|
802
|
+
}
|
|
803
|
+
return { pos: {} };
|
|
804
|
+
};
|
|
805
|
+
const processVisibilityEvent = (e, active, openOn, closeOn, isTrigger, isContent, showContent, hideContent) => {
|
|
806
|
+
if (!active && openOn === "click" && e.type === "click" && isTrigger(e)) {
|
|
807
|
+
showContent();
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
if (!active) return;
|
|
811
|
+
if (closeOn === "hover" && e.type === "scroll") {
|
|
812
|
+
hideContent();
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
if (e.type !== "click") return;
|
|
816
|
+
if (closeOn === "click") hideContent();
|
|
817
|
+
else if (closeOn === "clickOnTrigger" && isTrigger(e)) hideContent();
|
|
818
|
+
else if (closeOn === "clickOutsideContent" && !isContent(e)) hideContent();
|
|
819
|
+
};
|
|
820
|
+
const useOverlay = ({ isOpen = false, openOn = "click", closeOn = "click", type = "dropdown", position = "fixed", align = "bottom", alignX: propAlignX = "left", alignY: propAlignY = "bottom", offsetX = 0, offsetY = 0, throttleDelay = 200, parentContainer, closeOnEsc = true, hoverDelay = 100, disabled, onOpen, onClose } = {}) => {
|
|
821
|
+
const ctx = useOverlayContext();
|
|
822
|
+
const active = signal(isOpen);
|
|
823
|
+
const isContentLoaded = signal(false);
|
|
824
|
+
const innerAlignX = signal(propAlignX);
|
|
825
|
+
const innerAlignY = signal(propAlignY);
|
|
826
|
+
const blockedCount = signal(0);
|
|
827
|
+
const blocked = () => blockedCount() > 0;
|
|
828
|
+
let triggerEl = null;
|
|
829
|
+
let contentEl = null;
|
|
830
|
+
let hoverTimeout = null;
|
|
831
|
+
const triggerRef = (node) => {
|
|
832
|
+
triggerEl = node;
|
|
833
|
+
};
|
|
834
|
+
const contentRefCallback = (node) => {
|
|
835
|
+
contentEl = node;
|
|
836
|
+
isContentLoaded.set(!!node);
|
|
837
|
+
};
|
|
838
|
+
const setBlocked = () => blockedCount.update((c) => c + 1);
|
|
839
|
+
const setUnblocked = () => blockedCount.update((c) => Math.max(0, c - 1));
|
|
840
|
+
const showContent = () => {
|
|
841
|
+
active.set(true);
|
|
842
|
+
onOpen?.();
|
|
843
|
+
ctx.setBlocked?.();
|
|
844
|
+
};
|
|
845
|
+
const hideContent = () => {
|
|
846
|
+
active.set(false);
|
|
847
|
+
isContentLoaded.set(false);
|
|
848
|
+
onClose?.();
|
|
849
|
+
ctx.setUnblocked?.();
|
|
850
|
+
};
|
|
851
|
+
const getAncestorOffset = () => {
|
|
852
|
+
if (position !== "absolute" || !contentEl) return {
|
|
853
|
+
top: 0,
|
|
854
|
+
left: 0
|
|
855
|
+
};
|
|
856
|
+
const offsetParent = contentEl.offsetParent;
|
|
857
|
+
if (!offsetParent || offsetParent === document.body) return {
|
|
858
|
+
top: 0,
|
|
859
|
+
left: 0
|
|
860
|
+
};
|
|
861
|
+
const rect = offsetParent.getBoundingClientRect();
|
|
862
|
+
return {
|
|
863
|
+
top: rect.top,
|
|
864
|
+
left: rect.left
|
|
865
|
+
};
|
|
866
|
+
};
|
|
867
|
+
const calculateContentPosition = () => {
|
|
868
|
+
if (!active() || !isContentLoaded()) return {};
|
|
869
|
+
const result = computePosition(type, align, propAlignX, propAlignY, offsetX, offsetY, triggerEl, contentEl, getAncestorOffset());
|
|
870
|
+
if (result.resolvedAlignX) innerAlignX.set(result.resolvedAlignX);
|
|
871
|
+
if (result.resolvedAlignY) innerAlignY.set(result.resolvedAlignY);
|
|
872
|
+
return result.pos;
|
|
873
|
+
};
|
|
874
|
+
const assignContentPosition = (values = {}) => {
|
|
875
|
+
if (!contentEl) return;
|
|
876
|
+
const el = contentEl;
|
|
877
|
+
const setValue = (param) => value(param, 16);
|
|
878
|
+
el.style.position = position;
|
|
879
|
+
el.style.top = values.top != null ? setValue(values.top) : "";
|
|
880
|
+
el.style.bottom = values.bottom != null ? setValue(values.bottom) : "";
|
|
881
|
+
el.style.left = values.left != null ? setValue(values.left) : "";
|
|
882
|
+
el.style.right = values.right != null ? setValue(values.right) : "";
|
|
883
|
+
};
|
|
884
|
+
const setContentPosition = () => {
|
|
885
|
+
assignContentPosition(calculateContentPosition());
|
|
886
|
+
};
|
|
887
|
+
const isNodeOrChild = (getRef) => (e) => {
|
|
888
|
+
const ref = getRef();
|
|
889
|
+
if (e?.target && ref) return ref.contains(e.target) || e.target === ref;
|
|
890
|
+
return false;
|
|
891
|
+
};
|
|
892
|
+
const handleVisibilityByEventType = (e) => {
|
|
893
|
+
if (blocked() || disabled) return;
|
|
894
|
+
processVisibilityEvent(e, active(), openOn, closeOn, isNodeOrChild(() => triggerEl), isNodeOrChild(() => contentEl), showContent, hideContent);
|
|
895
|
+
};
|
|
896
|
+
const handleContentPosition = throttle(() => setContentPosition(), throttleDelay);
|
|
897
|
+
const handleClick = (e) => handleVisibilityByEventType(e);
|
|
898
|
+
const handleVisibility = throttle((e) => handleVisibilityByEventType(e), throttleDelay);
|
|
899
|
+
const setupListeners = () => {
|
|
900
|
+
const cleanups = [];
|
|
901
|
+
if (openOn === "click" || [
|
|
902
|
+
"click",
|
|
903
|
+
"clickOnTrigger",
|
|
904
|
+
"clickOutsideContent"
|
|
905
|
+
].includes(closeOn)) {
|
|
906
|
+
window.addEventListener("click", handleClick);
|
|
907
|
+
cleanups.push(() => window.removeEventListener("click", handleClick));
|
|
908
|
+
}
|
|
909
|
+
if (closeOnEsc) {
|
|
910
|
+
const handleEscKey = (e) => {
|
|
911
|
+
if (e.key === "Escape" && active() && !blocked()) hideContent();
|
|
912
|
+
};
|
|
913
|
+
window.addEventListener("keydown", handleEscKey);
|
|
914
|
+
cleanups.push(() => window.removeEventListener("keydown", handleEscKey));
|
|
915
|
+
}
|
|
916
|
+
if (openOn === "hover" || closeOn === "hover") {
|
|
917
|
+
const clearHoverTimeout = () => {
|
|
918
|
+
if (hoverTimeout != null) {
|
|
919
|
+
clearTimeout(hoverTimeout);
|
|
920
|
+
hoverTimeout = null;
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
const scheduleHide = () => {
|
|
924
|
+
clearHoverTimeout();
|
|
925
|
+
hoverTimeout = setTimeout(hideContent, hoverDelay);
|
|
926
|
+
};
|
|
927
|
+
const onTriggerEnter = () => {
|
|
928
|
+
clearHoverTimeout();
|
|
929
|
+
if (openOn === "hover" && !active()) showContent();
|
|
930
|
+
};
|
|
931
|
+
const onTriggerLeave = () => {
|
|
932
|
+
if (closeOn === "hover" && active()) scheduleHide();
|
|
933
|
+
};
|
|
934
|
+
const onContentEnter = () => {
|
|
935
|
+
clearHoverTimeout();
|
|
936
|
+
};
|
|
937
|
+
const onContentLeave = () => {
|
|
938
|
+
if (closeOn === "hover" && active()) scheduleHide();
|
|
939
|
+
};
|
|
940
|
+
const attachHoverListeners = () => {
|
|
941
|
+
if (triggerEl) {
|
|
942
|
+
triggerEl.addEventListener("mouseenter", onTriggerEnter);
|
|
943
|
+
triggerEl.addEventListener("mouseleave", onTriggerLeave);
|
|
944
|
+
}
|
|
945
|
+
if (contentEl) {
|
|
946
|
+
contentEl.addEventListener("mouseenter", onContentEnter);
|
|
947
|
+
contentEl.addEventListener("mouseleave", onContentLeave);
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
attachHoverListeners();
|
|
951
|
+
cleanups.push(() => {
|
|
952
|
+
clearHoverTimeout();
|
|
953
|
+
if (triggerEl) {
|
|
954
|
+
triggerEl.removeEventListener("mouseenter", onTriggerEnter);
|
|
955
|
+
triggerEl.removeEventListener("mouseleave", onTriggerLeave);
|
|
956
|
+
}
|
|
957
|
+
if (contentEl) {
|
|
958
|
+
contentEl.removeEventListener("mouseenter", onContentEnter);
|
|
959
|
+
contentEl.removeEventListener("mouseleave", onContentLeave);
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
const shouldSetOverflow = type === "modal";
|
|
964
|
+
const onScroll = (e) => {
|
|
965
|
+
handleContentPosition();
|
|
966
|
+
handleVisibility(e);
|
|
967
|
+
};
|
|
968
|
+
if (shouldSetOverflow) {
|
|
969
|
+
modalOverflowCount++;
|
|
970
|
+
if (modalOverflowCount === 1) document.body.style.overflow = "hidden";
|
|
971
|
+
}
|
|
972
|
+
window.addEventListener("resize", handleContentPosition);
|
|
973
|
+
window.addEventListener("scroll", onScroll, { passive: true });
|
|
974
|
+
cleanups.push(() => {
|
|
975
|
+
handleContentPosition.cancel();
|
|
976
|
+
handleVisibility.cancel();
|
|
977
|
+
if (shouldSetOverflow) {
|
|
978
|
+
modalOverflowCount--;
|
|
979
|
+
if (modalOverflowCount === 0) document.body.style.overflow = "";
|
|
980
|
+
}
|
|
981
|
+
window.removeEventListener("resize", handleContentPosition);
|
|
982
|
+
window.removeEventListener("scroll", onScroll);
|
|
983
|
+
});
|
|
984
|
+
if (parentContainer) {
|
|
985
|
+
if (closeOn !== "hover") parentContainer.style.overflow = "hidden";
|
|
986
|
+
const onParentScroll = (e) => {
|
|
987
|
+
handleContentPosition();
|
|
988
|
+
handleVisibility(e);
|
|
989
|
+
};
|
|
990
|
+
parentContainer.addEventListener("scroll", onParentScroll, { passive: true });
|
|
991
|
+
cleanups.push(() => {
|
|
992
|
+
parentContainer.style.overflow = "";
|
|
993
|
+
parentContainer.removeEventListener("scroll", onParentScroll);
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
return () => {
|
|
997
|
+
for (const cleanup of cleanups) cleanup();
|
|
998
|
+
};
|
|
999
|
+
};
|
|
1000
|
+
if (disabled) active.set(false);
|
|
1001
|
+
return {
|
|
1002
|
+
triggerRef,
|
|
1003
|
+
contentRef: contentRefCallback,
|
|
1004
|
+
active,
|
|
1005
|
+
align,
|
|
1006
|
+
alignX: innerAlignX,
|
|
1007
|
+
alignY: innerAlignY,
|
|
1008
|
+
showContent,
|
|
1009
|
+
hideContent,
|
|
1010
|
+
blocked,
|
|
1011
|
+
setBlocked,
|
|
1012
|
+
setUnblocked,
|
|
1013
|
+
setupListeners,
|
|
1014
|
+
Provider: Component$3
|
|
1015
|
+
};
|
|
1016
|
+
};
|
|
1017
|
+
|
|
1018
|
+
//#endregion
|
|
1019
|
+
//#region src/Overlay/component.tsx
|
|
1020
|
+
const IS_BROWSER = typeof window !== "undefined";
|
|
1021
|
+
const Component$2 = ({ children, trigger, DOMLocation, triggerRefName = "ref", contentRefName = "ref", ...props }) => {
|
|
1022
|
+
const { active, triggerRef, contentRef, showContent, hideContent, align, alignX, alignY, setupListeners, Provider, ...ctx } = useOverlay(props);
|
|
1023
|
+
const { openOn, closeOn, type } = props;
|
|
1024
|
+
const passHandlers = openOn === "manual" || closeOn === "manual" || closeOn === "clickOutsideContent";
|
|
1025
|
+
const ariaHasPopup = (() => {
|
|
1026
|
+
switch (type) {
|
|
1027
|
+
case "modal": return "dialog";
|
|
1028
|
+
case "tooltip": return "true";
|
|
1029
|
+
default: return "menu";
|
|
1030
|
+
}
|
|
1031
|
+
})();
|
|
1032
|
+
onMount(() => {
|
|
1033
|
+
return setupListeners();
|
|
1034
|
+
});
|
|
1035
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [render(trigger, {
|
|
1036
|
+
[triggerRefName]: triggerRef,
|
|
1037
|
+
active: active(),
|
|
1038
|
+
"aria-expanded": active(),
|
|
1039
|
+
"aria-haspopup": ariaHasPopup,
|
|
1040
|
+
...passHandlers ? {
|
|
1041
|
+
showContent,
|
|
1042
|
+
hideContent
|
|
1043
|
+
} : {}
|
|
1044
|
+
}), () => IS_BROWSER && active() ? /* @__PURE__ */ jsx(Portal, {
|
|
1045
|
+
target: DOMLocation ?? document.body,
|
|
1046
|
+
children: /* @__PURE__ */ jsx(Provider, {
|
|
1047
|
+
...ctx,
|
|
1048
|
+
children: render(children, {
|
|
1049
|
+
[contentRefName]: contentRef,
|
|
1050
|
+
role: type === "modal" ? "dialog" : void 0,
|
|
1051
|
+
"aria-modal": type === "modal" ? true : void 0,
|
|
1052
|
+
active: active(),
|
|
1053
|
+
align,
|
|
1054
|
+
alignX: alignX(),
|
|
1055
|
+
alignY: alignY(),
|
|
1056
|
+
...passHandlers ? {
|
|
1057
|
+
showContent,
|
|
1058
|
+
hideContent
|
|
1059
|
+
} : {}
|
|
1060
|
+
})
|
|
1061
|
+
})
|
|
1062
|
+
}) : null] });
|
|
1063
|
+
};
|
|
1064
|
+
const name$3 = `${PKG_NAME}/Overlay`;
|
|
1065
|
+
Component$2.displayName = name$3;
|
|
1066
|
+
Component$2.pkgName = PKG_NAME;
|
|
1067
|
+
Component$2.PYREON__COMPONENT = name$3;
|
|
1068
|
+
|
|
1069
|
+
//#endregion
|
|
1070
|
+
//#region src/Portal/component.tsx
|
|
1071
|
+
const Component$4 = ({ DOMLocation, tag: _tag = "div", children }) => {
|
|
1072
|
+
const target = DOMLocation ?? (typeof document !== "undefined" ? document.body : void 0);
|
|
1073
|
+
if (!target) return null;
|
|
1074
|
+
return /* @__PURE__ */ jsx(Portal, {
|
|
1075
|
+
target,
|
|
1076
|
+
children
|
|
1077
|
+
});
|
|
1078
|
+
};
|
|
1079
|
+
const name$2 = `${PKG_NAME}/Portal`;
|
|
1080
|
+
Component$4.displayName = name$2;
|
|
1081
|
+
Component$4.pkgName = PKG_NAME;
|
|
1082
|
+
Component$4.PYREON__COMPONENT = name$2;
|
|
1083
|
+
|
|
1084
|
+
//#endregion
|
|
1085
|
+
//#region src/Text/styled.ts
|
|
1086
|
+
/**
|
|
1087
|
+
* Styled text primitive that inherits color, font-weight, and line-height
|
|
1088
|
+
* from its parent so it blends seamlessly into any context. Additional
|
|
1089
|
+
* styles can be injected via the responsive `extraStyles` prop processed
|
|
1090
|
+
* through makeItResponsive.
|
|
1091
|
+
*/
|
|
1092
|
+
const { styled, css, textComponent } = config;
|
|
1093
|
+
const styles = ({ css: cssFn, theme: t }) => cssFn`
|
|
1094
|
+
${t.extraStyles && extendCss(t.extraStyles)};
|
|
1095
|
+
`;
|
|
1096
|
+
var styled_default = styled(textComponent)`
|
|
1097
|
+
${css`
|
|
1098
|
+
color: inherit;
|
|
1099
|
+
font-weight: inherit;
|
|
1100
|
+
line-height: 1;
|
|
1101
|
+
`};
|
|
1102
|
+
|
|
1103
|
+
${makeItResponsive({
|
|
1104
|
+
key: "$text",
|
|
1105
|
+
styles,
|
|
1106
|
+
css,
|
|
1107
|
+
normalize: false
|
|
1108
|
+
})};
|
|
1109
|
+
`;
|
|
1110
|
+
|
|
1111
|
+
//#endregion
|
|
1112
|
+
//#region src/Text/component.tsx
|
|
1113
|
+
const Component$5 = ({ paragraph, label, children, tag, css, ref, ...props }) => {
|
|
1114
|
+
let finalTag;
|
|
1115
|
+
if (paragraph) finalTag = "p";
|
|
1116
|
+
else finalTag = tag;
|
|
1117
|
+
return /* @__PURE__ */ jsx(styled_default, {
|
|
1118
|
+
ref,
|
|
1119
|
+
as: finalTag,
|
|
1120
|
+
$text: { extraStyles: css },
|
|
1121
|
+
...props,
|
|
1122
|
+
children: children ?? label
|
|
1123
|
+
});
|
|
1124
|
+
};
|
|
1125
|
+
const name$1 = `${PKG_NAME}/Text`;
|
|
1126
|
+
Component$5.displayName = name$1;
|
|
1127
|
+
Component$5.pkgName = PKG_NAME;
|
|
1128
|
+
Component$5.PYREON__COMPONENT = name$1;
|
|
1129
|
+
Component$5.isText = true;
|
|
1130
|
+
|
|
1131
|
+
//#endregion
|
|
1132
|
+
//#region src/Util/component.tsx
|
|
1133
|
+
const Component$6 = (({ children, className, style }) => {
|
|
1134
|
+
const mergedClasses = Array.isArray(className) ? className.join(" ") : className;
|
|
1135
|
+
const finalProps = {};
|
|
1136
|
+
if (style) finalProps.style = style;
|
|
1137
|
+
if (mergedClasses) finalProps.className = mergedClasses;
|
|
1138
|
+
return render(children, finalProps);
|
|
1139
|
+
});
|
|
1140
|
+
const name = `${PKG_NAME}/Util`;
|
|
1141
|
+
Component$6.displayName = name;
|
|
1142
|
+
Component$6.pkgName = PKG_NAME;
|
|
1143
|
+
Component$6.PYREON__COMPONENT = name;
|
|
1144
|
+
|
|
1145
|
+
//#endregion
|
|
1146
|
+
export { Component as Element, Iterator_default as Iterator, Component$1 as List, Component$2 as Overlay, Component$3 as OverlayProvider, Component$4 as Portal, Provider, Component$5 as Text, Component$6 as Util, useOverlay };
|
|
1147
|
+
//# sourceMappingURL=index.js.map
|