@vitus-labs/elements 2.6.1 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +8 -9
- package/lib/index.js +275 -194
- package/lib/vitus-labs-elements.native.js +78 -75
- package/package.json +13 -12
package/lib/index.d.ts
CHANGED
|
@@ -9,8 +9,7 @@ type SpreadTwo<L, R> = Id<Pick<L, Exclude<keyof L, keyof R>> & R>;
|
|
|
9
9
|
type Spread<A extends readonly [...any]> = A extends [infer L, ...infer R] ? SpreadTwo<L, Spread<R>> : unknown;
|
|
10
10
|
type MergeTypes<A extends readonly [...any]> = ExtractNullableKeys<Spread<A>>;
|
|
11
11
|
type InnerRef = ForwardedRef<any>;
|
|
12
|
-
type
|
|
13
|
-
type Css = CssCallback | ReturnType<typeof config.css> | string;
|
|
12
|
+
type Css = ((css: typeof config.css) => ReturnType<typeof css>) | ReturnType<typeof config.css> | string;
|
|
14
13
|
type Content = Parameters<typeof render>['0'];
|
|
15
14
|
type ContentAlignX = 'left' | 'center' | 'right' | 'spaceBetween' | 'spaceAround' | 'block';
|
|
16
15
|
type ContentAlignY = 'top' | 'center' | 'bottom' | 'spaceBetween' | 'spaceAround' | 'block';
|
|
@@ -296,7 +295,7 @@ type VLElement<P extends Record<string, unknown> = {}> = ((props: Props & P & {
|
|
|
296
295
|
}) => ReactElement | null) & VLStatic;
|
|
297
296
|
//#endregion
|
|
298
297
|
//#region src/Element/component.d.ts
|
|
299
|
-
declare const
|
|
298
|
+
declare const MemoComponent: VLElement;
|
|
300
299
|
//#endregion
|
|
301
300
|
//#region src/helpers/Iterator/types.d.ts
|
|
302
301
|
type MaybeNull = undefined | null;
|
|
@@ -447,7 +446,7 @@ interface Context {
|
|
|
447
446
|
setBlocked: () => void;
|
|
448
447
|
setUnblocked: () => void;
|
|
449
448
|
}
|
|
450
|
-
declare const Component$
|
|
449
|
+
declare const Component$1: FC<Context & {
|
|
451
450
|
children: ReactNode;
|
|
452
451
|
}>;
|
|
453
452
|
//#endregion
|
|
@@ -634,7 +633,7 @@ type Props$3 = {
|
|
|
634
633
|
*/
|
|
635
634
|
contentRefName?: string;
|
|
636
635
|
} & UseOverlayProps;
|
|
637
|
-
declare const Component
|
|
636
|
+
declare const Component: VLComponent<Props$3>;
|
|
638
637
|
//#endregion
|
|
639
638
|
//#region src/Portal/component.d.ts
|
|
640
639
|
interface Props$4 {
|
|
@@ -652,7 +651,7 @@ interface Props$4 {
|
|
|
652
651
|
*/
|
|
653
652
|
tag?: string;
|
|
654
653
|
}
|
|
655
|
-
declare const Component$
|
|
654
|
+
declare const Component$2: VLComponent<Props$4>;
|
|
656
655
|
//#endregion
|
|
657
656
|
//#region src/Text/component.d.ts
|
|
658
657
|
type Props$5 = Partial<{
|
|
@@ -677,7 +676,7 @@ type Props$5 = Partial<{
|
|
|
677
676
|
*/
|
|
678
677
|
css: ExtendCss;
|
|
679
678
|
}>;
|
|
680
|
-
declare const Component$
|
|
679
|
+
declare const Component$3: VLComponent<Props$5> & {
|
|
681
680
|
isText?: true;
|
|
682
681
|
};
|
|
683
682
|
//#endregion
|
|
@@ -696,7 +695,7 @@ interface Props$6 {
|
|
|
696
695
|
*/
|
|
697
696
|
style?: Record<string, unknown>;
|
|
698
697
|
}
|
|
699
|
-
declare const Component$
|
|
698
|
+
declare const Component$4: VLComponent<Props$6>;
|
|
700
699
|
//#endregion
|
|
701
|
-
export { type AlignX, type AlignY, type Content, type ContentBoolean, type Direction,
|
|
700
|
+
export { type AlignX, type AlignY, type Content, type ContentBoolean, type Direction, MemoComponent as Element, type Props as ElementProps, type ElementType, type ExtendCss, type ExtendedProps, type InnerRef, type Props$1 as IteratorProps, _default as List, type Props$2 as ListProps, type ObjectValue, Component as Overlay, type Props$3 as OverlayProps, Component$1 as OverlayProvider, Component$2 as Portal, type Props$4 as PortalProps, type PropsCallback, Provider, type Responsive, type ResponsiveBoolType, Component$3 as Text, type Props$5 as TextProps, type UseOverlayProps, Component$4 as Util, type Props$6 as UtilProps, type VLStatic, useOverlay };
|
|
702
701
|
//# sourceMappingURL=index2.d.ts.map
|
package/lib/index.js
CHANGED
|
@@ -22,10 +22,7 @@ const IS_DEVELOPMENT = process.env.NODE_ENV !== "production";
|
|
|
22
22
|
* fill remaining space between before and after.
|
|
23
23
|
*/
|
|
24
24
|
const { styled: styled$2, css: css$2, component: component$1 } = config;
|
|
25
|
-
const
|
|
26
|
-
flex: 1;
|
|
27
|
-
`;
|
|
28
|
-
const typeContentCSS = `
|
|
25
|
+
const FLEX_1 = `
|
|
29
26
|
flex: 1;
|
|
30
27
|
`;
|
|
31
28
|
const gapDimensions = {
|
|
@@ -57,7 +54,7 @@ const styles$2 = ({ css, theme: t, rootSize }) => css`
|
|
|
57
54
|
alignY: t.alignY
|
|
58
55
|
})};
|
|
59
56
|
|
|
60
|
-
${t.equalCols &&
|
|
57
|
+
${t.equalCols && FLEX_1};
|
|
61
58
|
|
|
62
59
|
${t.gap && t.contentType && calculateGap({
|
|
63
60
|
direction: t.parentDirection,
|
|
@@ -74,7 +71,7 @@ const StyledComponent = styled$2(component$1)`
|
|
|
74
71
|
align-self: stretch;
|
|
75
72
|
flex-wrap: wrap;
|
|
76
73
|
|
|
77
|
-
${({ $contentType }) => $contentType === "content" &&
|
|
74
|
+
${({ $contentType }) => $contentType === "content" && FLEX_1};
|
|
78
75
|
|
|
79
76
|
${makeItResponsive({
|
|
80
77
|
key: "$element",
|
|
@@ -125,11 +122,11 @@ const Component$9 = ({ contentType, tag, parentDirection, direction, alignX, ali
|
|
|
125
122
|
children: render(children)
|
|
126
123
|
});
|
|
127
124
|
};
|
|
128
|
-
var component_default = memo(Component$9);
|
|
125
|
+
var component_default$1 = memo(Component$9);
|
|
129
126
|
|
|
130
127
|
//#endregion
|
|
131
128
|
//#region src/helpers/Content/index.ts
|
|
132
|
-
var Content_default = component_default;
|
|
129
|
+
var Content_default = component_default$1;
|
|
133
130
|
|
|
134
131
|
//#endregion
|
|
135
132
|
//#region src/helpers/Wrapper/styled.ts
|
|
@@ -216,23 +213,34 @@ const isWebFixNeeded = (tag) => {
|
|
|
216
213
|
* (parent + child Styled) because these HTML elements do not natively
|
|
217
214
|
* support `display: flex` consistently across browsers.
|
|
218
215
|
*/
|
|
219
|
-
const DEV_PROPS = IS_DEVELOPMENT ? { "data-vl-element": "Element" } :
|
|
216
|
+
const DEV_PROPS = IS_DEVELOPMENT ? { "data-vl-element": "Element" } : null;
|
|
220
217
|
const Component$8 = ({ children, ref, tag, block, extendCss, direction, alignX, alignY, equalCols, isInline, ...props }) => {
|
|
221
|
-
const COMMON_PROPS = {
|
|
222
|
-
...props,
|
|
223
|
-
...DEV_PROPS,
|
|
224
|
-
ref,
|
|
225
|
-
as: tag
|
|
226
|
-
};
|
|
227
218
|
const needsFix = !props.dangerouslySetInnerHTML && isWebFixNeeded(tag);
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
219
|
+
const $element = useMemo(() => {
|
|
220
|
+
if (!needsFix) return {
|
|
221
|
+
block,
|
|
222
|
+
direction,
|
|
223
|
+
alignX,
|
|
224
|
+
alignY,
|
|
225
|
+
equalCols,
|
|
226
|
+
extraStyles: extendCss
|
|
227
|
+
};
|
|
228
|
+
return {
|
|
229
|
+
parent: {
|
|
230
|
+
parentFix: true,
|
|
231
|
+
block,
|
|
232
|
+
extraStyles: extendCss
|
|
233
|
+
},
|
|
234
|
+
child: {
|
|
235
|
+
childFix: true,
|
|
236
|
+
direction,
|
|
237
|
+
alignX,
|
|
238
|
+
alignY,
|
|
239
|
+
equalCols
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}, [
|
|
243
|
+
needsFix,
|
|
236
244
|
block,
|
|
237
245
|
direction,
|
|
238
246
|
alignX,
|
|
@@ -240,44 +248,35 @@ const Component$8 = ({ children, ref, tag, block, extendCss, direction, alignX,
|
|
|
240
248
|
equalCols,
|
|
241
249
|
extendCss
|
|
242
250
|
]);
|
|
243
|
-
const parentFixElement = useMemo(() => ({
|
|
244
|
-
parentFix: true,
|
|
245
|
-
block,
|
|
246
|
-
extraStyles: extendCss
|
|
247
|
-
}), [block, extendCss]);
|
|
248
|
-
const childFixElement = useMemo(() => ({
|
|
249
|
-
childFix: true,
|
|
250
|
-
direction,
|
|
251
|
-
alignX,
|
|
252
|
-
alignY,
|
|
253
|
-
equalCols
|
|
254
|
-
}), [
|
|
255
|
-
direction,
|
|
256
|
-
alignX,
|
|
257
|
-
alignY,
|
|
258
|
-
equalCols
|
|
259
|
-
]);
|
|
260
251
|
if (!needsFix || false) return /* @__PURE__ */ jsx(styled_default$1, {
|
|
261
|
-
...
|
|
262
|
-
|
|
252
|
+
...props,
|
|
253
|
+
...DEV_PROPS,
|
|
254
|
+
ref,
|
|
255
|
+
as: tag,
|
|
256
|
+
$element,
|
|
263
257
|
children
|
|
264
258
|
});
|
|
265
259
|
const asTag = isInline ? "span" : "div";
|
|
260
|
+
const fix = $element;
|
|
266
261
|
return /* @__PURE__ */ jsx(styled_default$1, {
|
|
267
|
-
...
|
|
268
|
-
|
|
262
|
+
...props,
|
|
263
|
+
...DEV_PROPS,
|
|
264
|
+
ref,
|
|
265
|
+
as: tag,
|
|
266
|
+
$element: fix.parent,
|
|
269
267
|
children: /* @__PURE__ */ jsx(styled_default$1, {
|
|
270
268
|
as: asTag,
|
|
271
269
|
$childFix: true,
|
|
272
|
-
$element:
|
|
270
|
+
$element: fix.child,
|
|
273
271
|
children
|
|
274
272
|
})
|
|
275
273
|
});
|
|
276
274
|
};
|
|
275
|
+
var component_default = memo(Component$8);
|
|
277
276
|
|
|
278
277
|
//#endregion
|
|
279
278
|
//#region src/helpers/Wrapper/index.ts
|
|
280
|
-
var Wrapper_default =
|
|
279
|
+
var Wrapper_default = component_default;
|
|
281
280
|
|
|
282
281
|
//#endregion
|
|
283
282
|
//#region src/Element/constants.ts
|
|
@@ -333,7 +332,6 @@ const EMPTY_ELEMENTS = {
|
|
|
333
332
|
hr: true,
|
|
334
333
|
img: true,
|
|
335
334
|
input: true,
|
|
336
|
-
keygen: true,
|
|
337
335
|
link: true,
|
|
338
336
|
textarea: true,
|
|
339
337
|
source: true,
|
|
@@ -367,22 +365,22 @@ const getShouldBeEmpty = (tag) => {
|
|
|
367
365
|
const equalize = (el, direction) => {
|
|
368
366
|
const beforeEl = el.firstElementChild;
|
|
369
367
|
const afterEl = el.lastElementChild;
|
|
370
|
-
if (beforeEl
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
368
|
+
if (!beforeEl || !afterEl || beforeEl === afterEl) return;
|
|
369
|
+
const type = direction === "rows" ? "height" : "width";
|
|
370
|
+
const prop = type === "height" ? "offsetHeight" : "offsetWidth";
|
|
371
|
+
const beforeSize = beforeEl[prop];
|
|
372
|
+
const afterSize = afterEl[prop];
|
|
373
|
+
if (!Number.isInteger(beforeSize) || !Number.isInteger(afterSize)) return;
|
|
374
|
+
if (beforeSize === afterSize && beforeSize > 0) return;
|
|
375
|
+
const maxSize = `${Math.max(beforeSize, afterSize)}px`;
|
|
376
|
+
beforeEl.style[type] = maxSize;
|
|
377
|
+
afterEl.style[type] = maxSize;
|
|
381
378
|
};
|
|
382
379
|
const defaultDirection = "inline";
|
|
383
380
|
const defaultContentDirection = "rows";
|
|
384
381
|
const defaultAlignX = "left";
|
|
385
382
|
const defaultAlignY = "center";
|
|
383
|
+
const AS_RESET = { as: void 0 };
|
|
386
384
|
const Component$7 = ({ innerRef, ref, 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, ...props }) => {
|
|
387
385
|
const shouldBeEmpty = !!props.dangerouslySetInnerHTML || getShouldBeEmpty(tag);
|
|
388
386
|
const isSimpleElement = !beforeContent && !afterContent;
|
|
@@ -415,11 +413,14 @@ const Component$7 = ({ innerRef, ref, tag, label, content, children, beforeConte
|
|
|
415
413
|
]);
|
|
416
414
|
const equalizeRef = useRef(null);
|
|
417
415
|
const externalRef = ref ?? innerRef;
|
|
416
|
+
const latestExternalRef = useRef(externalRef);
|
|
417
|
+
latestExternalRef.current = externalRef;
|
|
418
418
|
const mergedRef = useCallback((node) => {
|
|
419
419
|
equalizeRef.current = node;
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
420
|
+
const r = latestExternalRef.current;
|
|
421
|
+
if (typeof r === "function") r(node);
|
|
422
|
+
else if (r != null) r.current = node;
|
|
423
|
+
}, []);
|
|
423
424
|
useLayoutEffect(() => {
|
|
424
425
|
if (!equalBeforeAfter || !beforeContent || !afterContent) return;
|
|
425
426
|
const node = equalizeRef.current;
|
|
@@ -435,7 +436,8 @@ const Component$7 = ({ innerRef, ref, tag, label, content, children, beforeConte
|
|
|
435
436
|
afterContent,
|
|
436
437
|
direction
|
|
437
438
|
]);
|
|
438
|
-
|
|
439
|
+
if (shouldBeEmpty) return /* @__PURE__ */ jsx(Wrapper_default, {
|
|
440
|
+
...props,
|
|
439
441
|
ref: mergedRef,
|
|
440
442
|
extendCss: css,
|
|
441
443
|
tag,
|
|
@@ -443,16 +445,19 @@ const Component$7 = ({ innerRef, ref, tag, label, content, children, beforeConte
|
|
|
443
445
|
direction: wrapperDirection,
|
|
444
446
|
alignX: wrapperAlignX,
|
|
445
447
|
alignY: wrapperAlignY,
|
|
446
|
-
|
|
447
|
-
};
|
|
448
|
-
if (shouldBeEmpty) return /* @__PURE__ */ jsx(Wrapper_default, {
|
|
449
|
-
...props,
|
|
450
|
-
...WRAPPER_PROPS
|
|
448
|
+
...AS_RESET
|
|
451
449
|
});
|
|
452
450
|
return /* @__PURE__ */ jsxs(Wrapper_default, {
|
|
453
451
|
...props,
|
|
454
|
-
|
|
452
|
+
ref: mergedRef,
|
|
453
|
+
extendCss: css,
|
|
454
|
+
tag,
|
|
455
|
+
block,
|
|
456
|
+
direction: wrapperDirection,
|
|
457
|
+
alignX: wrapperAlignX,
|
|
458
|
+
alignY: wrapperAlignY,
|
|
455
459
|
isInline,
|
|
460
|
+
...AS_RESET,
|
|
456
461
|
children: [
|
|
457
462
|
beforeContent && /* @__PURE__ */ jsx(Content_default, {
|
|
458
463
|
tag: SUB_TAG,
|
|
@@ -493,13 +498,14 @@ const Component$7 = ({ innerRef, ref, tag, label, content, children, beforeConte
|
|
|
493
498
|
});
|
|
494
499
|
};
|
|
495
500
|
const name$5 = `${PKG_NAME}/Element`;
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
501
|
+
const MemoComponent = memo(Component$7);
|
|
502
|
+
MemoComponent.displayName = name$5;
|
|
503
|
+
MemoComponent.pkgName = PKG_NAME;
|
|
504
|
+
MemoComponent.VITUS_LABS__COMPONENT = name$5;
|
|
499
505
|
|
|
500
506
|
//#endregion
|
|
501
507
|
//#region src/Element/index.ts
|
|
502
|
-
var Element_default =
|
|
508
|
+
var Element_default = MemoComponent;
|
|
503
509
|
|
|
504
510
|
//#endregion
|
|
505
511
|
//#region src/helpers/Iterator/component.tsx
|
|
@@ -557,18 +563,41 @@ const flattenChildren = (children) => {
|
|
|
557
563
|
if (isFragment(children)) return children.props.children;
|
|
558
564
|
return [children];
|
|
559
565
|
};
|
|
560
|
-
|
|
561
|
-
const
|
|
562
|
-
|
|
563
|
-
|
|
566
|
+
const classifyItem = (item) => {
|
|
567
|
+
const t = typeof item;
|
|
568
|
+
if (t === "string" || t === "number") return "simple";
|
|
569
|
+
if (t === "object") return "complex";
|
|
570
|
+
return null;
|
|
571
|
+
};
|
|
572
|
+
/**
|
|
573
|
+
* Single-pass: filter out nullish + empty-object entries and detect whether
|
|
574
|
+
* the surviving items form a uniformly simple (string/number) or complex
|
|
575
|
+
* (object) collection. Mixed shapes → kind `null`. Replaces the prior
|
|
576
|
+
* `filterValidItems` + `detectKind` two-pass which allocated an intermediate
|
|
577
|
+
* filtered array; this fuses both into one scan.
|
|
578
|
+
*/
|
|
579
|
+
const filterAndDetectKind = (data) => {
|
|
580
|
+
const items = [];
|
|
564
581
|
let kind = null;
|
|
565
|
-
for (const item of
|
|
566
|
-
|
|
567
|
-
if (
|
|
582
|
+
for (const item of data) {
|
|
583
|
+
if (item == null) continue;
|
|
584
|
+
if (typeof item === "object" && isEmpty(item)) continue;
|
|
585
|
+
items.push(item);
|
|
586
|
+
const t = classifyItem(item);
|
|
587
|
+
if (t === null) return {
|
|
588
|
+
items,
|
|
589
|
+
kind: null
|
|
590
|
+
};
|
|
568
591
|
if (kind === null) kind = t;
|
|
569
|
-
else if (kind !== t) return
|
|
592
|
+
else if (kind !== t) return {
|
|
593
|
+
items,
|
|
594
|
+
kind: null
|
|
595
|
+
};
|
|
570
596
|
}
|
|
571
|
-
return
|
|
597
|
+
return {
|
|
598
|
+
items,
|
|
599
|
+
kind
|
|
600
|
+
};
|
|
572
601
|
};
|
|
573
602
|
const objectKey = (item, index, itemKey) => {
|
|
574
603
|
if (!itemKey) return item.key ?? item.id ?? item.itemId ?? index;
|
|
@@ -607,10 +636,8 @@ const buildObjectSpecs = (items, component, itemKey) => items.map((item, i) => {
|
|
|
607
636
|
};
|
|
608
637
|
});
|
|
609
638
|
const buildDataSpecs = (data, component, valueName, itemKey) => {
|
|
610
|
-
const items =
|
|
611
|
-
if (items.length === 0) return null;
|
|
612
|
-
const kind = detectKind(items);
|
|
613
|
-
if (!kind) return null;
|
|
639
|
+
const { items, kind } = filterAndDetectKind(data);
|
|
640
|
+
if (items.length === 0 || !kind) return null;
|
|
614
641
|
return kind === "simple" ? buildSimpleSpecs(items, component, valueName, itemKey) : buildObjectSpecs(items, component, itemKey);
|
|
615
642
|
};
|
|
616
643
|
const Component$6 = ({ itemKey, valueName, children, component, data, wrapComponent: Wrapper, wrapProps, itemProps }) => {
|
|
@@ -641,12 +668,13 @@ var Iterator_default = Iterator;
|
|
|
641
668
|
* is wrapped in an Element that receives all non-iterator props (e.g.,
|
|
642
669
|
* layout, alignment, css), allowing the list to be styled as a single block.
|
|
643
670
|
*/
|
|
671
|
+
const RESERVED_PROPS_SET = new Set(Iterator_default.RESERVED_PROPS);
|
|
644
672
|
const Component$5 = ({ rootElement = false, ref, ...props }) => {
|
|
645
673
|
const renderedList = /* @__PURE__ */ jsx(Iterator_default, { ...pick(props, Iterator_default.RESERVED_PROPS) });
|
|
646
674
|
if (!rootElement) return renderedList;
|
|
647
675
|
return /* @__PURE__ */ jsx(Element_default, {
|
|
648
676
|
ref,
|
|
649
|
-
...omit(props,
|
|
677
|
+
...omit(props, RESERVED_PROPS_SET),
|
|
650
678
|
children: renderedList
|
|
651
679
|
});
|
|
652
680
|
};
|
|
@@ -725,6 +753,11 @@ const Component = ({ children, blocked, setBlocked, setUnblocked }) => {
|
|
|
725
753
|
* the final viewport-relative position and the alignment that was actually used
|
|
726
754
|
* (which may differ from the requested alignment when an edge would be clipped).
|
|
727
755
|
*/
|
|
756
|
+
const DROPDOWN_TYPES = new Set([
|
|
757
|
+
"dropdown",
|
|
758
|
+
"tooltip",
|
|
759
|
+
"popover"
|
|
760
|
+
]);
|
|
728
761
|
const sel = (cond, a, b) => cond ? a : b;
|
|
729
762
|
const devWarn = (msg) => {
|
|
730
763
|
if (!IS_DEVELOPMENT) return;
|
|
@@ -850,11 +883,7 @@ const adjustForAncestor = (pos, ancestor) => {
|
|
|
850
883
|
return result;
|
|
851
884
|
};
|
|
852
885
|
const computePosition = (type, align, alignX, alignY, offsetX, offsetY, triggerEl, contentEl, ancestorOffset) => {
|
|
853
|
-
const isDropdown =
|
|
854
|
-
"dropdown",
|
|
855
|
-
"tooltip",
|
|
856
|
-
"popover"
|
|
857
|
-
].includes(type);
|
|
886
|
+
const isDropdown = DROPDOWN_TYPES.has(type);
|
|
858
887
|
if (isDropdown && (!triggerEl || !contentEl)) {
|
|
859
888
|
devWarn(`[@vitus-labs/elements] Overlay (${type}): ${triggerEl ? "contentRef" : "triggerRef"} is not attached. Position cannot be calculated without both refs.`);
|
|
860
889
|
return { pos: {} };
|
|
@@ -913,6 +942,80 @@ const useEscapeKey = (enabled, active, blocked, hide) => {
|
|
|
913
942
|
]);
|
|
914
943
|
};
|
|
915
944
|
|
|
945
|
+
//#endregion
|
|
946
|
+
//#region src/Overlay/useFocusTrap.ts
|
|
947
|
+
const FOCUSABLE = [
|
|
948
|
+
"a[href]",
|
|
949
|
+
"button:not([disabled])",
|
|
950
|
+
"input:not([disabled])",
|
|
951
|
+
"select:not([disabled])",
|
|
952
|
+
"textarea:not([disabled])",
|
|
953
|
+
"[contenteditable]:not([contenteditable=\"false\"])",
|
|
954
|
+
"video[controls]",
|
|
955
|
+
"audio[controls]",
|
|
956
|
+
"summary",
|
|
957
|
+
"[tabindex]:not([tabindex=\"-1\"])"
|
|
958
|
+
].join(",");
|
|
959
|
+
const isTabbable = (el) => el.getAttribute("aria-disabled") !== "true";
|
|
960
|
+
const useFocusTrap = (ref, enabled = true, options) => {
|
|
961
|
+
const autoFocus = options?.autoFocus !== false;
|
|
962
|
+
useEffect(() => {
|
|
963
|
+
if (!enabled) return void 0;
|
|
964
|
+
const container = ref.current;
|
|
965
|
+
if (!container) return void 0;
|
|
966
|
+
let focusable = [];
|
|
967
|
+
const refresh = () => {
|
|
968
|
+
const all = container.querySelectorAll(FOCUSABLE);
|
|
969
|
+
const result = [];
|
|
970
|
+
for (let i = 0; i < all.length; i++) {
|
|
971
|
+
const el = all[i];
|
|
972
|
+
if (el && isTabbable(el)) result.push(el);
|
|
973
|
+
}
|
|
974
|
+
focusable = result;
|
|
975
|
+
};
|
|
976
|
+
refresh();
|
|
977
|
+
const observer = new MutationObserver(refresh);
|
|
978
|
+
observer.observe(container, {
|
|
979
|
+
childList: true,
|
|
980
|
+
subtree: true
|
|
981
|
+
});
|
|
982
|
+
const prevFocus = document.activeElement;
|
|
983
|
+
if (autoFocus) if (focusable.length > 0) focusable[0].focus();
|
|
984
|
+
else {
|
|
985
|
+
if (container.tabIndex < 0) container.tabIndex = -1;
|
|
986
|
+
container.focus();
|
|
987
|
+
}
|
|
988
|
+
const handler = (e) => {
|
|
989
|
+
if (e.key !== "Tab" || focusable.length === 0) return;
|
|
990
|
+
const first = focusable[0];
|
|
991
|
+
const last = focusable[focusable.length - 1];
|
|
992
|
+
const active = document.activeElement;
|
|
993
|
+
if (active && !container.contains(active)) {
|
|
994
|
+
e.preventDefault();
|
|
995
|
+
first.focus();
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
if (e.shiftKey && active === first) {
|
|
999
|
+
e.preventDefault();
|
|
1000
|
+
last.focus();
|
|
1001
|
+
} else if (!e.shiftKey && active === last) {
|
|
1002
|
+
e.preventDefault();
|
|
1003
|
+
first.focus();
|
|
1004
|
+
}
|
|
1005
|
+
};
|
|
1006
|
+
document.addEventListener("keydown", handler);
|
|
1007
|
+
return () => {
|
|
1008
|
+
observer.disconnect();
|
|
1009
|
+
document.removeEventListener("keydown", handler);
|
|
1010
|
+
if (autoFocus && prevFocus && typeof prevFocus.focus === "function") prevFocus.focus();
|
|
1011
|
+
};
|
|
1012
|
+
}, [
|
|
1013
|
+
ref,
|
|
1014
|
+
enabled,
|
|
1015
|
+
autoFocus
|
|
1016
|
+
]);
|
|
1017
|
+
};
|
|
1018
|
+
|
|
916
1019
|
//#endregion
|
|
917
1020
|
//#region src/Overlay/useHoverListeners.ts
|
|
918
1021
|
/**
|
|
@@ -981,50 +1084,49 @@ const useHoverListeners = ({ triggerRef, contentRef, isContentLoaded, active, bl
|
|
|
981
1084
|
]);
|
|
982
1085
|
};
|
|
983
1086
|
|
|
1087
|
+
//#endregion
|
|
1088
|
+
//#region src/Overlay/useScrollLock.ts
|
|
1089
|
+
let lockCount = 0;
|
|
1090
|
+
let originalOverflow;
|
|
1091
|
+
const useScrollLock = (enabled) => {
|
|
1092
|
+
useEffect(() => {
|
|
1093
|
+
if (!enabled) return void 0;
|
|
1094
|
+
if (lockCount === 0) originalOverflow = document.body.style.overflow;
|
|
1095
|
+
lockCount++;
|
|
1096
|
+
document.body.style.overflow = "hidden";
|
|
1097
|
+
return () => {
|
|
1098
|
+
lockCount--;
|
|
1099
|
+
if (lockCount === 0) {
|
|
1100
|
+
document.body.style.overflow = originalOverflow ?? "";
|
|
1101
|
+
originalOverflow = void 0;
|
|
1102
|
+
}
|
|
1103
|
+
};
|
|
1104
|
+
}, [enabled]);
|
|
1105
|
+
};
|
|
1106
|
+
|
|
984
1107
|
//#endregion
|
|
985
1108
|
//#region src/Overlay/useScrollReposition.ts
|
|
986
|
-
|
|
987
|
-
/**
|
|
988
|
-
* Window-level scroll/resize listeners that reposition active overlays and
|
|
989
|
-
* re-evaluate close-on-scroll behavior. Also manages the body overflow lock
|
|
990
|
-
* for modal overlays (refcounted across nested modals).
|
|
991
|
-
*/
|
|
992
|
-
const useWindowReposition = (active, type, handleContentPosition, handleVisibility) => {
|
|
1109
|
+
const useWindowReposition = (active, handleContentPosition, handleVisibility) => {
|
|
993
1110
|
useEffect(() => {
|
|
994
1111
|
if (!active) return void 0;
|
|
995
|
-
const shouldSetOverflow = type === "modal";
|
|
996
1112
|
const onScroll = (e) => {
|
|
997
1113
|
handleContentPosition();
|
|
998
1114
|
handleVisibility(e);
|
|
999
1115
|
};
|
|
1000
|
-
if (shouldSetOverflow) {
|
|
1001
|
-
modalOverflowCount++;
|
|
1002
|
-
if (modalOverflowCount === 1) document.body.style.overflow = "hidden";
|
|
1003
|
-
}
|
|
1004
1116
|
window.addEventListener("resize", handleContentPosition);
|
|
1005
1117
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
1006
1118
|
return () => {
|
|
1007
1119
|
handleContentPosition.cancel();
|
|
1008
1120
|
handleVisibility.cancel();
|
|
1009
|
-
if (shouldSetOverflow) {
|
|
1010
|
-
modalOverflowCount--;
|
|
1011
|
-
if (modalOverflowCount === 0) document.body.style.overflow = "";
|
|
1012
|
-
}
|
|
1013
1121
|
window.removeEventListener("resize", handleContentPosition);
|
|
1014
1122
|
window.removeEventListener("scroll", onScroll);
|
|
1015
1123
|
};
|
|
1016
1124
|
}, [
|
|
1017
1125
|
active,
|
|
1018
|
-
type,
|
|
1019
1126
|
handleContentPosition,
|
|
1020
1127
|
handleVisibility
|
|
1021
1128
|
]);
|
|
1022
1129
|
};
|
|
1023
|
-
/**
|
|
1024
|
-
* Same as `useWindowReposition` but for a custom scrollable ancestor.
|
|
1025
|
-
* Locks the parent's overflow while the overlay is active (unless hover-driven,
|
|
1026
|
-
* which expects the parent to keep scrolling).
|
|
1027
|
-
*/
|
|
1028
1130
|
const useParentContainerReposition = (active, parentContainer, closeOn, handleContentPosition, handleVisibility) => {
|
|
1029
1131
|
useEffect(() => {
|
|
1030
1132
|
if (!active || !parentContainer) return void 0;
|
|
@@ -1046,8 +1148,8 @@ const useParentContainerReposition = (active, parentContainer, closeOn, handleCo
|
|
|
1046
1148
|
handleVisibility
|
|
1047
1149
|
]);
|
|
1048
1150
|
};
|
|
1049
|
-
const useScrollReposition = ({ active,
|
|
1050
|
-
useWindowReposition(active,
|
|
1151
|
+
const useScrollReposition = ({ active, parentContainer, closeOn, handleContentPosition, handleVisibility }) => {
|
|
1152
|
+
useWindowReposition(active, handleContentPosition, handleVisibility);
|
|
1051
1153
|
useParentContainerReposition(active, parentContainer, closeOn, handleContentPosition, handleVisibility);
|
|
1052
1154
|
};
|
|
1053
1155
|
|
|
@@ -1063,6 +1165,12 @@ const useScrollReposition = ({ active, type, parentContainer, closeOn, handleCon
|
|
|
1063
1165
|
* live in dedicated hooks: `./useEscapeKey`, `./useHoverListeners`,
|
|
1064
1166
|
* `./useScrollReposition`.
|
|
1065
1167
|
*/
|
|
1168
|
+
const CLICK_CLOSE_KINDS = new Set([
|
|
1169
|
+
"click",
|
|
1170
|
+
"clickOnTrigger",
|
|
1171
|
+
"clickOutsideContent"
|
|
1172
|
+
]);
|
|
1173
|
+
const isNodeOrChildOf = (node, target) => !!(node && target && (node.contains(target) || target === node));
|
|
1066
1174
|
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, hoverDelay = 100, disabled, onOpen, onClose } = {}) => {
|
|
1067
1175
|
const { rootSize } = useContext(context);
|
|
1068
1176
|
const ctx = useOverlayContext();
|
|
@@ -1074,7 +1182,6 @@ const useOverlay = ({ isOpen = false, openOn = "click", closeOn = "click", type
|
|
|
1074
1182
|
const [active, setActive] = useState(isOpen);
|
|
1075
1183
|
const triggerRef = useRef(null);
|
|
1076
1184
|
const contentRef = useRef(null);
|
|
1077
|
-
const prevFocusRef = useRef(null);
|
|
1078
1185
|
const setBlocked = useCallback(() => setBlockedCount((c) => c + 1), []);
|
|
1079
1186
|
const setUnblocked = useCallback(() => setBlockedCount((c) => Math.max(0, c - 1)), []);
|
|
1080
1187
|
const showContent = useCallback(() => setActive(true), []);
|
|
@@ -1125,13 +1232,11 @@ const useOverlay = ({ isOpen = false, openOn = "click", closeOn = "click", type
|
|
|
1125
1232
|
const setContentPosition = useCallback(() => {
|
|
1126
1233
|
assignContentPosition(calculateContentPosition());
|
|
1127
1234
|
}, [assignContentPosition, calculateContentPosition]);
|
|
1128
|
-
const
|
|
1129
|
-
|
|
1130
|
-
return false;
|
|
1131
|
-
}, []);
|
|
1235
|
+
const isTriggerOrChild = useCallback((e) => isNodeOrChildOf(triggerRef.current, e?.target), []);
|
|
1236
|
+
const isContentOrChild = useCallback((e) => isNodeOrChildOf(contentRef.current, e?.target), []);
|
|
1132
1237
|
const handleVisibilityByEventType = useCallback((e) => {
|
|
1133
1238
|
if (blocked || disabled) return;
|
|
1134
|
-
processVisibilityEvent(e, active, openOn, closeOn,
|
|
1239
|
+
processVisibilityEvent(e, active, openOn, closeOn, isTriggerOrChild, isContentOrChild, showContent, hideContent);
|
|
1135
1240
|
}, [
|
|
1136
1241
|
active,
|
|
1137
1242
|
blocked,
|
|
@@ -1140,7 +1245,8 @@ const useOverlay = ({ isOpen = false, openOn = "click", closeOn = "click", type
|
|
|
1140
1245
|
closeOn,
|
|
1141
1246
|
hideContent,
|
|
1142
1247
|
showContent,
|
|
1143
|
-
|
|
1248
|
+
isTriggerOrChild,
|
|
1249
|
+
isContentOrChild
|
|
1144
1250
|
]);
|
|
1145
1251
|
const latestSetContentPosition = useRef(setContentPosition);
|
|
1146
1252
|
latestSetContentPosition.current = setContentPosition;
|
|
@@ -1169,50 +1275,35 @@ const useOverlay = ({ isOpen = false, openOn = "click", closeOn = "click", type
|
|
|
1169
1275
|
isContentLoaded,
|
|
1170
1276
|
setContentPosition
|
|
1171
1277
|
]);
|
|
1278
|
+
const latestOnOpen = useRef(onOpen);
|
|
1279
|
+
latestOnOpen.current = onOpen;
|
|
1280
|
+
const latestOnClose = useRef(onClose);
|
|
1281
|
+
latestOnClose.current = onClose;
|
|
1172
1282
|
const prevActiveRef = useRef(false);
|
|
1173
1283
|
useEffect(() => {
|
|
1174
1284
|
const wasActive = prevActiveRef.current;
|
|
1175
1285
|
prevActiveRef.current = active;
|
|
1176
1286
|
if (active && !wasActive) {
|
|
1177
|
-
|
|
1287
|
+
latestOnOpen.current?.();
|
|
1178
1288
|
ctx.setBlocked?.();
|
|
1179
1289
|
} else if (!active && wasActive) {
|
|
1180
1290
|
setContentLoaded(false);
|
|
1181
|
-
|
|
1291
|
+
latestOnClose.current?.();
|
|
1182
1292
|
ctx.setUnblocked?.();
|
|
1183
1293
|
} else if (!active) setContentLoaded(false);
|
|
1184
1294
|
return () => {
|
|
1185
1295
|
if (active) {
|
|
1186
|
-
|
|
1296
|
+
latestOnClose.current?.();
|
|
1187
1297
|
ctx.setUnblocked?.();
|
|
1188
1298
|
}
|
|
1189
1299
|
};
|
|
1190
|
-
}, [
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
onOpen
|
|
1195
|
-
]);
|
|
1196
|
-
useEffect(() => {
|
|
1197
|
-
if (type !== "modal") return;
|
|
1198
|
-
if (active && isContentLoaded && contentRef.current) {
|
|
1199
|
-
prevFocusRef.current = document.activeElement;
|
|
1200
|
-
if (contentRef.current.tabIndex < 0) contentRef.current.tabIndex = -1;
|
|
1201
|
-
contentRef.current.focus();
|
|
1202
|
-
}
|
|
1203
|
-
if (!active && prevFocusRef.current) {
|
|
1204
|
-
prevFocusRef.current.focus();
|
|
1205
|
-
prevFocusRef.current = null;
|
|
1206
|
-
}
|
|
1207
|
-
}, [
|
|
1208
|
-
active,
|
|
1209
|
-
isContentLoaded,
|
|
1210
|
-
type
|
|
1211
|
-
]);
|
|
1300
|
+
}, [active, ctx]);
|
|
1301
|
+
const modalActive = type === "modal" && active && isContentLoaded;
|
|
1302
|
+
useFocusTrap(contentRef, modalActive);
|
|
1303
|
+
useScrollLock(modalActive);
|
|
1212
1304
|
useEscapeKey(closeOnEsc, active, blocked, hideContent);
|
|
1213
1305
|
useScrollReposition({
|
|
1214
1306
|
active,
|
|
1215
|
-
type,
|
|
1216
1307
|
parentContainer,
|
|
1217
1308
|
closeOn,
|
|
1218
1309
|
handleContentPosition,
|
|
@@ -1220,12 +1311,8 @@ const useOverlay = ({ isOpen = false, openOn = "click", closeOn = "click", type
|
|
|
1220
1311
|
});
|
|
1221
1312
|
useEffect(() => {
|
|
1222
1313
|
if (blocked || disabled) return void 0;
|
|
1223
|
-
if (openOn === "click" ||
|
|
1224
|
-
|
|
1225
|
-
"clickOnTrigger",
|
|
1226
|
-
"clickOutsideContent"
|
|
1227
|
-
].includes(closeOn)) window.addEventListener("click", handleClick);
|
|
1228
|
-
return () => window.removeEventListener("click", handleClick);
|
|
1314
|
+
if (openOn === "click" || CLICK_CLOSE_KINDS.has(closeOn)) document.addEventListener("click", handleClick);
|
|
1315
|
+
return () => document.removeEventListener("click", handleClick);
|
|
1229
1316
|
}, [
|
|
1230
1317
|
openOn,
|
|
1231
1318
|
closeOn,
|
|
@@ -1279,44 +1366,42 @@ const Component$3 = ({ children, trigger, DOMLocation, triggerRefName = "ref", c
|
|
|
1279
1366
|
const { active, triggerRef, contentRef, showContent, hideContent, align, alignX, alignY, Provider, ...ctx } = useOverlay(props);
|
|
1280
1367
|
const { openOn, closeOn, type } = props;
|
|
1281
1368
|
const contentId = useId();
|
|
1282
|
-
const passHandlers =
|
|
1283
|
-
const ariaHasPopup =
|
|
1284
|
-
|
|
1285
|
-
case "modal": return "dialog";
|
|
1286
|
-
case "tooltip": return "true";
|
|
1287
|
-
default: return "menu";
|
|
1288
|
-
}
|
|
1289
|
-
}, [type]);
|
|
1290
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [render(trigger, {
|
|
1369
|
+
const passHandlers = openOn === "manual" || closeOn === "manual" || closeOn === "clickOutsideContent";
|
|
1370
|
+
const ariaHasPopup = type === "modal" ? "dialog" : type === "tooltip" ? "true" : "menu";
|
|
1371
|
+
const triggerProps = {
|
|
1291
1372
|
[triggerRefName]: triggerRef,
|
|
1292
1373
|
active,
|
|
1293
1374
|
"aria-expanded": active,
|
|
1294
1375
|
"aria-haspopup": ariaHasPopup,
|
|
1295
|
-
"aria-controls": active ? contentId : void 0
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1376
|
+
"aria-controls": active ? contentId : void 0
|
|
1377
|
+
};
|
|
1378
|
+
if (passHandlers) {
|
|
1379
|
+
triggerProps.showContent = showContent;
|
|
1380
|
+
triggerProps.hideContent = hideContent;
|
|
1381
|
+
}
|
|
1382
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [render(trigger, triggerProps), IS_BROWSER && active && (() => {
|
|
1383
|
+
const contentProps = {
|
|
1384
|
+
[contentRefName]: contentRef,
|
|
1385
|
+
id: contentId,
|
|
1386
|
+
role: type === "modal" ? "dialog" : void 0,
|
|
1387
|
+
"aria-modal": type === "modal" ? true : void 0,
|
|
1388
|
+
active,
|
|
1389
|
+
align,
|
|
1390
|
+
alignX,
|
|
1391
|
+
alignY
|
|
1392
|
+
};
|
|
1393
|
+
if (passHandlers) {
|
|
1394
|
+
contentProps.showContent = showContent;
|
|
1395
|
+
contentProps.hideContent = hideContent;
|
|
1396
|
+
}
|
|
1397
|
+
return /* @__PURE__ */ jsx(Portal_default, {
|
|
1398
|
+
DOMLocation,
|
|
1399
|
+
children: /* @__PURE__ */ jsx(Provider, {
|
|
1400
|
+
...ctx,
|
|
1401
|
+
children: render(children, contentProps)
|
|
1317
1402
|
})
|
|
1318
|
-
})
|
|
1319
|
-
})] });
|
|
1403
|
+
});
|
|
1404
|
+
})()] });
|
|
1320
1405
|
};
|
|
1321
1406
|
const name$2 = `${PKG_NAME}/Overlay`;
|
|
1322
1407
|
Component$3.displayName = name$2;
|
|
@@ -1357,17 +1442,13 @@ var styled_default = styled(textComponent)`
|
|
|
1357
1442
|
//#endregion
|
|
1358
1443
|
//#region src/Text/component.tsx
|
|
1359
1444
|
const Component$2 = ({ paragraph, label, children, tag, css, ref, ...props }) => {
|
|
1360
|
-
|
|
1445
|
+
return /* @__PURE__ */ jsx(styled_default, {
|
|
1361
1446
|
ref,
|
|
1362
|
-
as,
|
|
1447
|
+
as: paragraph ? "p" : tag,
|
|
1363
1448
|
$text: { extraStyles: css },
|
|
1364
1449
|
...props,
|
|
1365
1450
|
children: children ?? label
|
|
1366
1451
|
});
|
|
1367
|
-
let finalTag;
|
|
1368
|
-
if (paragraph) finalTag = "p";
|
|
1369
|
-
else finalTag = tag;
|
|
1370
|
-
return renderContent(finalTag);
|
|
1371
1452
|
};
|
|
1372
1453
|
const name$1 = `${PKG_NAME}/Text`;
|
|
1373
1454
|
Component$2.displayName = name$1;
|
|
@@ -1387,7 +1468,7 @@ var Text_default = Component$2;
|
|
|
1387
1468
|
* helper to clone children with the merged props.
|
|
1388
1469
|
*/
|
|
1389
1470
|
const Component$1 = ({ children, className, style }) => {
|
|
1390
|
-
const mergedClasses =
|
|
1471
|
+
const mergedClasses = Array.isArray(className) ? className.join(" ") : className;
|
|
1391
1472
|
const finalProps = {};
|
|
1392
1473
|
if (style) finalProps.style = style;
|
|
1393
1474
|
if (mergedClasses) finalProps.className = mergedClasses;
|
|
@@ -21,10 +21,7 @@ const IS_DEVELOPMENT = process.env.NODE_ENV !== "production";
|
|
|
21
21
|
* fill remaining space between before and after.
|
|
22
22
|
*/
|
|
23
23
|
const { styled: styled$2, css: css$2, component: component$1 } = config;
|
|
24
|
-
const
|
|
25
|
-
flex: 1;
|
|
26
|
-
`;
|
|
27
|
-
const typeContentCSS = `
|
|
24
|
+
const FLEX_1 = `
|
|
28
25
|
flex: 1;
|
|
29
26
|
`;
|
|
30
27
|
const gapDimensions = {
|
|
@@ -56,7 +53,7 @@ const styles$2 = ({ css, theme: t, rootSize }) => css`
|
|
|
56
53
|
alignY: t.alignY
|
|
57
54
|
})};
|
|
58
55
|
|
|
59
|
-
${t.equalCols &&
|
|
56
|
+
${t.equalCols && FLEX_1};
|
|
60
57
|
|
|
61
58
|
${t.gap && t.contentType && calculateGap({
|
|
62
59
|
direction: t.parentDirection,
|
|
@@ -73,7 +70,7 @@ const StyledComponent = styled$2(component$1)`
|
|
|
73
70
|
align-self: stretch;
|
|
74
71
|
flex-wrap: wrap;
|
|
75
72
|
|
|
76
|
-
${({ $contentType }) => $contentType === "content" &&
|
|
73
|
+
${({ $contentType }) => $contentType === "content" && FLEX_1};
|
|
77
74
|
|
|
78
75
|
${makeItResponsive({
|
|
79
76
|
key: "$element",
|
|
@@ -124,11 +121,11 @@ const Component$6 = ({ contentType, tag, parentDirection, direction, alignX, ali
|
|
|
124
121
|
children: render(children)
|
|
125
122
|
});
|
|
126
123
|
};
|
|
127
|
-
var component_default = memo(Component$6);
|
|
124
|
+
var component_default$1 = memo(Component$6);
|
|
128
125
|
|
|
129
126
|
//#endregion
|
|
130
127
|
//#region src/helpers/Content/index.ts
|
|
131
|
-
var Content_default = component_default;
|
|
128
|
+
var Content_default = component_default$1;
|
|
132
129
|
|
|
133
130
|
//#endregion
|
|
134
131
|
//#region src/helpers/Wrapper/styled.ts
|
|
@@ -190,22 +187,19 @@ var styled_default$1 = styled$1(component)`
|
|
|
190
187
|
* (parent + child Styled) because these HTML elements do not natively
|
|
191
188
|
* support `display: flex` consistently across browsers.
|
|
192
189
|
*/
|
|
193
|
-
const DEV_PROPS = IS_DEVELOPMENT ? { "data-vl-element": "Element" } :
|
|
190
|
+
const DEV_PROPS = IS_DEVELOPMENT ? { "data-vl-element": "Element" } : null;
|
|
194
191
|
const Component$5 = ({ children, ref, tag, block, extendCss, direction, alignX, alignY, equalCols, isInline, ...props }) => {
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
equalCols,
|
|
207
|
-
extraStyles: extendCss
|
|
208
|
-
}), [
|
|
192
|
+
const $element = useMemo(() => {
|
|
193
|
+
return {
|
|
194
|
+
block,
|
|
195
|
+
direction,
|
|
196
|
+
alignX,
|
|
197
|
+
alignY,
|
|
198
|
+
equalCols,
|
|
199
|
+
extraStyles: extendCss
|
|
200
|
+
};
|
|
201
|
+
}, [
|
|
202
|
+
false,
|
|
209
203
|
block,
|
|
210
204
|
direction,
|
|
211
205
|
alignX,
|
|
@@ -213,33 +207,20 @@ const Component$5 = ({ children, ref, tag, block, extendCss, direction, alignX,
|
|
|
213
207
|
equalCols,
|
|
214
208
|
extendCss
|
|
215
209
|
]);
|
|
216
|
-
useMemo(() => ({
|
|
217
|
-
parentFix: true,
|
|
218
|
-
block,
|
|
219
|
-
extraStyles: extendCss
|
|
220
|
-
}), [block, extendCss]);
|
|
221
|
-
useMemo(() => ({
|
|
222
|
-
childFix: true,
|
|
223
|
-
direction,
|
|
224
|
-
alignX,
|
|
225
|
-
alignY,
|
|
226
|
-
equalCols
|
|
227
|
-
}), [
|
|
228
|
-
direction,
|
|
229
|
-
alignX,
|
|
230
|
-
alignY,
|
|
231
|
-
equalCols
|
|
232
|
-
]);
|
|
233
210
|
return /* @__PURE__ */ jsx(styled_default$1, {
|
|
234
|
-
...
|
|
235
|
-
|
|
211
|
+
...props,
|
|
212
|
+
...DEV_PROPS,
|
|
213
|
+
ref,
|
|
214
|
+
as: tag,
|
|
215
|
+
$element,
|
|
236
216
|
children
|
|
237
217
|
});
|
|
238
218
|
};
|
|
219
|
+
var component_default = memo(Component$5);
|
|
239
220
|
|
|
240
221
|
//#endregion
|
|
241
222
|
//#region src/helpers/Wrapper/index.ts
|
|
242
|
-
var Wrapper_default =
|
|
223
|
+
var Wrapper_default = component_default;
|
|
243
224
|
|
|
244
225
|
//#endregion
|
|
245
226
|
//#region src/Element/component.tsx
|
|
@@ -255,6 +236,7 @@ const defaultDirection = "inline";
|
|
|
255
236
|
const defaultContentDirection = "rows";
|
|
256
237
|
const defaultAlignX = "left";
|
|
257
238
|
const defaultAlignY = "center";
|
|
239
|
+
const AS_RESET = { as: void 0 };
|
|
258
240
|
const Component$4 = ({ innerRef, ref, 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, ...props }) => {
|
|
259
241
|
const isSimpleElement = !beforeContent && !afterContent;
|
|
260
242
|
const CHILDREN = children ?? content ?? label;
|
|
@@ -286,18 +268,22 @@ const Component$4 = ({ innerRef, ref, tag, label, content, children, beforeConte
|
|
|
286
268
|
]);
|
|
287
269
|
const equalizeRef = useRef(null);
|
|
288
270
|
const externalRef = ref ?? innerRef;
|
|
271
|
+
const latestExternalRef = useRef(externalRef);
|
|
272
|
+
latestExternalRef.current = externalRef;
|
|
289
273
|
const mergedRef = useCallback((node) => {
|
|
290
274
|
equalizeRef.current = node;
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
275
|
+
const r = latestExternalRef.current;
|
|
276
|
+
if (typeof r === "function") r(node);
|
|
277
|
+
else if (r != null) r.current = node;
|
|
278
|
+
}, []);
|
|
294
279
|
useLayoutEffect(() => {}, [
|
|
295
280
|
equalBeforeAfter,
|
|
296
281
|
beforeContent,
|
|
297
282
|
afterContent,
|
|
298
283
|
direction
|
|
299
284
|
]);
|
|
300
|
-
|
|
285
|
+
return /* @__PURE__ */ jsxs(Wrapper_default, {
|
|
286
|
+
...props,
|
|
301
287
|
ref: mergedRef,
|
|
302
288
|
extendCss: css,
|
|
303
289
|
tag,
|
|
@@ -305,12 +291,8 @@ const Component$4 = ({ innerRef, ref, tag, label, content, children, beforeConte
|
|
|
305
291
|
direction: wrapperDirection,
|
|
306
292
|
alignX: wrapperAlignX,
|
|
307
293
|
alignY: wrapperAlignY,
|
|
308
|
-
as: void 0
|
|
309
|
-
};
|
|
310
|
-
return /* @__PURE__ */ jsxs(Wrapper_default, {
|
|
311
|
-
...props,
|
|
312
|
-
...WRAPPER_PROPS,
|
|
313
294
|
isInline,
|
|
295
|
+
...AS_RESET,
|
|
314
296
|
children: [
|
|
315
297
|
beforeContent && /* @__PURE__ */ jsx(Content_default, {
|
|
316
298
|
tag: SUB_TAG,
|
|
@@ -351,13 +333,14 @@ const Component$4 = ({ innerRef, ref, tag, label, content, children, beforeConte
|
|
|
351
333
|
});
|
|
352
334
|
};
|
|
353
335
|
const name$3 = `${PKG_NAME}/Element`;
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
336
|
+
const MemoComponent = memo(Component$4);
|
|
337
|
+
MemoComponent.displayName = name$3;
|
|
338
|
+
MemoComponent.pkgName = PKG_NAME;
|
|
339
|
+
MemoComponent.VITUS_LABS__COMPONENT = name$3;
|
|
357
340
|
|
|
358
341
|
//#endregion
|
|
359
342
|
//#region src/Element/index.ts
|
|
360
|
-
var Element_default =
|
|
343
|
+
var Element_default = MemoComponent;
|
|
361
344
|
|
|
362
345
|
//#endregion
|
|
363
346
|
//#region src/helpers/Iterator/component.tsx
|
|
@@ -415,18 +398,41 @@ const flattenChildren = (children) => {
|
|
|
415
398
|
if (isFragment(children)) return children.props.children;
|
|
416
399
|
return [children];
|
|
417
400
|
};
|
|
418
|
-
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
401
|
+
const classifyItem = (item) => {
|
|
402
|
+
const t = typeof item;
|
|
403
|
+
if (t === "string" || t === "number") return "simple";
|
|
404
|
+
if (t === "object") return "complex";
|
|
405
|
+
return null;
|
|
406
|
+
};
|
|
407
|
+
/**
|
|
408
|
+
* Single-pass: filter out nullish + empty-object entries and detect whether
|
|
409
|
+
* the surviving items form a uniformly simple (string/number) or complex
|
|
410
|
+
* (object) collection. Mixed shapes → kind `null`. Replaces the prior
|
|
411
|
+
* `filterValidItems` + `detectKind` two-pass which allocated an intermediate
|
|
412
|
+
* filtered array; this fuses both into one scan.
|
|
413
|
+
*/
|
|
414
|
+
const filterAndDetectKind = (data) => {
|
|
415
|
+
const items = [];
|
|
422
416
|
let kind = null;
|
|
423
|
-
for (const item of
|
|
424
|
-
|
|
425
|
-
if (
|
|
417
|
+
for (const item of data) {
|
|
418
|
+
if (item == null) continue;
|
|
419
|
+
if (typeof item === "object" && isEmpty(item)) continue;
|
|
420
|
+
items.push(item);
|
|
421
|
+
const t = classifyItem(item);
|
|
422
|
+
if (t === null) return {
|
|
423
|
+
items,
|
|
424
|
+
kind: null
|
|
425
|
+
};
|
|
426
426
|
if (kind === null) kind = t;
|
|
427
|
-
else if (kind !== t) return
|
|
427
|
+
else if (kind !== t) return {
|
|
428
|
+
items,
|
|
429
|
+
kind: null
|
|
430
|
+
};
|
|
428
431
|
}
|
|
429
|
-
return
|
|
432
|
+
return {
|
|
433
|
+
items,
|
|
434
|
+
kind
|
|
435
|
+
};
|
|
430
436
|
};
|
|
431
437
|
const objectKey = (item, index, itemKey) => {
|
|
432
438
|
if (!itemKey) return item.key ?? item.id ?? item.itemId ?? index;
|
|
@@ -465,10 +471,8 @@ const buildObjectSpecs = (items, component, itemKey) => items.map((item, i) => {
|
|
|
465
471
|
};
|
|
466
472
|
});
|
|
467
473
|
const buildDataSpecs = (data, component, valueName, itemKey) => {
|
|
468
|
-
const items =
|
|
469
|
-
if (items.length === 0) return null;
|
|
470
|
-
const kind = detectKind(items);
|
|
471
|
-
if (!kind) return null;
|
|
474
|
+
const { items, kind } = filterAndDetectKind(data);
|
|
475
|
+
if (items.length === 0 || !kind) return null;
|
|
472
476
|
return kind === "simple" ? buildSimpleSpecs(items, component, valueName, itemKey) : buildObjectSpecs(items, component, itemKey);
|
|
473
477
|
};
|
|
474
478
|
const Component$3 = ({ itemKey, valueName, children, component, data, wrapComponent: Wrapper, wrapProps, itemProps }) => {
|
|
@@ -499,12 +503,13 @@ var Iterator_default = Iterator;
|
|
|
499
503
|
* is wrapped in an Element that receives all non-iterator props (e.g.,
|
|
500
504
|
* layout, alignment, css), allowing the list to be styled as a single block.
|
|
501
505
|
*/
|
|
506
|
+
const RESERVED_PROPS_SET = new Set(Iterator_default.RESERVED_PROPS);
|
|
502
507
|
const Component$2 = ({ rootElement = false, ref, ...props }) => {
|
|
503
508
|
const renderedList = /* @__PURE__ */ jsx(Iterator_default, { ...pick(props, Iterator_default.RESERVED_PROPS) });
|
|
504
509
|
if (!rootElement) return renderedList;
|
|
505
510
|
return /* @__PURE__ */ jsx(Element_default, {
|
|
506
511
|
ref,
|
|
507
|
-
...omit(props,
|
|
512
|
+
...omit(props, RESERVED_PROPS_SET),
|
|
508
513
|
children: renderedList
|
|
509
514
|
});
|
|
510
515
|
};
|
|
@@ -543,15 +548,13 @@ var styled_default = styled(textComponent)`
|
|
|
543
548
|
//#endregion
|
|
544
549
|
//#region src/Text/component.tsx
|
|
545
550
|
const Component$1 = ({ paragraph, label, children, tag, css, ref, ...props }) => {
|
|
546
|
-
|
|
551
|
+
return /* @__PURE__ */ jsx(styled_default, {
|
|
547
552
|
ref,
|
|
548
|
-
as,
|
|
553
|
+
as: void 0,
|
|
549
554
|
$text: { extraStyles: css },
|
|
550
555
|
...props,
|
|
551
556
|
children: children ?? label
|
|
552
557
|
});
|
|
553
|
-
let finalTag;
|
|
554
|
-
return renderContent(finalTag);
|
|
555
558
|
};
|
|
556
559
|
const name$1 = `${PKG_NAME}/Text`;
|
|
557
560
|
Component$1.displayName = name$1;
|
|
@@ -571,7 +574,7 @@ var Text_default = Component$1;
|
|
|
571
574
|
* helper to clone children with the merged props.
|
|
572
575
|
*/
|
|
573
576
|
const Component = ({ children, className, style }) => {
|
|
574
|
-
const mergedClasses =
|
|
577
|
+
const mergedClasses = Array.isArray(className) ? className.join(" ") : className;
|
|
575
578
|
const finalProps = {};
|
|
576
579
|
if (style) finalProps.style = style;
|
|
577
580
|
if (mergedClasses) finalProps.className = mergedClasses;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitus-labs/elements",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Vit Bokisch <vit@bokisch.cz>",
|
|
6
6
|
"maintainers": [
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"prepublish": "bun run build",
|
|
50
50
|
"build": "bun run vl_rolldown_build",
|
|
51
51
|
"build:watch": "bun run vl_rolldown_build-watch",
|
|
52
|
+
"bench": "bun benchmarks/render-bench.tsx",
|
|
52
53
|
"lint": "biome check src/",
|
|
53
54
|
"test": "vitest run",
|
|
54
55
|
"test:coverage": "vitest run --coverage",
|
|
@@ -57,27 +58,27 @@
|
|
|
57
58
|
"typecheck": "tsc --noEmit"
|
|
58
59
|
},
|
|
59
60
|
"peerDependencies": {
|
|
60
|
-
"@vitus-labs/core": "^2.
|
|
61
|
-
"@vitus-labs/unistyle": "^2.
|
|
61
|
+
"@vitus-labs/core": "^2.7.0",
|
|
62
|
+
"@vitus-labs/unistyle": "^2.7.0",
|
|
62
63
|
"react": ">= 19",
|
|
63
|
-
"react-dom": ">= 19"
|
|
64
|
-
"react-native": ">= 0.76"
|
|
64
|
+
"react-dom": ">= 19"
|
|
65
65
|
},
|
|
66
66
|
"peerDependenciesMeta": {
|
|
67
67
|
"react-dom": {
|
|
68
68
|
"optional": true
|
|
69
|
-
},
|
|
70
|
-
"react-native": {
|
|
71
|
-
"optional": true
|
|
72
69
|
}
|
|
73
70
|
},
|
|
74
71
|
"devDependencies": {
|
|
72
|
+
"@vitus-labs/connector-styler": "workspace:*",
|
|
75
73
|
"@vitus-labs/core": "workspace:*",
|
|
76
74
|
"@vitus-labs/rocketstyle": "workspace:*",
|
|
77
|
-
"@vitus-labs/
|
|
78
|
-
"@vitus-labs/tools-
|
|
79
|
-
"@vitus-labs/tools-
|
|
80
|
-
"@vitus-labs/
|
|
75
|
+
"@vitus-labs/styler": "workspace:*",
|
|
76
|
+
"@vitus-labs/tools-rolldown": "2.5.0",
|
|
77
|
+
"@vitus-labs/tools-storybook": "2.5.0",
|
|
78
|
+
"@vitus-labs/tools-typescript": "2.5.0",
|
|
79
|
+
"@vitus-labs/unistyle": "workspace:*",
|
|
80
|
+
"jsdom": "^29.1.1",
|
|
81
|
+
"tinybench": "^6.0.1"
|
|
81
82
|
},
|
|
82
83
|
"dependencies": {
|
|
83
84
|
"react-is": "^19.2.4"
|