@vitus-labs/elements 2.0.0-alpha.9 → 2.0.0-beta.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.
@@ -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, useImperativeHandle, 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, forwardRef, 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";
@@ -21,7 +20,7 @@ const IS_DEVELOPMENT = process.env.NODE_ENV !== "production";
21
20
  * equalCols flex distribution. The "content" slot gets `flex: 1` to
22
21
  * fill remaining space between before and after.
23
22
  */
24
- const { styled: styled$2, css: css$2, component: component$2 } = config;
23
+ const { styled: styled$2, css: css$2, component: component$1 } = config;
25
24
  const equalColsCSS = `
26
25
  flex: 1;
27
26
  `;
@@ -67,7 +66,7 @@ const styles$2 = ({ css, theme: t, rootSize }) => css`
67
66
 
68
67
  ${t.extraStyles && extendCss(t.extraStyles)};
69
68
  `;
70
- const StyledComponent = styled$2(component$2)`
69
+ const StyledComponent = styled$2(component$1)`
71
70
  ${""};
72
71
 
73
72
  display: flex;
@@ -86,11 +85,22 @@ const StyledComponent = styled$2(component$2)`
86
85
 
87
86
  //#endregion
88
87
  //#region src/helpers/Content/component.tsx
89
- const Component$9 = ({ contentType, tag, parentDirection, direction, alignX, alignY, equalCols, gap, extendCss, ...props }) => {
88
+ /**
89
+ * Memoized 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-vl-element` attribute in development for debugging.
93
+ *
94
+ * Children are passed as raw content and rendered inside the memo boundary
95
+ * via core `render()` — this lets React.memo skip re-renders when the
96
+ * content reference is stable (common for component-type or string content).
97
+ */
98
+ const Component$6 = ({ contentType, tag, parentDirection, direction, alignX, alignY, equalCols, gap, extendCss, children, ...props }) => {
99
+ const debugProps = IS_DEVELOPMENT ? { "data-vl-element": contentType } : {};
90
100
  return /* @__PURE__ */ jsx(StyledComponent, {
91
101
  as: tag,
92
102
  $contentType: contentType,
93
- $element: {
103
+ $element: useMemo(() => ({
94
104
  contentType,
95
105
  parentDirection,
96
106
  direction,
@@ -99,16 +109,26 @@ const Component$9 = ({ contentType, tag, parentDirection, direction, alignX, ali
99
109
  equalCols,
100
110
  gap,
101
111
  extraStyles: extendCss
102
- },
103
- ...IS_DEVELOPMENT ? { "data-vl-element": contentType } : {},
104
- ...props
112
+ }), [
113
+ contentType,
114
+ parentDirection,
115
+ direction,
116
+ alignX,
117
+ alignY,
118
+ equalCols,
119
+ gap,
120
+ extendCss
121
+ ]),
122
+ ...debugProps,
123
+ ...props,
124
+ children: render(children)
105
125
  });
106
126
  };
107
- var component_default = memo(Component$9);
127
+ var component_default$1 = memo(Component$6);
108
128
 
109
129
  //#endregion
110
130
  //#region src/helpers/Content/index.ts
111
- var Content_default = component_default;
131
+ var Content_default = component_default$1;
112
132
 
113
133
  //#endregion
114
134
  //#region src/helpers/Wrapper/styled.ts
@@ -119,7 +139,7 @@ var Content_default = component_default;
119
139
  * split flex behavior across two DOM nodes for button/fieldset/legend
120
140
  * elements where a single flex container is insufficient.
121
141
  */
122
- const { styled: styled$1, css: css$1, component: component$1 } = config;
142
+ const { styled: styled$1, css: css$1, component } = config;
123
143
  const childFixCSS = `
124
144
  display: flex;
125
145
  flex: 1;
@@ -128,6 +148,7 @@ const childFixCSS = `
128
148
  `;
129
149
  const blockCSS = `
130
150
  align-self: stretch;
151
+ width: 100%;
131
152
  `;
132
153
  const styles$1 = ({ theme: t, css }) => css`
133
154
  ${false};
@@ -139,15 +160,15 @@ const styles$1 = ({ theme: t, css }) => css`
139
160
  })};
140
161
 
141
162
  ${t.block && blockCSS};
142
-
143
163
  ${false};
164
+
144
165
  ${false};
145
166
  ${false};
146
167
 
147
168
  ${t.extraStyles && extendCss(t.extraStyles)};
148
169
  `;
149
170
  const platformCSS = `display: flex;`;
150
- var styled_default$1 = styled$1(component$1)`
171
+ var styled_default$1 = styled$1(component)`
151
172
  position: relative;
152
173
  ${platformCSS};
153
174
 
@@ -171,27 +192,55 @@ var styled_default$1 = styled$1(component$1)`
171
192
  * support `display: flex` consistently across browsers.
172
193
  */
173
194
  const DEV_PROPS = IS_DEVELOPMENT ? { "data-vl-element": "Element" } : {};
174
- const Component$8 = forwardRef(({ children, tag, block, extendCss, direction, alignX, alignY, equalCols, isInline, ...props }, ref) => {
175
- return /* @__PURE__ */ jsx(styled_default$1, {
195
+ const Component$5 = forwardRef(({ children, tag, block, extendCss, direction, alignX, alignY, equalCols, isInline, ...props }, ref) => {
196
+ const COMMON_PROPS = {
176
197
  ...props,
177
198
  ...DEV_PROPS,
178
199
  ref,
179
- as: tag,
180
- $element: {
181
- block,
182
- direction,
183
- alignX,
184
- alignY,
185
- equalCols,
186
- extraStyles: extendCss
187
- },
200
+ as: tag
201
+ };
202
+ const normalElement = useMemo(() => ({
203
+ block,
204
+ direction,
205
+ alignX,
206
+ alignY,
207
+ equalCols,
208
+ extraStyles: extendCss
209
+ }), [
210
+ block,
211
+ direction,
212
+ alignX,
213
+ alignY,
214
+ equalCols,
215
+ extendCss
216
+ ]);
217
+ useMemo(() => ({
218
+ parentFix: true,
219
+ block,
220
+ extraStyles: extendCss
221
+ }), [block, extendCss]);
222
+ useMemo(() => ({
223
+ childFix: true,
224
+ direction,
225
+ alignX,
226
+ alignY,
227
+ equalCols
228
+ }), [
229
+ direction,
230
+ alignX,
231
+ alignY,
232
+ equalCols
233
+ ]);
234
+ return /* @__PURE__ */ jsx(styled_default$1, {
235
+ ...COMMON_PROPS,
236
+ $element: normalElement,
188
237
  children
189
238
  });
190
239
  });
191
240
 
192
241
  //#endregion
193
242
  //#region src/helpers/Wrapper/index.ts
194
- var Wrapper_default = Component$8;
243
+ var Wrapper_default = Component$5;
195
244
 
196
245
  //#endregion
197
246
  //#region src/Element/component.tsx
@@ -207,7 +256,7 @@ const defaultDirection = "inline";
207
256
  const defaultContentDirection = "rows";
208
257
  const defaultAlignX = "left";
209
258
  const defaultAlignY = "center";
210
- const Component$7 = forwardRef(({ innerRef, tag, label, content, children, beforeContent, afterContent, block, equalCols, gap, direction, alignX = defaultAlignX, alignY = defaultAlignY, css, contentCss, beforeContentCss, afterContentCss, contentDirection = defaultContentDirection, contentAlignX = defaultAlignX, contentAlignY = defaultAlignY, beforeContentDirection = defaultDirection, beforeContentAlignX = defaultAlignX, beforeContentAlignY = defaultAlignY, afterContentDirection = defaultDirection, afterContentAlignX = defaultAlignX, afterContentAlignY = defaultAlignY, ...props }, ref) => {
259
+ const Component$4 = 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) => {
211
260
  const isSimpleElement = !beforeContent && !afterContent;
212
261
  const CHILDREN = children ?? content ?? label;
213
262
  const isInline = false;
@@ -236,8 +285,21 @@ const Component$7 = forwardRef(({ innerRef, tag, label, content, children, befor
236
285
  alignY,
237
286
  direction
238
287
  ]);
288
+ const equalizeRef = useRef(null);
289
+ const externalRef = ref ?? innerRef;
290
+ const mergedRef = useCallback((node) => {
291
+ equalizeRef.current = node;
292
+ if (typeof externalRef === "function") externalRef(node);
293
+ else if (externalRef != null) externalRef.current = node;
294
+ }, [externalRef]);
295
+ useLayoutEffect(() => {}, [
296
+ equalBeforeAfter,
297
+ beforeContent,
298
+ afterContent,
299
+ direction
300
+ ]);
239
301
  const WRAPPER_PROPS = {
240
- ref: ref ?? innerRef,
302
+ ref: mergedRef,
241
303
  extendCss: css,
242
304
  tag,
243
305
  block,
@@ -246,7 +308,6 @@ const Component$7 = forwardRef(({ innerRef, tag, label, content, children, befor
246
308
  alignY: wrapperAlignY,
247
309
  as: void 0
248
310
  };
249
- const contentRenderOutput = render(CHILDREN);
250
311
  return /* @__PURE__ */ jsxs(Wrapper_default, {
251
312
  ...props,
252
313
  ...WRAPPER_PROPS,
@@ -262,9 +323,9 @@ const Component$7 = forwardRef(({ innerRef, tag, label, content, children, befor
262
323
  alignY: beforeContentAlignY,
263
324
  equalCols,
264
325
  gap,
265
- children: render(beforeContent)
326
+ children: beforeContent
266
327
  }),
267
- isSimpleElement ? contentRenderOutput : /* @__PURE__ */ jsx(Content_default, {
328
+ isSimpleElement ? render(CHILDREN) : /* @__PURE__ */ jsx(Content_default, {
268
329
  tag: SUB_TAG,
269
330
  contentType: "content",
270
331
  parentDirection: wrapperDirection,
@@ -273,7 +334,7 @@ const Component$7 = forwardRef(({ innerRef, tag, label, content, children, befor
273
334
  alignX: contentAlignX,
274
335
  alignY: contentAlignY,
275
336
  equalCols,
276
- children: contentRenderOutput
337
+ children: CHILDREN
277
338
  }),
278
339
  afterContent && /* @__PURE__ */ jsx(Content_default, {
279
340
  tag: SUB_TAG,
@@ -285,66 +346,19 @@ const Component$7 = forwardRef(({ innerRef, tag, label, content, children, befor
285
346
  alignY: afterContentAlignY,
286
347
  equalCols,
287
348
  gap,
288
- children: render(afterContent)
349
+ children: afterContent
289
350
  })
290
351
  ]
291
352
  });
292
353
  });
293
- const name$5 = `${PKG_NAME}/Element`;
294
- Component$7.displayName = name$5;
295
- Component$7.pkgName = PKG_NAME;
296
- Component$7.VITUS_LABS__COMPONENT = name$5;
297
-
298
- //#endregion
299
- //#region src/Element/withEqualSizeBeforeAfter.tsx
300
- /**
301
- * HOC that equalizes the dimensions of beforeContent and afterContent areas.
302
- * After render, it measures both DOM nodes via useLayoutEffect and sets the
303
- * larger dimension on both so they match. Uses width for inline direction
304
- * and height for rows direction. This is useful for centering the main
305
- * content when before/after slots have different intrinsic sizes.
306
- */
307
- const types = {
308
- height: "offsetHeight",
309
- width: "offsetWidth"
310
- };
311
- const equalize = (beforeEl, afterEl, type) => {
312
- const prop = types[type];
313
- const beforeSize = beforeEl[prop];
314
- const afterSize = afterEl[prop];
315
- if (Number.isInteger(beforeSize) && Number.isInteger(afterSize)) {
316
- const maxSize = `${Math.max(beforeSize, afterSize)}px`;
317
- beforeEl.style[type] = maxSize;
318
- afterEl.style[type] = maxSize;
319
- }
320
- };
321
- const withEqualBeforeAfter = (WrappedComponent) => {
322
- const displayName = WrappedComponent.displayName ?? WrappedComponent.name ?? "Component";
323
- const Enhanced = ({ equalBeforeAfter, direction, afterContent, beforeContent, ref, ...rest }) => {
324
- const internalRef = useRef(null);
325
- useImperativeHandle(ref, () => internalRef.current);
326
- useLayoutEffect(() => {
327
- if (!equalBeforeAfter || !beforeContent || !afterContent) return;
328
- if (!internalRef.current) return;
329
- const el = internalRef.current;
330
- const beforeEl = el.firstElementChild;
331
- const afterEl = el.lastElementChild;
332
- if (beforeEl && afterEl && beforeEl !== afterEl) equalize(beforeEl, afterEl, direction === "rows" ? "height" : "width");
333
- });
334
- return /* @__PURE__ */ jsx(WrappedComponent, {
335
- ...rest,
336
- afterContent,
337
- beforeContent,
338
- ref: internalRef
339
- });
340
- };
341
- Enhanced.displayName = `withEqualSizeBeforeAfter(${displayName})`;
342
- return Enhanced;
343
- };
354
+ const name$3 = `${PKG_NAME}/Element`;
355
+ Component$4.displayName = name$3;
356
+ Component$4.pkgName = PKG_NAME;
357
+ Component$4.VITUS_LABS__COMPONENT = name$3;
344
358
 
345
359
  //#endregion
346
360
  //#region src/Element/index.ts
347
- var Element_default = Component$7;
361
+ var Element_default = Component$4;
348
362
 
349
363
  //#endregion
350
364
  //#region src/helpers/Iterator/component.tsx
@@ -398,7 +412,7 @@ const attachItemProps = ({ i, length }) => {
398
412
  position
399
413
  };
400
414
  };
401
- const Component$6 = (props) => {
415
+ const Component$3 = (props) => {
402
416
  const { itemKey, valueName, children, component, data, wrapComponent: Wrapper, wrapProps, itemProps } = props;
403
417
  const injectItemProps = useMemo(() => typeof itemProps === "function" ? itemProps : () => itemProps, [itemProps]);
404
418
  const injectWrapItemProps = useMemo(() => typeof wrapProps === "function" ? wrapProps : () => wrapProps, [wrapProps]);
@@ -499,12 +513,14 @@ const Component$6 = (props) => {
499
513
  };
500
514
  return renderItems();
501
515
  };
502
- Component$6.isIterator = true;
503
- Component$6.RESERVED_PROPS = RESERVED_PROPS;
516
+ var component_default = Object.assign(memo(Component$3), {
517
+ isIterator: true,
518
+ RESERVED_PROPS
519
+ });
504
520
 
505
521
  //#endregion
506
522
  //#region src/helpers/Iterator/index.ts
507
- var Iterator_default = Component$6;
523
+ var Iterator_default = component_default;
508
524
 
509
525
  //#endregion
510
526
  //#region src/List/component.tsx
@@ -515,7 +531,7 @@ var Iterator_default = Component$6;
515
531
  * is wrapped in an Element that receives all non-iterator props (e.g.,
516
532
  * layout, alignment, css), allowing the list to be styled as a single block.
517
533
  */
518
- const Component$5 = forwardRef(({ rootElement = false, ...props }, ref) => {
534
+ const Component$2 = forwardRef(({ rootElement = false, ...props }, ref) => {
519
535
  const renderedList = /* @__PURE__ */ jsx(Iterator_default, { ...pick(props, Iterator_default.RESERVED_PROPS) });
520
536
  if (!rootElement) return renderedList;
521
537
  return /* @__PURE__ */ jsx(Element_default, {
@@ -524,682 +540,14 @@ const Component$5 = forwardRef(({ rootElement = false, ...props }, ref) => {
524
540
  children: renderedList
525
541
  });
526
542
  });
527
- const name$4 = `${PKG_NAME}/List`;
528
- Component$5.displayName = name$4;
529
- Component$5.pkgName = PKG_NAME;
530
- Component$5.VITUS_LABS__COMPONENT = name$4;
531
-
532
- //#endregion
533
- //#region src/List/withActiveState.tsx
534
- /**
535
- * HOC that adds single or multi selection state management to a list component.
536
- * Tracks which items are active via a scalar key (single mode) or a Map of
537
- * key-to-boolean entries (multi mode). Injects `itemProps` callback that
538
- * provides each item with `active`, `handleItemActive`, `toggleItemActive`,
539
- * and other selection helpers. Supports `activeItemRequired` to prevent
540
- * deselecting the last active item.
541
- */
542
- const RESERVED_KEYS = [
543
- "type",
544
- "activeItems",
545
- "itemProps",
546
- "activeItemRequired"
547
- ];
548
- const component = (WrappedComponent) => {
549
- const displayName = WrappedComponent.displayName || WrappedComponent.name || "Component";
550
- const Enhanced = (props) => {
551
- const { type = "single", activeItemRequired, activeItems, itemProps = {}, ...rest } = props;
552
- const initActiveItems = () => {
553
- if (type === "single") {
554
- if (!Array.isArray(activeItems)) return activeItems;
555
- } else if (type === "multi") {
556
- const activeItemsHelper = Array.isArray(activeItems) ? activeItems : [activeItems];
557
- return new Map(activeItemsHelper.map((id) => [id, true]));
558
- }
559
- };
560
- const [innerActiveItems, setActiveItems] = useState(initActiveItems());
561
- const countActiveItems = (data) => {
562
- let result = 0;
563
- data.forEach((value) => {
564
- if (value) result += 1;
565
- });
566
- return result;
567
- };
568
- const updateItemState = (key) => {
569
- if (type === "single") setActiveItems((prevState) => {
570
- if (activeItemRequired) return key;
571
- if (prevState === key) return void 0;
572
- return key;
573
- });
574
- else if (type === "multi") setActiveItems((prevState) => {
575
- const activeItems = new Map(prevState);
576
- if (activeItemRequired && activeItems.get(key) && countActiveItems(activeItems) === 1) return activeItems;
577
- activeItems.set(key, !activeItems.get(key));
578
- return activeItems;
579
- });
580
- else setActiveItems(void 0);
581
- };
582
- const handleItemActive = (key) => {
583
- updateItemState(key);
584
- };
585
- const updateAllItemsState = (status) => {
586
- if (!status) setActiveItems(/* @__PURE__ */ new Map());
587
- };
588
- const setItemActive = (key) => {
589
- updateItemState(key);
590
- };
591
- const unsetItemActive = (key) => {
592
- updateItemState(key);
593
- };
594
- const toggleItemActive = (key) => {
595
- updateItemState(key);
596
- };
597
- const unsetAllItemsActive = () => {
598
- updateAllItemsState(false);
599
- };
600
- const isItemActive = (key) => {
601
- if (!innerActiveItems) return false;
602
- if (type === "single") return innerActiveItems === key;
603
- if (type === "multi" && innerActiveItems instanceof Map) return !!innerActiveItems.get(key);
604
- return false;
605
- };
606
- const attachMultipleProps = { unsetAllItemsActive };
607
- const attachItemProps = (props) => {
608
- const { key } = props;
609
- return {
610
- ...typeof itemProps === "object" ? itemProps : itemProps(props),
611
- active: isItemActive(key),
612
- handleItemActive: () => handleItemActive(key),
613
- setItemActive,
614
- unsetItemActive,
615
- toggleItemActive,
616
- ...type === "multi" ? attachMultipleProps : {}
617
- };
618
- };
619
- useEffect(() => {
620
- if (type === "single" && Array.isArray(activeItems)) {
621
- if (process.env.NODE_ENV !== "production") console.warn("[@vitus-labs/elements] List/withActiveState: `activeItems` was passed as an array but `type` is \"single\". In single selection mode, `activeItems` should be a single key (string | number). The array value will be ignored.");
622
- }
623
- }, [type, activeItems]);
624
- return /* @__PURE__ */ jsx(WrappedComponent, {
625
- ...rest,
626
- itemProps: attachItemProps
627
- });
628
- };
629
- Enhanced.RESERVED_KEYS = RESERVED_KEYS;
630
- Enhanced.displayName = `@vitus-labs/elements/List/withActiveState(${displayName})`;
631
- return Enhanced;
632
- };
543
+ const name$2 = `${PKG_NAME}/List`;
544
+ Component$2.displayName = name$2;
545
+ Component$2.pkgName = PKG_NAME;
546
+ Component$2.VITUS_LABS__COMPONENT = name$2;
633
547
 
634
548
  //#endregion
635
549
  //#region src/List/index.ts
636
- var List_default = Component$5;
637
-
638
- //#endregion
639
- //#region src/Portal/component.ts
640
- /**
641
- * Portal component that creates a new DOM element on mount, appends it to
642
- * the target location (defaults to document.body), and uses React's
643
- * createPortal to render children into it. The DOM element is cleaned up
644
- * on unmount. Accepts a custom DOMLocation for rendering into specific
645
- * containers (e.g., a modal root).
646
- */
647
- const Component$4 = ({ DOMLocation, tag = "div", children }) => {
648
- const [element, setElement] = useState();
649
- useEffect(() => {
650
- if (!tag) return void 0;
651
- const position = DOMLocation ?? document.body;
652
- const element = document.createElement(tag);
653
- setElement(element);
654
- position.appendChild(element);
655
- return () => {
656
- position.removeChild(element);
657
- };
658
- }, [tag, DOMLocation]);
659
- if (!tag || !element) return null;
660
- return createPortal(children, element);
661
- };
662
- const name$3 = `${PKG_NAME}/Portal`;
663
- Component$4.displayName = name$3;
664
- Component$4.pkgName = PKG_NAME;
665
- Component$4.VITUS_LABS__COMPONENT = name$3;
666
-
667
- //#endregion
668
- //#region src/Portal/index.ts
669
- var Portal_default = Component$4;
670
-
671
- //#endregion
672
- //#region src/Overlay/context.tsx
673
- /**
674
- * Context for nested overlay coordination. When a child overlay opens, it
675
- * sets the parent's blocked state to true, preventing the parent from
676
- * closing in response to click/hover events that belong to the child.
677
- */
678
- const context$1 = createContext({});
679
- const { Provider: Provider$1 } = context$1;
680
- const useOverlayContext = () => useContext(context$1);
681
- const Component = ({ children, blocked, setBlocked, setUnblocked }) => {
682
- return /* @__PURE__ */ jsx(Provider$1, {
683
- value: useMemo(() => ({
684
- blocked,
685
- setBlocked,
686
- setUnblocked
687
- }), [
688
- blocked,
689
- setBlocked,
690
- setUnblocked
691
- ]),
692
- children
693
- });
694
- };
695
-
696
- //#endregion
697
- //#region src/Overlay/useOverlay.tsx
698
- /**
699
- * Core hook powering the Overlay component. Manages open/close state, DOM
700
- * event listeners (click, hover, scroll, resize, ESC key), and dynamic
701
- * positioning of overlay content relative to its trigger. Supports dropdown,
702
- * tooltip, popover, and modal types with automatic edge-of-viewport flipping.
703
- * Event handlers are throttled for performance, and nested overlay blocking
704
- * is coordinated through the overlay context.
705
- */
706
- const sel = (cond, a, b) => cond ? a : b;
707
- const devWarn = (msg) => {
708
- if (!IS_DEVELOPMENT) return;
709
- console.warn(msg);
710
- };
711
- const calcDropdownVertical = (c, t, align, alignX, offsetX, offsetY) => {
712
- const pos = {};
713
- const topPos = t.top - offsetY - c.height;
714
- const bottomPos = t.bottom + offsetY;
715
- const leftPos = t.left + offsetX;
716
- const rightPos = t.right - offsetX - c.width;
717
- const fitsTop = topPos >= 0;
718
- const fitsBottom = bottomPos + c.height <= window.innerHeight;
719
- const fitsLeft = leftPos + c.width <= window.innerWidth;
720
- const fitsRight = rightPos >= 0;
721
- const useTop = sel(align === "top", fitsTop, !fitsBottom);
722
- pos.top = sel(useTop, topPos, bottomPos);
723
- const resolvedAlignY = sel(useTop, "top", "bottom");
724
- let resolvedAlignX = alignX;
725
- if (alignX === "left") {
726
- pos.left = sel(fitsLeft, leftPos, rightPos);
727
- resolvedAlignX = sel(fitsLeft, "left", "right");
728
- } else if (alignX === "right") {
729
- pos.left = sel(fitsRight, rightPos, leftPos);
730
- resolvedAlignX = sel(fitsRight, "right", "left");
731
- } else {
732
- const center = t.left + (t.right - t.left) / 2 - c.width / 2;
733
- const fitsCL = center >= 0;
734
- const fitsCR = center + c.width <= window.innerWidth;
735
- if (fitsCL && fitsCR) {
736
- resolvedAlignX = "center";
737
- pos.left = center;
738
- } else if (fitsCL) {
739
- resolvedAlignX = "left";
740
- pos.left = leftPos;
741
- } else if (fitsCR) {
742
- resolvedAlignX = "right";
743
- pos.left = rightPos;
744
- }
745
- }
746
- return {
747
- pos,
748
- resolvedAlignX,
749
- resolvedAlignY
750
- };
751
- };
752
- const calcDropdownHorizontal = (c, t, align, alignY, offsetX, offsetY) => {
753
- const pos = {};
754
- const leftPos = t.left - offsetX - c.width;
755
- const rightPos = t.right + offsetX;
756
- const topPos = t.top + offsetY;
757
- const bottomPos = t.bottom - offsetY - c.height;
758
- const fitsLeft = leftPos >= 0;
759
- const fitsRight = rightPos + c.width <= window.innerWidth;
760
- const fitsTop = topPos + c.height <= window.innerHeight;
761
- const fitsBottom = bottomPos >= 0;
762
- const useLeft = sel(align === "left", fitsLeft, !fitsRight);
763
- pos.left = sel(useLeft, leftPos, rightPos);
764
- const resolvedAlignX = sel(useLeft, "left", "right");
765
- let resolvedAlignY = alignY;
766
- if (alignY === "top") {
767
- pos.top = sel(fitsTop, topPos, bottomPos);
768
- resolvedAlignY = sel(fitsTop, "top", "bottom");
769
- } else if (alignY === "bottom") {
770
- pos.top = sel(fitsBottom, bottomPos, topPos);
771
- resolvedAlignY = sel(fitsBottom, "bottom", "top");
772
- } else {
773
- const center = t.top + (t.bottom - t.top) / 2 - c.height / 2;
774
- const fitsCT = center >= 0;
775
- const fitsCB = center + c.height <= window.innerHeight;
776
- if (fitsCT && fitsCB) {
777
- resolvedAlignY = "center";
778
- pos.top = center;
779
- } else if (fitsCT) {
780
- resolvedAlignY = "top";
781
- pos.top = topPos;
782
- } else if (fitsCB) {
783
- resolvedAlignY = "bottom";
784
- pos.top = bottomPos;
785
- }
786
- }
787
- return {
788
- pos,
789
- resolvedAlignX,
790
- resolvedAlignY
791
- };
792
- };
793
- const calcModalPos = (c, alignX, alignY, offsetX, offsetY) => {
794
- const pos = {};
795
- switch (alignX) {
796
- case "right":
797
- pos.right = offsetX;
798
- break;
799
- case "left":
800
- pos.left = offsetX;
801
- break;
802
- case "center":
803
- pos.left = window.innerWidth / 2 - c.width / 2;
804
- break;
805
- default: pos.right = offsetX;
806
- }
807
- switch (alignY) {
808
- case "top":
809
- pos.top = offsetY;
810
- break;
811
- case "center":
812
- pos.top = window.innerHeight / 2 - c.height / 2;
813
- break;
814
- case "bottom":
815
- pos.bottom = offsetY;
816
- break;
817
- default: pos.top = offsetY;
818
- }
819
- return pos;
820
- };
821
- const adjustForAncestor = (pos, ancestor) => {
822
- if (ancestor.top === 0 && ancestor.left === 0) return pos;
823
- const result = { ...pos };
824
- if (typeof result.top === "number") result.top -= ancestor.top;
825
- if (typeof result.bottom === "number") result.bottom += ancestor.top;
826
- if (typeof result.left === "number") result.left -= ancestor.left;
827
- if (typeof result.right === "number") result.right += ancestor.left;
828
- return result;
829
- };
830
- const computePosition = (type, align, alignX, alignY, offsetX, offsetY, triggerEl, contentEl, ancestorOffset) => {
831
- const isDropdown = [
832
- "dropdown",
833
- "tooltip",
834
- "popover"
835
- ].includes(type);
836
- if (isDropdown && (!triggerEl || !contentEl)) {
837
- devWarn(`[@vitus-labs/elements] Overlay (${type}): ${triggerEl ? "contentRef" : "triggerRef"} is not attached. Position cannot be calculated without both refs.`);
838
- return { pos: {} };
839
- }
840
- if (isDropdown && triggerEl && contentEl) {
841
- const c = contentEl.getBoundingClientRect();
842
- const t = triggerEl.getBoundingClientRect();
843
- const result = align === "top" || align === "bottom" ? calcDropdownVertical(c, t, align, alignX, offsetX, offsetY) : calcDropdownHorizontal(c, t, align, alignY, offsetX, offsetY);
844
- return {
845
- pos: adjustForAncestor(result.pos, ancestorOffset),
846
- resolvedAlignX: result.resolvedAlignX,
847
- resolvedAlignY: result.resolvedAlignY
848
- };
849
- }
850
- if (type === "modal") {
851
- if (!contentEl) {
852
- devWarn("[@vitus-labs/elements] Overlay (modal): contentRef is not attached. Modal position cannot be calculated without a content element.");
853
- return { pos: {} };
854
- }
855
- return { pos: adjustForAncestor(calcModalPos(contentEl.getBoundingClientRect(), alignX, alignY, offsetX, offsetY), ancestorOffset) };
856
- }
857
- return { pos: {} };
858
- };
859
- const processVisibilityEvent = (e, active, openOn, closeOn, isTrigger, isContent, showContent, hideContent) => {
860
- if (!active && openOn === "click" && e.type === "click" && isTrigger(e)) {
861
- showContent();
862
- return;
863
- }
864
- if (!active) return;
865
- if (closeOn === "hover" && e.type === "scroll") {
866
- hideContent();
867
- return;
868
- }
869
- if (e.type !== "click") return;
870
- if (closeOn === "click") hideContent();
871
- else if (closeOn === "clickOnTrigger" && isTrigger(e)) hideContent();
872
- else if (closeOn === "clickOutsideContent" && !isContent(e)) hideContent();
873
- };
874
- 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 } = {}) => {
875
- const { rootSize } = useContext(context);
876
- const ctx = useOverlayContext();
877
- const [isContentLoaded, setContentLoaded] = useState(false);
878
- const [innerAlignX, setInnerAlignX] = useState(alignX);
879
- const [innerAlignY, setInnerAlignY] = useState(alignY);
880
- const [blocked, handleBlocked] = useState(false);
881
- const [active, handleActive] = useState(isOpen);
882
- const triggerRef = useRef(null);
883
- const contentRef = useRef(null);
884
- const setBlocked = useCallback(() => handleBlocked(true), []);
885
- const setUnblocked = useCallback(() => handleBlocked(false), []);
886
- const showContent = useCallback(() => {
887
- handleActive(true);
888
- }, []);
889
- const hideContent = useCallback(() => {
890
- handleActive(false);
891
- }, []);
892
- const getAncestorOffset = useCallback(() => {
893
- if (position !== "absolute" || !contentRef.current) return {
894
- top: 0,
895
- left: 0
896
- };
897
- const offsetParent = contentRef.current.offsetParent;
898
- if (!offsetParent || offsetParent === document.body) return {
899
- top: 0,
900
- left: 0
901
- };
902
- const rect = offsetParent.getBoundingClientRect();
903
- return {
904
- top: rect.top,
905
- left: rect.left
906
- };
907
- }, [position]);
908
- const calculateContentPosition = useCallback(() => {
909
- if (!active || !isContentLoaded) return {};
910
- const result = computePosition(type, align, alignX, alignY, offsetX, offsetY, triggerRef.current, contentRef.current, getAncestorOffset());
911
- if (result.resolvedAlignX) setInnerAlignX(result.resolvedAlignX);
912
- if (result.resolvedAlignY) setInnerAlignY(result.resolvedAlignY);
913
- return result.pos;
914
- }, [
915
- isContentLoaded,
916
- active,
917
- align,
918
- alignX,
919
- alignY,
920
- offsetX,
921
- offsetY,
922
- type,
923
- getAncestorOffset
924
- ]);
925
- const assignContentPosition = useCallback((values = {}) => {
926
- if (!contentRef.current) return;
927
- const el = contentRef.current;
928
- const setValue = (param) => value(param, rootSize);
929
- el.style.position = position;
930
- el.style.top = values.top != null ? setValue(values.top) : "";
931
- el.style.bottom = values.bottom != null ? setValue(values.bottom) : "";
932
- el.style.left = values.left != null ? setValue(values.left) : "";
933
- el.style.right = values.right != null ? setValue(values.right) : "";
934
- }, [position, rootSize]);
935
- const setContentPosition = useCallback(() => {
936
- assignContentPosition(calculateContentPosition());
937
- }, [assignContentPosition, calculateContentPosition]);
938
- const isNodeOrChild = useCallback((ref) => (e) => {
939
- if (e?.target && ref.current) return ref.current.contains(e.target) || e.target === ref.current;
940
- return false;
941
- }, []);
942
- const handleVisibilityByEventType = useCallback((e) => {
943
- if (blocked || disabled) return;
944
- processVisibilityEvent(e, active, openOn, closeOn, isNodeOrChild(triggerRef), isNodeOrChild(contentRef), showContent, hideContent);
945
- }, [
946
- active,
947
- blocked,
948
- disabled,
949
- openOn,
950
- closeOn,
951
- hideContent,
952
- showContent,
953
- isNodeOrChild
954
- ]);
955
- const latestSetContentPosition = useRef(setContentPosition);
956
- latestSetContentPosition.current = setContentPosition;
957
- const latestHandleVisibility = useRef(handleVisibilityByEventType);
958
- latestHandleVisibility.current = handleVisibilityByEventType;
959
- const handleContentPosition = useMemo(() => throttle(() => latestSetContentPosition.current(), throttleDelay), [throttleDelay]);
960
- const handleClick = handleVisibilityByEventType;
961
- const handleVisibility = useMemo(() => throttle((e) => latestHandleVisibility.current(e), throttleDelay), [throttleDelay]);
962
- useEffect(() => {
963
- setInnerAlignX(alignX);
964
- setInnerAlignY(alignY);
965
- if (disabled) hideContent();
966
- }, [
967
- disabled,
968
- alignX,
969
- alignY,
970
- hideContent
971
- ]);
972
- useEffect(() => {
973
- if (!active || !isContentLoaded) return void 0;
974
- setContentPosition();
975
- const rafId = requestAnimationFrame(() => setContentPosition());
976
- return () => cancelAnimationFrame(rafId);
977
- }, [
978
- active,
979
- isContentLoaded,
980
- setContentPosition
981
- ]);
982
- const prevActiveRef = useRef(false);
983
- useEffect(() => {
984
- const wasActive = prevActiveRef.current;
985
- prevActiveRef.current = active;
986
- if (active && !wasActive) {
987
- onOpen?.();
988
- ctx.setBlocked?.();
989
- } else if (!active && wasActive) {
990
- setContentLoaded(false);
991
- onClose?.();
992
- ctx.setUnblocked?.();
993
- } else if (!active) setContentLoaded(false);
994
- return () => {
995
- if (active) {
996
- onClose?.();
997
- ctx.setUnblocked?.();
998
- }
999
- };
1000
- }, [
1001
- active,
1002
- ctx,
1003
- onClose,
1004
- onOpen
1005
- ]);
1006
- useEffect(() => {
1007
- if (!closeOnEsc || !active || blocked) return void 0;
1008
- const handleEscKey = (e) => {
1009
- if (e.key === "Escape") hideContent();
1010
- };
1011
- window.addEventListener("keydown", handleEscKey);
1012
- return () => {
1013
- window.removeEventListener("keydown", handleEscKey);
1014
- };
1015
- }, [
1016
- active,
1017
- blocked,
1018
- closeOnEsc,
1019
- hideContent
1020
- ]);
1021
- useEffect(() => {
1022
- if (!active) return void 0;
1023
- const shouldSetOverflow = type === "modal";
1024
- const onScroll = (e) => {
1025
- handleContentPosition();
1026
- handleVisibility(e);
1027
- };
1028
- if (shouldSetOverflow) document.body.style.overflow = "hidden";
1029
- window.addEventListener("resize", handleContentPosition);
1030
- window.addEventListener("scroll", onScroll, { passive: true });
1031
- return () => {
1032
- if (shouldSetOverflow) document.body.style.overflow = "";
1033
- window.removeEventListener("resize", handleContentPosition);
1034
- window.removeEventListener("scroll", onScroll);
1035
- };
1036
- }, [
1037
- active,
1038
- type,
1039
- handleVisibility,
1040
- handleContentPosition
1041
- ]);
1042
- useEffect(() => {
1043
- if (!active || !parentContainer) return void 0;
1044
- if (closeOn !== "hover") parentContainer.style.overflow = "hidden";
1045
- const onScroll = (e) => {
1046
- handleContentPosition();
1047
- handleVisibility(e);
1048
- };
1049
- parentContainer.addEventListener("scroll", onScroll, { passive: true });
1050
- return () => {
1051
- parentContainer.style.overflow = "";
1052
- parentContainer.removeEventListener("scroll", onScroll);
1053
- };
1054
- }, [
1055
- active,
1056
- parentContainer,
1057
- closeOn,
1058
- handleContentPosition,
1059
- handleVisibility
1060
- ]);
1061
- useEffect(() => {
1062
- if (blocked || disabled) return void 0;
1063
- if (openOn === "click" || [
1064
- "click",
1065
- "clickOnTrigger",
1066
- "clickOutsideContent"
1067
- ].includes(closeOn)) window.addEventListener("click", handleClick);
1068
- return () => {
1069
- window.removeEventListener("click", handleClick);
1070
- };
1071
- }, [
1072
- openOn,
1073
- closeOn,
1074
- blocked,
1075
- disabled,
1076
- handleClick
1077
- ]);
1078
- const hoverTimeoutRef = useRef(null);
1079
- useEffect(() => {
1080
- if (blocked || disabled || !(openOn === "hover" || closeOn === "hover")) return void 0;
1081
- const trigger = triggerRef.current;
1082
- const content = contentRef.current;
1083
- const clearHoverTimeout = () => {
1084
- if (hoverTimeoutRef.current != null) {
1085
- clearTimeout(hoverTimeoutRef.current);
1086
- hoverTimeoutRef.current = null;
1087
- }
1088
- };
1089
- const scheduleHide = () => {
1090
- clearHoverTimeout();
1091
- hoverTimeoutRef.current = setTimeout(hideContent, 100);
1092
- };
1093
- const onTriggerEnter = () => {
1094
- clearHoverTimeout();
1095
- if (openOn === "hover" && !active) showContent();
1096
- };
1097
- const onTriggerLeave = () => {
1098
- if (closeOn === "hover" && active) scheduleHide();
1099
- };
1100
- const onContentEnter = () => {
1101
- clearHoverTimeout();
1102
- };
1103
- const onContentLeave = () => {
1104
- if (closeOn === "hover" && active) scheduleHide();
1105
- };
1106
- if (trigger) {
1107
- trigger.addEventListener("mouseenter", onTriggerEnter);
1108
- trigger.addEventListener("mouseleave", onTriggerLeave);
1109
- }
1110
- if (content) {
1111
- content.addEventListener("mouseenter", onContentEnter);
1112
- content.addEventListener("mouseleave", onContentLeave);
1113
- }
1114
- return () => {
1115
- clearHoverTimeout();
1116
- if (trigger) {
1117
- trigger.removeEventListener("mouseenter", onTriggerEnter);
1118
- trigger.removeEventListener("mouseleave", onTriggerLeave);
1119
- }
1120
- if (content) {
1121
- content.removeEventListener("mouseenter", onContentEnter);
1122
- content.removeEventListener("mouseleave", onContentLeave);
1123
- }
1124
- };
1125
- }, [
1126
- active,
1127
- isContentLoaded,
1128
- blocked,
1129
- disabled,
1130
- openOn,
1131
- closeOn,
1132
- showContent,
1133
- hideContent
1134
- ]);
1135
- return {
1136
- triggerRef,
1137
- contentRef: useCallback((node) => {
1138
- if (node) {
1139
- contentRef.current = node;
1140
- setContentLoaded(true);
1141
- }
1142
- }, []),
1143
- active,
1144
- align,
1145
- alignX: innerAlignX,
1146
- alignY: innerAlignY,
1147
- showContent,
1148
- hideContent,
1149
- blocked,
1150
- setBlocked,
1151
- setUnblocked,
1152
- Provider: Component
1153
- };
1154
- };
1155
-
1156
- //#endregion
1157
- //#region src/Overlay/component.tsx
1158
- /**
1159
- * Overlay component that renders a trigger element and conditionally shows
1160
- * content via a Portal. The trigger receives a ref and optional show/hide
1161
- * callbacks; the content is positioned and managed by the useOverlay hook.
1162
- * A context Provider wraps the content to support nested overlays (e.g.,
1163
- * a dropdown inside another dropdown) via blocked-state propagation.
1164
- */
1165
- const IS_BROWSER = typeof window !== "undefined";
1166
- const Component$3 = ({ children, trigger, DOMLocation, triggerRefName = "ref", contentRefName = "ref", ...props }) => {
1167
- const { active, triggerRef, contentRef, showContent, hideContent, align, alignX, alignY, Provider, ...ctx } = useOverlay(props);
1168
- const { openOn, closeOn } = props;
1169
- const passHandlers = useMemo(() => openOn === "manual" || closeOn === "manual" || closeOn === "clickOutsideContent", [openOn, closeOn]);
1170
- return /* @__PURE__ */ jsxs(Fragment, { children: [render(trigger, {
1171
- [triggerRefName]: triggerRef,
1172
- active,
1173
- ...passHandlers ? {
1174
- showContent,
1175
- hideContent
1176
- } : {}
1177
- }), IS_BROWSER && active && /* @__PURE__ */ jsx(Portal_default, {
1178
- DOMLocation,
1179
- children: /* @__PURE__ */ jsx(Provider, {
1180
- ...ctx,
1181
- children: render(children, {
1182
- [contentRefName]: contentRef,
1183
- active,
1184
- align,
1185
- alignX,
1186
- alignY,
1187
- ...passHandlers ? {
1188
- showContent,
1189
- hideContent
1190
- } : {}
1191
- })
1192
- })
1193
- })] });
1194
- };
1195
- const name$2 = `${PKG_NAME}/Overlay`;
1196
- Component$3.displayName = name$2;
1197
- Component$3.pkgName = PKG_NAME;
1198
- Component$3.VITUS_LABS__COMPONENT = name$2;
1199
-
1200
- //#endregion
1201
- //#region src/Overlay/index.ts
1202
- var Overlay_default = Component$3;
550
+ var List_default = Component$2;
1203
551
 
1204
552
  //#endregion
1205
553
  //#region src/Text/styled.ts
@@ -1214,9 +562,7 @@ const styles = ({ css, theme: t }) => css`
1214
562
  ${t.extraStyles && extendCss(t.extraStyles)};
1215
563
  `;
1216
564
  var styled_default = styled(textComponent)`
1217
- color: inherit;
1218
- font-weight: inherit;
1219
- line-height: 1;
565
+ ${false};
1220
566
 
1221
567
  ${makeItResponsive({
1222
568
  key: "$text",
@@ -1228,7 +574,7 @@ var styled_default = styled(textComponent)`
1228
574
 
1229
575
  //#endregion
1230
576
  //#region src/Text/component.tsx
1231
- const Component$2 = forwardRef(({ paragraph, label, children, tag, css, ...props }, ref) => {
577
+ const Component$1 = forwardRef(({ paragraph, label, children, tag, css, ...props }, ref) => {
1232
578
  const renderContent = (as = void 0) => /* @__PURE__ */ jsx(styled_default, {
1233
579
  ref,
1234
580
  as,
@@ -1240,14 +586,14 @@ const Component$2 = forwardRef(({ paragraph, label, children, tag, css, ...props
1240
586
  return renderContent(finalTag);
1241
587
  });
1242
588
  const name$1 = `${PKG_NAME}/Text`;
1243
- Component$2.displayName = name$1;
1244
- Component$2.pkgName = PKG_NAME;
1245
- Component$2.VITUS_LABS__COMPONENT = name$1;
1246
- Component$2.isText = true;
589
+ Component$1.displayName = name$1;
590
+ Component$1.pkgName = PKG_NAME;
591
+ Component$1.VITUS_LABS__COMPONENT = name$1;
592
+ Component$1.isText = true;
1247
593
 
1248
594
  //#endregion
1249
595
  //#region src/Text/index.ts
1250
- var Text_default = Component$2;
596
+ var Text_default = Component$1;
1251
597
 
1252
598
  //#endregion
1253
599
  //#region src/Util/component.tsx
@@ -1256,7 +602,7 @@ var Text_default = Component$2;
1256
602
  * children without adding any DOM nodes of its own. Uses the core `render`
1257
603
  * helper to clone children with the merged props.
1258
604
  */
1259
- const Component$1 = ({ children, className, style }) => {
605
+ const Component = ({ children, className, style }) => {
1260
606
  const mergedClasses = useMemo(() => Array.isArray(className) ? className.join(" ") : className, [className]);
1261
607
  const finalProps = {};
1262
608
  if (style) finalProps.style = style;
@@ -1264,14 +610,14 @@ const Component$1 = ({ children, className, style }) => {
1264
610
  return render(children, finalProps);
1265
611
  };
1266
612
  const name = `${PKG_NAME}/Util`;
1267
- Component$1.displayName = name;
1268
- Component$1.pkgName = PKG_NAME;
1269
- Component$1.VITUS_LABS__COMPONENT = name;
613
+ Component.displayName = name;
614
+ Component.pkgName = PKG_NAME;
615
+ Component.VITUS_LABS__COMPONENT = name;
1270
616
 
1271
617
  //#endregion
1272
618
  //#region src/Util/index.ts
1273
- var Util_default = Component$1;
619
+ var Util_default = Component;
1274
620
 
1275
621
  //#endregion
1276
- export { Element_default as Element, List_default as List, Overlay_default as Overlay, Component as OverlayProvider, Portal_default as Portal, Provider, Text_default as Text, Util_default as Util, useOverlay, component as withActiveState, withEqualBeforeAfter as withEqualSizeBeforeAfter };
622
+ export { Element_default as Element, List_default as List, Provider, Text_default as Text, Util_default as Util };
1277
623
  //# sourceMappingURL=vitus-labs-elements.native.js.map