@vitus-labs/elements 2.0.0-alpha.27 → 2.0.0-alpha.28

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.
@@ -1,9 +1,8 @@
1
1
  import { Provider, alignContent, extendCss, makeItResponsive, value } from "@vitus-labs/unistyle";
2
- import { config, context, isEmpty, omit, pick, render, throttle } from "@vitus-labs/core";
3
- import { Children, createContext, forwardRef, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
4
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { config, isEmpty, omit, pick, render } from "@vitus-labs/core";
3
+ import { Children, memo, useCallback, useLayoutEffect, useMemo, useRef } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { isFragment } from "react-is";
6
- import { createPortal } from "react-dom";
7
6
 
8
7
  //#region src/constants.ts
9
8
  const PKG_NAME = "@vitus-labs/elements";
@@ -96,7 +95,7 @@ const StyledComponent = styled$2(component$1)`
96
95
  * via core `render()` — this lets React.memo skip re-renders when the
97
96
  * content reference is stable (common for component-type or string content).
98
97
  */
99
- const Component$9 = ({ contentType, tag, parentDirection, direction, alignX, alignY, equalCols, gap, extendCss, children, ...props }) => {
98
+ const Component$6 = ({ contentType, tag, parentDirection, direction, alignX, alignY, equalCols, gap, extendCss, children, ...props }) => {
100
99
  const debugProps = IS_DEVELOPMENT ? { "data-vl-element": contentType } : {};
101
100
  return /* @__PURE__ */ jsx(StyledComponent, {
102
101
  as: tag,
@@ -125,7 +124,7 @@ const Component$9 = ({ contentType, tag, parentDirection, direction, alignX, ali
125
124
  children: render(children)
126
125
  });
127
126
  };
128
- var component_default$1 = memo(Component$9);
127
+ var component_default$1 = memo(Component$6);
129
128
 
130
129
  //#endregion
131
130
  //#region src/helpers/Content/index.ts
@@ -149,6 +148,7 @@ const childFixCSS = `
149
148
  `;
150
149
  const blockCSS = `
151
150
  align-self: stretch;
151
+ width: 100%;
152
152
  `;
153
153
  const styles$1 = ({ theme: t, css }) => css`
154
154
  ${false};
@@ -160,8 +160,8 @@ const styles$1 = ({ theme: t, css }) => css`
160
160
  })};
161
161
 
162
162
  ${t.block && blockCSS};
163
-
164
163
  ${false};
164
+
165
165
  ${false};
166
166
  ${false};
167
167
 
@@ -186,13 +186,12 @@ var styled_default$1 = styled$1(component)`
186
186
  //#region src/helpers/Wrapper/component.tsx
187
187
  /**
188
188
  * Wrapper component that serves as the outermost styled container for Element.
189
- * Uses forwardRef for ref forwarding to the underlying DOM node. On web, it
190
- * detects button/fieldset/legend tags and applies a two-layer flex fix
189
+ * On web, it detects button/fieldset/legend tags and applies a two-layer flex fix
191
190
  * (parent + child Styled) because these HTML elements do not natively
192
191
  * support `display: flex` consistently across browsers.
193
192
  */
194
193
  const DEV_PROPS = IS_DEVELOPMENT ? { "data-vl-element": "Element" } : {};
195
- const Component$8 = forwardRef(({ children, tag, block, extendCss, direction, alignX, alignY, equalCols, isInline, ...props }, ref) => {
194
+ const Component$5 = ({ children, ref, tag, block, extendCss, direction, alignX, alignY, equalCols, isInline, ...props }) => {
196
195
  const COMMON_PROPS = {
197
196
  ...props,
198
197
  ...DEV_PROPS,
@@ -236,11 +235,11 @@ const Component$8 = forwardRef(({ children, tag, block, extendCss, direction, al
236
235
  $element: normalElement,
237
236
  children
238
237
  });
239
- });
238
+ };
240
239
 
241
240
  //#endregion
242
241
  //#region src/helpers/Wrapper/index.ts
243
- var Wrapper_default = Component$8;
242
+ var Wrapper_default = Component$5;
244
243
 
245
244
  //#endregion
246
245
  //#region src/Element/component.tsx
@@ -252,26 +251,11 @@ var Wrapper_default = Component$8;
252
251
  * like void elements (input, img) and inline elements (span, a) by
253
252
  * skipping children or switching sub-tags accordingly.
254
253
  */
255
- const equalize = (el, direction) => {
256
- const beforeEl = el.firstElementChild;
257
- const afterEl = el.lastElementChild;
258
- if (beforeEl && afterEl && beforeEl !== afterEl) {
259
- const type = direction === "rows" ? "height" : "width";
260
- const prop = type === "height" ? "offsetHeight" : "offsetWidth";
261
- const beforeSize = beforeEl[prop];
262
- const afterSize = afterEl[prop];
263
- if (Number.isInteger(beforeSize) && Number.isInteger(afterSize)) {
264
- const maxSize = `${Math.max(beforeSize, afterSize)}px`;
265
- beforeEl.style[type] = maxSize;
266
- afterEl.style[type] = maxSize;
267
- }
268
- }
269
- };
270
254
  const defaultDirection = "inline";
271
255
  const defaultContentDirection = "rows";
272
256
  const defaultAlignX = "left";
273
257
  const defaultAlignY = "center";
274
- const Component$7 = forwardRef(({ 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, ...props }, ref) => {
258
+ 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 }) => {
275
259
  const isSimpleElement = !beforeContent && !afterContent;
276
260
  const CHILDREN = children ?? content ?? label;
277
261
  const isInline = false;
@@ -307,10 +291,7 @@ const Component$7 = forwardRef(({ innerRef, tag, label, content, children, befor
307
291
  if (typeof externalRef === "function") externalRef(node);
308
292
  else if (externalRef != null) externalRef.current = node;
309
293
  }, [externalRef]);
310
- useLayoutEffect(() => {
311
- if (!equalBeforeAfter || !beforeContent || !afterContent) return;
312
- if (equalizeRef.current) equalize(equalizeRef.current, direction);
313
- }, [
294
+ useLayoutEffect(() => {}, [
314
295
  equalBeforeAfter,
315
296
  beforeContent,
316
297
  afterContent,
@@ -368,15 +349,15 @@ const Component$7 = forwardRef(({ innerRef, tag, label, content, children, befor
368
349
  })
369
350
  ]
370
351
  });
371
- });
372
- const name$5 = `${PKG_NAME}/Element`;
373
- Component$7.displayName = name$5;
374
- Component$7.pkgName = PKG_NAME;
375
- Component$7.VITUS_LABS__COMPONENT = name$5;
352
+ };
353
+ const name$3 = `${PKG_NAME}/Element`;
354
+ Component$4.displayName = name$3;
355
+ Component$4.pkgName = PKG_NAME;
356
+ Component$4.VITUS_LABS__COMPONENT = name$3;
376
357
 
377
358
  //#endregion
378
359
  //#region src/Element/index.ts
379
- var Element_default = Component$7;
360
+ var Element_default = Component$4;
380
361
 
381
362
  //#endregion
382
363
  //#region src/helpers/Iterator/component.tsx
@@ -388,27 +369,6 @@ var Element_default = Component$7;
388
369
  * wrapped with `wrapComponent`. Children always take priority over the
389
370
  * component+data prop pattern.
390
371
  */
391
- const classifyData = (data) => {
392
- const items = data.filter((item) => item != null && !(typeof item === "object" && isEmpty(item)));
393
- if (items.length === 0) return null;
394
- let isSimple = true;
395
- let isComplex = true;
396
- for (const item of items) if (typeof item === "string" || typeof item === "number") isComplex = false;
397
- else if (typeof item === "object") isSimple = false;
398
- else {
399
- isSimple = false;
400
- isComplex = false;
401
- }
402
- if (isSimple) return {
403
- type: "simple",
404
- data: items
405
- };
406
- if (isComplex) return {
407
- type: "complex",
408
- data: items
409
- };
410
- return null;
411
- };
412
372
  const RESERVED_PROPS = [
413
373
  "children",
414
374
  "component",
@@ -419,7 +379,7 @@ const RESERVED_PROPS = [
419
379
  "itemProps",
420
380
  "wrapProps"
421
381
  ];
422
- const attachItemProps = ({ i, length }) => {
382
+ const buildExtendedProps = (i, length) => {
423
383
  const position = i + 1;
424
384
  return {
425
385
  index: i,
@@ -430,108 +390,98 @@ const attachItemProps = ({ i, length }) => {
430
390
  position
431
391
  };
432
392
  };
433
- const Component$6 = (props) => {
434
- const { itemKey, valueName, children, component, data, wrapComponent: Wrapper, wrapProps, itemProps } = props;
435
- const injectItemProps = useMemo(() => typeof itemProps === "function" ? itemProps : () => itemProps, [itemProps]);
436
- const injectWrapItemProps = useMemo(() => typeof wrapProps === "function" ? wrapProps : () => wrapProps, [wrapProps]);
437
- const getKey = useCallback((item, index) => {
438
- if (typeof itemKey === "function") return itemKey(item, index);
439
- return index;
440
- }, [itemKey]);
441
- const renderChild = (child, total = 1, i = 0) => {
442
- if (!itemProps && !Wrapper) return child;
443
- const extendedProps = attachItemProps({
444
- i,
445
- length: total
446
- });
447
- const finalItemProps = itemProps ? injectItemProps({}, extendedProps) : {};
448
- if (Wrapper) return /* @__PURE__ */ jsx(Wrapper, {
449
- ...wrapProps ? injectWrapItemProps({}, extendedProps) : {},
450
- children: render(child, finalItemProps)
451
- }, i);
452
- return render(child, {
453
- key: i,
454
- ...finalItemProps
455
- });
456
- };
457
- const renderChildren = () => {
458
- if (!children) return null;
459
- if (Array.isArray(children)) return Children.map(children, (item, i) => renderChild(item, children.length, i));
460
- if (isFragment(children)) {
461
- const fragmentChildren = children.props.children;
462
- const childrenLength = fragmentChildren.length;
463
- return fragmentChildren.map((item, i) => renderChild(item, childrenLength, i));
464
- }
465
- return renderChild(children);
466
- };
467
- const renderSimpleArray = (data) => {
468
- const { length } = data;
469
- if (length === 0) return null;
470
- return data.map((item, i) => {
471
- const key = getKey(item, i);
472
- const keyName = valueName ?? "children";
473
- const extendedProps = attachItemProps({
474
- i,
475
- length
476
- });
477
- const finalItemProps = {
478
- ...itemProps ? injectItemProps({ [keyName]: item }, extendedProps) : {},
479
- [keyName]: item
480
- };
481
- if (Wrapper) return /* @__PURE__ */ jsx(Wrapper, {
482
- ...wrapProps ? injectWrapItemProps({ [keyName]: item }, extendedProps) : {},
483
- children: render(component, finalItemProps)
484
- }, key);
485
- return render(component, {
486
- key,
487
- ...finalItemProps
488
- });
489
- });
490
- };
491
- const getObjectKey = (item, index) => {
492
- if (!itemKey) return item.key ?? item.id ?? item.itemId ?? index;
493
- if (typeof itemKey === "function") return itemKey(item, index);
494
- if (typeof itemKey === "string") return item[itemKey];
495
- return index;
393
+ const resolveCallback = (cb, source, ext) => {
394
+ if (!cb) return {};
395
+ return typeof cb === "function" ? cb(source, ext) : cb;
396
+ };
397
+ const renderSpec = (spec, ext, itemProps, wrapProps, Wrapper) => {
398
+ const finalItemProps = {
399
+ ...resolveCallback(itemProps, spec.source, ext),
400
+ ...spec.base
496
401
  };
497
- const renderComplexArray = (data) => {
498
- const { length } = data;
499
- if (length === 0) return null;
500
- return data.map((item, i) => {
501
- const { component: itemComponent, ...restItem } = item;
502
- const renderItem = itemComponent ?? component;
503
- const key = getObjectKey(restItem, i);
504
- const extendedProps = attachItemProps({
505
- i,
506
- length
507
- });
508
- const finalItemProps = {
509
- ...itemProps ? injectItemProps(item, extendedProps) : {},
510
- ...restItem
511
- };
512
- if (Wrapper && !itemComponent) return /* @__PURE__ */ jsx(Wrapper, {
513
- ...wrapProps ? injectWrapItemProps(item, extendedProps) : {},
514
- children: render(renderItem, finalItemProps)
515
- }, key);
516
- return render(renderItem, {
517
- key,
518
- ...finalItemProps
519
- });
520
- });
402
+ if (Wrapper && !spec.skipWrap) return /* @__PURE__ */ jsx(Wrapper, {
403
+ ...resolveCallback(wrapProps, spec.source, ext),
404
+ children: spec.isNode ? render(spec.target, finalItemProps) : render(spec.target, finalItemProps)
405
+ }, spec.key);
406
+ const propsWithKey = {
407
+ key: spec.key,
408
+ ...finalItemProps
521
409
  };
522
- const renderItems = () => {
523
- if (children) return renderChildren();
524
- if (component && Array.isArray(data)) {
525
- const classified = classifyData(data);
526
- if (!classified) return null;
527
- if (classified.type === "simple") return renderSimpleArray(classified.data);
528
- return renderComplexArray(classified.data);
529
- }
530
- return null;
410
+ return spec.isNode ? render(spec.target, propsWithKey) : render(spec.target, propsWithKey);
411
+ };
412
+ /** Normalize children (single, array, or fragment) into an array of nodes. */
413
+ const flattenChildren = (children) => {
414
+ if (Array.isArray(children)) return children;
415
+ if (isFragment(children)) return children.props.children;
416
+ return [children];
417
+ };
418
+ /** Drop nullish entries and empty objects (matches legacy behavior). */
419
+ const filterValidItems = (data) => data.filter((item) => item != null && !(typeof item === "object" && isEmpty(item)));
420
+ /** Determine if the array is uniformly simple (string/number) or complex (object). Mixed → null. */
421
+ const detectKind = (items) => {
422
+ let kind = null;
423
+ for (const item of items) {
424
+ const t = typeof item === "string" || typeof item === "number" ? "simple" : typeof item === "object" ? "complex" : null;
425
+ if (t === null) return null;
426
+ if (kind === null) kind = t;
427
+ else if (kind !== t) return null;
428
+ }
429
+ return kind;
430
+ };
431
+ const objectKey = (item, index, itemKey) => {
432
+ if (!itemKey) return item.key ?? item.id ?? item.itemId ?? index;
433
+ if (typeof itemKey === "function") return itemKey(item, index);
434
+ if (typeof itemKey === "string") return item[itemKey] ?? index;
435
+ return index;
436
+ };
437
+ const buildChildrenSpecs = (children) => flattenChildren(children).map((node, i) => ({
438
+ key: i,
439
+ target: node,
440
+ source: {},
441
+ base: {},
442
+ isNode: true,
443
+ skipWrap: false
444
+ }));
445
+ const buildSimpleSpecs = (items, component, valueName, itemKey) => {
446
+ const keyName = valueName ?? "children";
447
+ return items.map((value, i) => ({
448
+ key: typeof itemKey === "function" ? itemKey(value, i) : i,
449
+ target: component,
450
+ source: { [keyName]: value },
451
+ base: { [keyName]: value },
452
+ isNode: false,
453
+ skipWrap: false
454
+ }));
455
+ };
456
+ const buildObjectSpecs = (items, component, itemKey) => items.map((item, i) => {
457
+ const { component: itemComponent, ...rest } = item;
458
+ return {
459
+ key: objectKey(rest, i, itemKey),
460
+ target: itemComponent ?? component,
461
+ source: item,
462
+ base: rest,
463
+ isNode: false,
464
+ skipWrap: Boolean(itemComponent)
531
465
  };
532
- return renderItems();
466
+ });
467
+ const buildDataSpecs = (data, component, valueName, itemKey) => {
468
+ const items = filterValidItems(data);
469
+ if (items.length === 0) return null;
470
+ const kind = detectKind(items);
471
+ if (!kind) return null;
472
+ return kind === "simple" ? buildSimpleSpecs(items, component, valueName, itemKey) : buildObjectSpecs(items, component, itemKey);
473
+ };
474
+ const Component$3 = ({ itemKey, valueName, children, component, data, wrapComponent: Wrapper, wrapProps, itemProps }) => {
475
+ let specs = null;
476
+ if (children) {
477
+ if (!Array.isArray(children) && !isFragment(children) && !itemProps && !Wrapper) return children;
478
+ specs = buildChildrenSpecs(children);
479
+ } else if (component && Array.isArray(data)) specs = buildDataSpecs(data, component, valueName, itemKey);
480
+ if (!specs || specs.length === 0) return null;
481
+ const total = specs.length;
482
+ return Children.toArray(specs.map((spec, i) => renderSpec(spec, buildExtendedProps(i, total), itemProps, wrapProps, Wrapper)));
533
483
  };
534
- var component_default = Object.assign(memo(Component$6), {
484
+ var component_default = Object.assign(memo(Component$3), {
535
485
  isIterator: true,
536
486
  RESERVED_PROPS
537
487
  });
@@ -549,7 +499,7 @@ var Iterator_default = component_default;
549
499
  * is wrapped in an Element that receives all non-iterator props (e.g.,
550
500
  * layout, alignment, css), allowing the list to be styled as a single block.
551
501
  */
552
- const Component$5 = forwardRef(({ rootElement = false, ...props }, ref) => {
502
+ const Component$2 = ({ rootElement = false, ref, ...props }) => {
553
503
  const renderedList = /* @__PURE__ */ jsx(Iterator_default, { ...pick(props, Iterator_default.RESERVED_PROPS) });
554
504
  if (!rootElement) return renderedList;
555
505
  return /* @__PURE__ */ jsx(Element_default, {
@@ -557,581 +507,15 @@ const Component$5 = forwardRef(({ rootElement = false, ...props }, ref) => {
557
507
  ...omit(props, Iterator_default.RESERVED_PROPS),
558
508
  children: renderedList
559
509
  });
560
- });
561
- const name$4 = `${PKG_NAME}/List`;
562
- Component$5.displayName = name$4;
563
- Component$5.pkgName = PKG_NAME;
564
- Component$5.VITUS_LABS__COMPONENT = name$4;
565
-
566
- //#endregion
567
- //#region src/List/index.ts
568
- var List_default = Component$5;
569
-
570
- //#endregion
571
- //#region src/Portal/component.ts
572
- /**
573
- * Portal component that creates a new DOM element on mount, appends it to
574
- * the target location (defaults to document.body), and uses React's
575
- * createPortal to render children into it. The DOM element is cleaned up
576
- * on unmount. Accepts a custom DOMLocation for rendering into specific
577
- * containers (e.g., a modal root).
578
- */
579
- const Component$4 = ({ DOMLocation, tag = "div", children }) => {
580
- const [element, setElement] = useState();
581
- useEffect(() => {
582
- if (!tag) return void 0;
583
- const position = DOMLocation ?? document.body;
584
- const element = document.createElement(tag);
585
- setElement(element);
586
- position.appendChild(element);
587
- return () => {
588
- position.removeChild(element);
589
- };
590
- }, [tag, DOMLocation]);
591
- if (!tag || !element) return null;
592
- return createPortal(children, element);
593
- };
594
- const name$3 = `${PKG_NAME}/Portal`;
595
- Component$4.displayName = name$3;
596
- Component$4.pkgName = PKG_NAME;
597
- Component$4.VITUS_LABS__COMPONENT = name$3;
598
-
599
- //#endregion
600
- //#region src/Portal/index.ts
601
- var Portal_default = Component$4;
602
-
603
- //#endregion
604
- //#region src/Overlay/context.tsx
605
- /**
606
- * Context for nested overlay coordination. When a child overlay opens, it
607
- * sets the parent's blocked state to true, preventing the parent from
608
- * closing in response to click/hover events that belong to the child.
609
- */
610
- const context$1 = createContext({});
611
- const { Provider: Provider$1 } = context$1;
612
- const useOverlayContext = () => useContext(context$1);
613
- const Component = ({ children, blocked, setBlocked, setUnblocked }) => {
614
- return /* @__PURE__ */ jsx(Provider$1, {
615
- value: useMemo(() => ({
616
- blocked,
617
- setBlocked,
618
- setUnblocked
619
- }), [
620
- blocked,
621
- setBlocked,
622
- setUnblocked
623
- ]),
624
- children
625
- });
626
- };
627
-
628
- //#endregion
629
- //#region src/Overlay/useOverlay.tsx
630
- /**
631
- * Core hook powering the Overlay component. Manages open/close state, DOM
632
- * event listeners (click, hover, scroll, resize, ESC key), and dynamic
633
- * positioning of overlay content relative to its trigger. Supports dropdown,
634
- * tooltip, popover, and modal types with automatic edge-of-viewport flipping.
635
- * Event handlers are throttled for performance, and nested overlay blocking
636
- * is coordinated through the overlay context.
637
- */
638
- const sel = (cond, a, b) => cond ? a : b;
639
- const devWarn = (msg) => {
640
- if (!IS_DEVELOPMENT) return;
641
- console.warn(msg);
642
- };
643
- const calcDropdownVertical = (c, t, align, alignX, offsetX, offsetY) => {
644
- const pos = {};
645
- const topPos = t.top - offsetY - c.height;
646
- const bottomPos = t.bottom + offsetY;
647
- const leftPos = t.left + offsetX;
648
- const rightPos = t.right - offsetX - c.width;
649
- const fitsTop = topPos >= 0;
650
- const fitsBottom = bottomPos + c.height <= window.innerHeight;
651
- const fitsLeft = leftPos + c.width <= window.innerWidth;
652
- const fitsRight = rightPos >= 0;
653
- const useTop = sel(align === "top", fitsTop, !fitsBottom);
654
- pos.top = sel(useTop, topPos, bottomPos);
655
- const resolvedAlignY = sel(useTop, "top", "bottom");
656
- let resolvedAlignX = alignX;
657
- if (alignX === "left") {
658
- pos.left = sel(fitsLeft, leftPos, rightPos);
659
- resolvedAlignX = sel(fitsLeft, "left", "right");
660
- } else if (alignX === "right") {
661
- pos.left = sel(fitsRight, rightPos, leftPos);
662
- resolvedAlignX = sel(fitsRight, "right", "left");
663
- } else {
664
- const center = t.left + (t.right - t.left) / 2 - c.width / 2;
665
- const fitsCL = center >= 0;
666
- const fitsCR = center + c.width <= window.innerWidth;
667
- if (fitsCL && fitsCR) {
668
- resolvedAlignX = "center";
669
- pos.left = center;
670
- } else if (fitsCL) {
671
- resolvedAlignX = "left";
672
- pos.left = leftPos;
673
- } else if (fitsCR) {
674
- resolvedAlignX = "right";
675
- pos.left = rightPos;
676
- }
677
- }
678
- return {
679
- pos,
680
- resolvedAlignX,
681
- resolvedAlignY
682
- };
683
- };
684
- const calcDropdownHorizontal = (c, t, align, alignY, offsetX, offsetY) => {
685
- const pos = {};
686
- const leftPos = t.left - offsetX - c.width;
687
- const rightPos = t.right + offsetX;
688
- const topPos = t.top + offsetY;
689
- const bottomPos = t.bottom - offsetY - c.height;
690
- const fitsLeft = leftPos >= 0;
691
- const fitsRight = rightPos + c.width <= window.innerWidth;
692
- const fitsTop = topPos + c.height <= window.innerHeight;
693
- const fitsBottom = bottomPos >= 0;
694
- const useLeft = sel(align === "left", fitsLeft, !fitsRight);
695
- pos.left = sel(useLeft, leftPos, rightPos);
696
- const resolvedAlignX = sel(useLeft, "left", "right");
697
- let resolvedAlignY = alignY;
698
- if (alignY === "top") {
699
- pos.top = sel(fitsTop, topPos, bottomPos);
700
- resolvedAlignY = sel(fitsTop, "top", "bottom");
701
- } else if (alignY === "bottom") {
702
- pos.top = sel(fitsBottom, bottomPos, topPos);
703
- resolvedAlignY = sel(fitsBottom, "bottom", "top");
704
- } else {
705
- const center = t.top + (t.bottom - t.top) / 2 - c.height / 2;
706
- const fitsCT = center >= 0;
707
- const fitsCB = center + c.height <= window.innerHeight;
708
- if (fitsCT && fitsCB) {
709
- resolvedAlignY = "center";
710
- pos.top = center;
711
- } else if (fitsCT) {
712
- resolvedAlignY = "top";
713
- pos.top = topPos;
714
- } else if (fitsCB) {
715
- resolvedAlignY = "bottom";
716
- pos.top = bottomPos;
717
- }
718
- }
719
- return {
720
- pos,
721
- resolvedAlignX,
722
- resolvedAlignY
723
- };
724
- };
725
- const calcModalPos = (c, alignX, alignY, offsetX, offsetY) => {
726
- const pos = {};
727
- switch (alignX) {
728
- case "right":
729
- pos.right = offsetX;
730
- break;
731
- case "left":
732
- pos.left = offsetX;
733
- break;
734
- case "center":
735
- pos.left = window.innerWidth / 2 - c.width / 2;
736
- break;
737
- default: pos.right = offsetX;
738
- }
739
- switch (alignY) {
740
- case "top":
741
- pos.top = offsetY;
742
- break;
743
- case "center":
744
- pos.top = window.innerHeight / 2 - c.height / 2;
745
- break;
746
- case "bottom":
747
- pos.bottom = offsetY;
748
- break;
749
- default: pos.top = offsetY;
750
- }
751
- return pos;
752
510
  };
753
- const adjustForAncestor = (pos, ancestor) => {
754
- if (ancestor.top === 0 && ancestor.left === 0) return pos;
755
- const result = { ...pos };
756
- if (typeof result.top === "number") result.top -= ancestor.top;
757
- if (typeof result.bottom === "number") result.bottom += ancestor.top;
758
- if (typeof result.left === "number") result.left -= ancestor.left;
759
- if (typeof result.right === "number") result.right += ancestor.left;
760
- return result;
761
- };
762
- const computePosition = (type, align, alignX, alignY, offsetX, offsetY, triggerEl, contentEl, ancestorOffset) => {
763
- const isDropdown = [
764
- "dropdown",
765
- "tooltip",
766
- "popover"
767
- ].includes(type);
768
- if (isDropdown && (!triggerEl || !contentEl)) {
769
- devWarn(`[@vitus-labs/elements] Overlay (${type}): ${triggerEl ? "contentRef" : "triggerRef"} is not attached. Position cannot be calculated without both refs.`);
770
- return { pos: {} };
771
- }
772
- if (isDropdown && triggerEl && contentEl) {
773
- const c = contentEl.getBoundingClientRect();
774
- const t = triggerEl.getBoundingClientRect();
775
- const result = align === "top" || align === "bottom" ? calcDropdownVertical(c, t, align, alignX, offsetX, offsetY) : calcDropdownHorizontal(c, t, align, alignY, offsetX, offsetY);
776
- return {
777
- pos: adjustForAncestor(result.pos, ancestorOffset),
778
- resolvedAlignX: result.resolvedAlignX,
779
- resolvedAlignY: result.resolvedAlignY
780
- };
781
- }
782
- if (type === "modal") {
783
- if (!contentEl) {
784
- devWarn("[@vitus-labs/elements] Overlay (modal): contentRef is not attached. Modal position cannot be calculated without a content element.");
785
- return { pos: {} };
786
- }
787
- return { pos: adjustForAncestor(calcModalPos(contentEl.getBoundingClientRect(), alignX, alignY, offsetX, offsetY), ancestorOffset) };
788
- }
789
- return { pos: {} };
790
- };
791
- const processVisibilityEvent = (e, active, openOn, closeOn, isTrigger, isContent, showContent, hideContent) => {
792
- if (!active && openOn === "click" && e.type === "click" && isTrigger(e)) {
793
- showContent();
794
- return;
795
- }
796
- if (!active) return;
797
- if (closeOn === "hover" && e.type === "scroll") {
798
- hideContent();
799
- return;
800
- }
801
- if (e.type !== "click") return;
802
- if (closeOn === "click") hideContent();
803
- else if (closeOn === "clickOnTrigger" && isTrigger(e)) hideContent();
804
- else if (closeOn === "clickOutsideContent" && !isContent(e)) hideContent();
805
- };
806
- const useOverlay = ({ isOpen = false, openOn = "click", closeOn = "click", type = "dropdown", position = "fixed", align = "bottom", alignX = "left", alignY = "bottom", offsetX = 0, offsetY = 0, throttleDelay = 200, parentContainer, closeOnEsc = true, disabled, onOpen, onClose } = {}) => {
807
- const { rootSize } = useContext(context);
808
- const ctx = useOverlayContext();
809
- const [isContentLoaded, setContentLoaded] = useState(false);
810
- const [innerAlignX, setInnerAlignX] = useState(alignX);
811
- const [innerAlignY, setInnerAlignY] = useState(alignY);
812
- const [blocked, handleBlocked] = useState(false);
813
- const [active, handleActive] = useState(isOpen);
814
- const triggerRef = useRef(null);
815
- const contentRef = useRef(null);
816
- const setBlocked = useCallback(() => handleBlocked(true), []);
817
- const setUnblocked = useCallback(() => handleBlocked(false), []);
818
- const showContent = useCallback(() => {
819
- handleActive(true);
820
- }, []);
821
- const hideContent = useCallback(() => {
822
- handleActive(false);
823
- }, []);
824
- const getAncestorOffset = useCallback(() => {
825
- if (position !== "absolute" || !contentRef.current) return {
826
- top: 0,
827
- left: 0
828
- };
829
- const offsetParent = contentRef.current.offsetParent;
830
- if (!offsetParent || offsetParent === document.body) return {
831
- top: 0,
832
- left: 0
833
- };
834
- const rect = offsetParent.getBoundingClientRect();
835
- return {
836
- top: rect.top,
837
- left: rect.left
838
- };
839
- }, [position]);
840
- const calculateContentPosition = useCallback(() => {
841
- if (!active || !isContentLoaded) return {};
842
- const result = computePosition(type, align, alignX, alignY, offsetX, offsetY, triggerRef.current, contentRef.current, getAncestorOffset());
843
- if (result.resolvedAlignX) setInnerAlignX(result.resolvedAlignX);
844
- if (result.resolvedAlignY) setInnerAlignY(result.resolvedAlignY);
845
- return result.pos;
846
- }, [
847
- isContentLoaded,
848
- active,
849
- align,
850
- alignX,
851
- alignY,
852
- offsetX,
853
- offsetY,
854
- type,
855
- getAncestorOffset
856
- ]);
857
- const assignContentPosition = useCallback((values = {}) => {
858
- if (!contentRef.current) return;
859
- const el = contentRef.current;
860
- const setValue = (param) => value(param, rootSize);
861
- el.style.position = position;
862
- el.style.top = values.top != null ? setValue(values.top) : "";
863
- el.style.bottom = values.bottom != null ? setValue(values.bottom) : "";
864
- el.style.left = values.left != null ? setValue(values.left) : "";
865
- el.style.right = values.right != null ? setValue(values.right) : "";
866
- }, [position, rootSize]);
867
- const setContentPosition = useCallback(() => {
868
- assignContentPosition(calculateContentPosition());
869
- }, [assignContentPosition, calculateContentPosition]);
870
- const isNodeOrChild = useCallback((ref) => (e) => {
871
- if (e?.target && ref.current) return ref.current.contains(e.target) || e.target === ref.current;
872
- return false;
873
- }, []);
874
- const handleVisibilityByEventType = useCallback((e) => {
875
- if (blocked || disabled) return;
876
- processVisibilityEvent(e, active, openOn, closeOn, isNodeOrChild(triggerRef), isNodeOrChild(contentRef), showContent, hideContent);
877
- }, [
878
- active,
879
- blocked,
880
- disabled,
881
- openOn,
882
- closeOn,
883
- hideContent,
884
- showContent,
885
- isNodeOrChild
886
- ]);
887
- const latestSetContentPosition = useRef(setContentPosition);
888
- latestSetContentPosition.current = setContentPosition;
889
- const latestHandleVisibility = useRef(handleVisibilityByEventType);
890
- latestHandleVisibility.current = handleVisibilityByEventType;
891
- const handleContentPosition = useMemo(() => throttle(() => latestSetContentPosition.current(), throttleDelay), [throttleDelay]);
892
- const handleClick = handleVisibilityByEventType;
893
- const handleVisibility = useMemo(() => throttle((e) => latestHandleVisibility.current(e), throttleDelay), [throttleDelay]);
894
- useEffect(() => {
895
- setInnerAlignX(alignX);
896
- setInnerAlignY(alignY);
897
- if (disabled) hideContent();
898
- }, [
899
- disabled,
900
- alignX,
901
- alignY,
902
- hideContent
903
- ]);
904
- useEffect(() => {
905
- if (!active || !isContentLoaded) return void 0;
906
- setContentPosition();
907
- const rafId = requestAnimationFrame(() => setContentPosition());
908
- return () => cancelAnimationFrame(rafId);
909
- }, [
910
- active,
911
- isContentLoaded,
912
- setContentPosition
913
- ]);
914
- const prevActiveRef = useRef(false);
915
- useEffect(() => {
916
- const wasActive = prevActiveRef.current;
917
- prevActiveRef.current = active;
918
- if (active && !wasActive) {
919
- onOpen?.();
920
- ctx.setBlocked?.();
921
- } else if (!active && wasActive) {
922
- setContentLoaded(false);
923
- onClose?.();
924
- ctx.setUnblocked?.();
925
- } else if (!active) setContentLoaded(false);
926
- return () => {
927
- if (active) {
928
- onClose?.();
929
- ctx.setUnblocked?.();
930
- }
931
- };
932
- }, [
933
- active,
934
- ctx,
935
- onClose,
936
- onOpen
937
- ]);
938
- useEffect(() => {
939
- if (!closeOnEsc || !active || blocked) return void 0;
940
- const handleEscKey = (e) => {
941
- if (e.key === "Escape") hideContent();
942
- };
943
- window.addEventListener("keydown", handleEscKey);
944
- return () => {
945
- window.removeEventListener("keydown", handleEscKey);
946
- };
947
- }, [
948
- active,
949
- blocked,
950
- closeOnEsc,
951
- hideContent
952
- ]);
953
- useEffect(() => {
954
- if (!active) return void 0;
955
- const shouldSetOverflow = type === "modal";
956
- const onScroll = (e) => {
957
- handleContentPosition();
958
- handleVisibility(e);
959
- };
960
- if (shouldSetOverflow) document.body.style.overflow = "hidden";
961
- window.addEventListener("resize", handleContentPosition);
962
- window.addEventListener("scroll", onScroll, { passive: true });
963
- return () => {
964
- if (shouldSetOverflow) document.body.style.overflow = "";
965
- window.removeEventListener("resize", handleContentPosition);
966
- window.removeEventListener("scroll", onScroll);
967
- };
968
- }, [
969
- active,
970
- type,
971
- handleVisibility,
972
- handleContentPosition
973
- ]);
974
- useEffect(() => {
975
- if (!active || !parentContainer) return void 0;
976
- if (closeOn !== "hover") parentContainer.style.overflow = "hidden";
977
- const onScroll = (e) => {
978
- handleContentPosition();
979
- handleVisibility(e);
980
- };
981
- parentContainer.addEventListener("scroll", onScroll, { passive: true });
982
- return () => {
983
- parentContainer.style.overflow = "";
984
- parentContainer.removeEventListener("scroll", onScroll);
985
- };
986
- }, [
987
- active,
988
- parentContainer,
989
- closeOn,
990
- handleContentPosition,
991
- handleVisibility
992
- ]);
993
- useEffect(() => {
994
- if (blocked || disabled) return void 0;
995
- if (openOn === "click" || [
996
- "click",
997
- "clickOnTrigger",
998
- "clickOutsideContent"
999
- ].includes(closeOn)) window.addEventListener("click", handleClick);
1000
- return () => {
1001
- window.removeEventListener("click", handleClick);
1002
- };
1003
- }, [
1004
- openOn,
1005
- closeOn,
1006
- blocked,
1007
- disabled,
1008
- handleClick
1009
- ]);
1010
- const hoverTimeoutRef = useRef(null);
1011
- useEffect(() => {
1012
- if (blocked || disabled || !(openOn === "hover" || closeOn === "hover")) return void 0;
1013
- const trigger = triggerRef.current;
1014
- const content = contentRef.current;
1015
- const clearHoverTimeout = () => {
1016
- if (hoverTimeoutRef.current != null) {
1017
- clearTimeout(hoverTimeoutRef.current);
1018
- hoverTimeoutRef.current = null;
1019
- }
1020
- };
1021
- const scheduleHide = () => {
1022
- clearHoverTimeout();
1023
- hoverTimeoutRef.current = setTimeout(hideContent, 100);
1024
- };
1025
- const onTriggerEnter = () => {
1026
- clearHoverTimeout();
1027
- if (openOn === "hover" && !active) showContent();
1028
- };
1029
- const onTriggerLeave = () => {
1030
- if (closeOn === "hover" && active) scheduleHide();
1031
- };
1032
- const onContentEnter = () => {
1033
- clearHoverTimeout();
1034
- };
1035
- const onContentLeave = () => {
1036
- if (closeOn === "hover" && active) scheduleHide();
1037
- };
1038
- if (trigger) {
1039
- trigger.addEventListener("mouseenter", onTriggerEnter);
1040
- trigger.addEventListener("mouseleave", onTriggerLeave);
1041
- }
1042
- if (content) {
1043
- content.addEventListener("mouseenter", onContentEnter);
1044
- content.addEventListener("mouseleave", onContentLeave);
1045
- }
1046
- return () => {
1047
- clearHoverTimeout();
1048
- if (trigger) {
1049
- trigger.removeEventListener("mouseenter", onTriggerEnter);
1050
- trigger.removeEventListener("mouseleave", onTriggerLeave);
1051
- }
1052
- if (content) {
1053
- content.removeEventListener("mouseenter", onContentEnter);
1054
- content.removeEventListener("mouseleave", onContentLeave);
1055
- }
1056
- };
1057
- }, [
1058
- active,
1059
- isContentLoaded,
1060
- blocked,
1061
- disabled,
1062
- openOn,
1063
- closeOn,
1064
- showContent,
1065
- hideContent
1066
- ]);
1067
- return {
1068
- triggerRef,
1069
- contentRef: useCallback((node) => {
1070
- if (node) {
1071
- contentRef.current = node;
1072
- setContentLoaded(true);
1073
- }
1074
- }, []),
1075
- active,
1076
- align,
1077
- alignX: innerAlignX,
1078
- alignY: innerAlignY,
1079
- showContent,
1080
- hideContent,
1081
- blocked,
1082
- setBlocked,
1083
- setUnblocked,
1084
- Provider: Component
1085
- };
1086
- };
1087
-
1088
- //#endregion
1089
- //#region src/Overlay/component.tsx
1090
- /**
1091
- * Overlay component that renders a trigger element and conditionally shows
1092
- * content via a Portal. The trigger receives a ref and optional show/hide
1093
- * callbacks; the content is positioned and managed by the useOverlay hook.
1094
- * A context Provider wraps the content to support nested overlays (e.g.,
1095
- * a dropdown inside another dropdown) via blocked-state propagation.
1096
- */
1097
- const IS_BROWSER = typeof window !== "undefined";
1098
- const Component$3 = ({ children, trigger, DOMLocation, triggerRefName = "ref", contentRefName = "ref", ...props }) => {
1099
- const { active, triggerRef, contentRef, showContent, hideContent, align, alignX, alignY, Provider, ...ctx } = useOverlay(props);
1100
- const { openOn, closeOn } = props;
1101
- const passHandlers = useMemo(() => openOn === "manual" || closeOn === "manual" || closeOn === "clickOutsideContent", [openOn, closeOn]);
1102
- return /* @__PURE__ */ jsxs(Fragment, { children: [render(trigger, {
1103
- [triggerRefName]: triggerRef,
1104
- active,
1105
- ...passHandlers ? {
1106
- showContent,
1107
- hideContent
1108
- } : {}
1109
- }), IS_BROWSER && active && /* @__PURE__ */ jsx(Portal_default, {
1110
- DOMLocation,
1111
- children: /* @__PURE__ */ jsx(Provider, {
1112
- ...ctx,
1113
- children: render(children, {
1114
- [contentRefName]: contentRef,
1115
- active,
1116
- align,
1117
- alignX,
1118
- alignY,
1119
- ...passHandlers ? {
1120
- showContent,
1121
- hideContent
1122
- } : {}
1123
- })
1124
- })
1125
- })] });
1126
- };
1127
- const name$2 = `${PKG_NAME}/Overlay`;
1128
- Component$3.displayName = name$2;
1129
- Component$3.pkgName = PKG_NAME;
1130
- Component$3.VITUS_LABS__COMPONENT = name$2;
511
+ const name$2 = `${PKG_NAME}/List`;
512
+ Component$2.displayName = name$2;
513
+ Component$2.pkgName = PKG_NAME;
514
+ Component$2.VITUS_LABS__COMPONENT = name$2;
1131
515
 
1132
516
  //#endregion
1133
- //#region src/Overlay/index.ts
1134
- var Overlay_default = Component$3;
517
+ //#region src/List/index.ts
518
+ var List_default = Component$2;
1135
519
 
1136
520
  //#endregion
1137
521
  //#region src/Text/styled.ts
@@ -1146,9 +530,7 @@ const styles = ({ css, theme: t }) => css`
1146
530
  ${t.extraStyles && extendCss(t.extraStyles)};
1147
531
  `;
1148
532
  var styled_default = styled(textComponent)`
1149
- color: inherit;
1150
- font-weight: inherit;
1151
- line-height: 1;
533
+ ${false};
1152
534
 
1153
535
  ${makeItResponsive({
1154
536
  key: "$text",
@@ -1160,7 +542,7 @@ var styled_default = styled(textComponent)`
1160
542
 
1161
543
  //#endregion
1162
544
  //#region src/Text/component.tsx
1163
- const Component$2 = forwardRef(({ paragraph, label, children, tag, css, ...props }, ref) => {
545
+ const Component$1 = ({ paragraph, label, children, tag, css, ref, ...props }) => {
1164
546
  const renderContent = (as = void 0) => /* @__PURE__ */ jsx(styled_default, {
1165
547
  ref,
1166
548
  as,
@@ -1170,16 +552,16 @@ const Component$2 = forwardRef(({ paragraph, label, children, tag, css, ...props
1170
552
  });
1171
553
  let finalTag;
1172
554
  return renderContent(finalTag);
1173
- });
555
+ };
1174
556
  const name$1 = `${PKG_NAME}/Text`;
1175
- Component$2.displayName = name$1;
1176
- Component$2.pkgName = PKG_NAME;
1177
- Component$2.VITUS_LABS__COMPONENT = name$1;
1178
- Component$2.isText = true;
557
+ Component$1.displayName = name$1;
558
+ Component$1.pkgName = PKG_NAME;
559
+ Component$1.VITUS_LABS__COMPONENT = name$1;
560
+ Component$1.isText = true;
1179
561
 
1180
562
  //#endregion
1181
563
  //#region src/Text/index.ts
1182
- var Text_default = Component$2;
564
+ var Text_default = Component$1;
1183
565
 
1184
566
  //#endregion
1185
567
  //#region src/Util/component.tsx
@@ -1188,7 +570,7 @@ var Text_default = Component$2;
1188
570
  * children without adding any DOM nodes of its own. Uses the core `render`
1189
571
  * helper to clone children with the merged props.
1190
572
  */
1191
- const Component$1 = ({ children, className, style }) => {
573
+ const Component = ({ children, className, style }) => {
1192
574
  const mergedClasses = useMemo(() => Array.isArray(className) ? className.join(" ") : className, [className]);
1193
575
  const finalProps = {};
1194
576
  if (style) finalProps.style = style;
@@ -1196,14 +578,14 @@ const Component$1 = ({ children, className, style }) => {
1196
578
  return render(children, finalProps);
1197
579
  };
1198
580
  const name = `${PKG_NAME}/Util`;
1199
- Component$1.displayName = name;
1200
- Component$1.pkgName = PKG_NAME;
1201
- Component$1.VITUS_LABS__COMPONENT = name;
581
+ Component.displayName = name;
582
+ Component.pkgName = PKG_NAME;
583
+ Component.VITUS_LABS__COMPONENT = name;
1202
584
 
1203
585
  //#endregion
1204
586
  //#region src/Util/index.ts
1205
- var Util_default = Component$1;
587
+ var Util_default = Component;
1206
588
 
1207
589
  //#endregion
1208
- export { Element_default as Element, List_default as List, Overlay_default as Overlay, Component as OverlayProvider, Portal_default as Portal, Provider, Text_default as Text, Util_default as Util, useOverlay };
590
+ export { Element_default as Element, List_default as List, Provider, Text_default as Text, Util_default as Util };
1209
591
  //# sourceMappingURL=vitus-labs-elements.native.js.map