@zuzjs/ui 0.8.9 → 0.9.1

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.
Files changed (46) hide show
  1. package/dist/cjs/comps/Box/index.js +5 -16
  2. package/dist/cjs/comps/Form/index.js +7 -2
  3. package/dist/cjs/comps/Password/index.js +1 -1
  4. package/dist/cjs/comps/ProgressBar/index.js +0 -1
  5. package/dist/cjs/comps/Segmented/index.d.ts +2 -2
  6. package/dist/cjs/comps/Segmented/index.js +21 -7
  7. package/dist/cjs/comps/Segmented/types.d.ts +3 -0
  8. package/dist/cjs/comps/Table/index.d.ts +2 -2
  9. package/dist/cjs/comps/Table/index.js +27 -6
  10. package/dist/cjs/comps/Table/types.d.ts +5 -1
  11. package/dist/cjs/funs/css.js +0 -7
  12. package/dist/cjs/hooks/index.d.ts +1 -0
  13. package/dist/cjs/hooks/index.js +1 -0
  14. package/dist/cjs/hooks/useBase.d.ts +2 -2
  15. package/dist/cjs/hooks/useBase.js +37 -20
  16. package/dist/cjs/hooks/useScrollPhysics.d.ts +22 -0
  17. package/dist/cjs/hooks/useScrollPhysics.js +69 -0
  18. package/dist/cjs/types/enums.d.ts +2 -1
  19. package/dist/cjs/types/enums.js +1 -0
  20. package/dist/cjs/types/index.d.ts +1 -0
  21. package/dist/cjs/types/interfaces.d.ts +9 -0
  22. package/dist/css/styles.css +1 -1
  23. package/dist/esm/comps/Box/index.js +5 -16
  24. package/dist/esm/comps/Form/index.js +7 -2
  25. package/dist/esm/comps/Password/index.js +1 -1
  26. package/dist/esm/comps/ProgressBar/index.js +0 -1
  27. package/dist/esm/comps/Segmented/index.d.ts +2 -2
  28. package/dist/esm/comps/Segmented/index.js +21 -7
  29. package/dist/esm/comps/Segmented/types.d.ts +3 -0
  30. package/dist/esm/comps/Table/index.d.ts +2 -2
  31. package/dist/esm/comps/Table/index.js +27 -6
  32. package/dist/esm/comps/Table/types.d.ts +5 -1
  33. package/dist/esm/funs/css.js +0 -7
  34. package/dist/esm/hooks/index.d.ts +1 -0
  35. package/dist/esm/hooks/index.js +1 -0
  36. package/dist/esm/hooks/useBase.d.ts +2 -2
  37. package/dist/esm/hooks/useBase.js +37 -20
  38. package/dist/esm/hooks/useScrollPhysics.d.ts +22 -0
  39. package/dist/esm/hooks/useScrollPhysics.js +69 -0
  40. package/dist/esm/types/enums.d.ts +2 -1
  41. package/dist/esm/types/enums.js +1 -0
  42. package/dist/esm/types/index.d.ts +1 -0
  43. package/dist/esm/types/interfaces.d.ts +9 -0
  44. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  45. package/dist/tsconfig.tsbuildinfo +1 -1
  46. package/package.json +2 -2
@@ -1,23 +1,12 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { forwardRef } from "react";
2
+ import { forwardRef, useMemo, useRef } from "react";
3
3
  import { useBase } from "../../hooks";
4
4
  const Box = forwardRef((props, ref) => {
5
5
  const { style, withEditor, ...pops } = props;
6
- const { style: _style, className, rest } = useBase(pops);
7
- // const handleInternalClick = (e: React.MouseEvent<HTMLDivElement>) => {
8
- // // if ( withEditor && isBrowser ) {
9
- // // // window.dispatchEvent(new CustomEvent(`ZUZ_COMP_SELECTED`, {
10
- // // // detail: {
11
- // // // compName: 'Box',
12
- // // // target: e.target,
13
- // // // props
14
- // // // }
15
- // // // }))
16
- // // }
17
- // }
18
- return _jsx("div", { ref: ref,
19
- // onClick={handleInternalClick}
20
- className: `${className} ${withEditor ? `--with-zuz-editor` : ``}`.trim(), style: {
6
+ const innerRef = useRef(null);
7
+ const targetRef = useMemo(() => ref && typeof ref !== "function" && ref.current ? ref : innerRef, [ref]);
8
+ const { style: _style, className, rest } = useBase(pops, targetRef);
9
+ return _jsx("div", { ref: ref || innerRef, className: `${className} ${withEditor ? `--with-zuz-editor` : ``}`.trim(), style: {
21
10
  ..._style,
22
11
  ...(style || {})
23
12
  }, ...rest });
@@ -92,8 +92,13 @@ const Form = forwardRef((props, ref) => {
92
92
  case FORMVALIDATION.Email:
93
93
  return isEmail(el.value);
94
94
  case FORMVALIDATION.Uri:
95
- console.log(`Add FORMVALIDATION.Uri`);
96
- return false;
95
+ try {
96
+ new URL(el.value);
97
+ return true;
98
+ }
99
+ catch (e) {
100
+ return false;
101
+ }
97
102
  case FORMVALIDATION.Password:
98
103
  console.log(`Add FORMVALIDATION.Password`);
99
104
  return false;
@@ -23,7 +23,7 @@ const Password = forwardRef((props, ref) => {
23
23
  onChange && onChange(ev);
24
24
  }, 100);
25
25
  useEffect(() => { }, []);
26
- return _jsxs(Box, { as: `w:100% flex cols`, children: [_jsxs(Box, { style: style, className: `--password flex aic rel`, children: [_jsx(Input, { ref: ref, type: visible ? 'text' : 'password', className: className, onChange: debounce, ...rest }), _jsx(Button, { tabIndex: -1, onClick: () => setVisible(prev => !prev), className: `--toggle flex aic jcc abs`, children: visible ? SVGIcons.eye : SVGIcons.eyeSlash })] }), strenthMeter && _jsxs(Box, { as: `flex aic --password-meter --pb-score-${strenth.score}`, children: [_jsxs(Box, { as: `--password-bars flex aic`, children: [_jsx(Box, { as: `--pbar ${strenth.score >= 1 ? `--pb-on` : ``}`.trim() }), _jsx(Box, { as: `--pbar ${strenth.score >= 2 ? `--pb-on` : ``}`.trim() }), _jsx(Box, { as: `--pbar ${strenth.score >= 3 ? `--pb-on` : ``}`.trim() }), _jsx(Box, { as: `--pbar ${strenth.score >= 4 ? `--pb-on` : ``}`.trim() }), _jsx(Box, { as: `--pbar ${strenth.score >= 5 ? `--pb-on` : ``}`.trim() })] }), _jsx(Text, { as: `--password-meter-label`, "aria-hidden": true, children: strenth.result })] })] });
26
+ return _jsxs(Box, { as: `w:100% flex cols`, children: [_jsxs(Box, { style: style, className: `--password flex aic rel`, children: [_jsx(Input, { ref: ref, type: visible ? 'text' : 'password', className: className, onChange: debounce, ...rest }), _jsx(Button, { tabIndex: -1, onClick: () => setVisible(prev => !prev), className: `--toggle flex aic jcc abs`, children: visible ? SVGIcons.eye : SVGIcons.eyeSlash })] }), strenthMeter && _jsxs(Box, { as: `flex aic --password-meter --pb-score-${strenth.score}`, style: style, children: [_jsxs(Box, { as: `--password-bars flex aic`, children: [_jsx(Box, { as: `--pbar ${strenth.score >= 1 ? `--pb-on` : ``}`.trim() }), _jsx(Box, { as: `--pbar ${strenth.score >= 2 ? `--pb-on` : ``}`.trim() }), _jsx(Box, { as: `--pbar ${strenth.score >= 3 ? `--pb-on` : ``}`.trim() }), _jsx(Box, { as: `--pbar ${strenth.score >= 4 ? `--pb-on` : ``}`.trim() }), _jsx(Box, { as: `--pbar ${strenth.score >= 5 ? `--pb-on` : ``}`.trim() })] }), _jsx(Text, { as: `--password-meter-label`, "aria-hidden": true, children: strenth.result })] })] });
27
27
  });
28
28
  Password.displayName = `Zuz.Password`;
29
29
  export default Password;
@@ -12,7 +12,6 @@ const ProgressBar = forwardRef((props, ref) => {
12
12
  }
13
13
  }), []);
14
14
  useEffect(() => {
15
- console.log(`progressChanged`, progress);
16
15
  if (progress && bar.current) {
17
16
  if (animated) {
18
17
  setTimeout(() => bar.current.style.width = `${progress * 100}%`, 500);
@@ -1,6 +1,6 @@
1
1
  import { Variant } from "../../types/enums";
2
2
  import { BoxProps } from "../Box";
3
- import { Segment } from "./types";
3
+ import { Segment, SegmentController } from "./types";
4
4
  /**
5
5
  * `SelectTabs` component is a segmented control that allows switching between segments.
6
6
  *
@@ -25,5 +25,5 @@ declare const Segmented: import("react").ForwardRefExoticComponent<BoxProps & {
25
25
  selected?: number;
26
26
  onSwitch?: (segment: Segment) => void;
27
27
  items: Segment[];
28
- } & import("react").RefAttributes<HTMLDivElement>>;
28
+ } & import("react").RefAttributes<SegmentController>>;
29
29
  export default Segmented;
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { forwardRef, useEffect, useRef, useState } from "react";
3
+ import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
4
4
  import { useBase } from "../../hooks";
5
5
  import { Variant } from "../../types/enums";
6
6
  import Box from "../Box";
@@ -26,6 +26,8 @@ import SegmentItem from "./item";
26
26
  const Segmented = forwardRef((props, ref) => {
27
27
  const { animate, fx, items, selected, size, variant, onSwitch, ...pops } = props;
28
28
  const [_selected, setSelected] = useState(selected || 0);
29
+ const hasMounted = useRef(false);
30
+ const prevSelectedRef = useRef(_selected);
29
31
  const { className, style, rest } = useBase(pops);
30
32
  const _tab = useRef(null);
31
33
  const _segmented = useRef(null);
@@ -40,8 +42,9 @@ const Segmented = forwardRef((props, ref) => {
40
42
  // console.log(selected, _selected, index, mounted)
41
43
  if (force || (_selected != index && _selected != -2)) {
42
44
  setSelected(index);
43
- if (onSwitch)
44
- onSwitch(meta);
45
+ prevSelectedRef.current = _selected;
46
+ if (hasMounted.current)
47
+ onSwitch?.(meta);
45
48
  }
46
49
  if (_tab.current) {
47
50
  const _sp = _segmented.current?.getBoundingClientRect();
@@ -49,11 +52,22 @@ const Segmented = forwardRef((props, ref) => {
49
52
  _tab.current.style.setProperty(`--x`, `${_sp ? x - _sp.left : x}px`);
50
53
  }
51
54
  };
52
- useEffect(() => {
53
- if (selected !== undefined && selected != _selected) {
54
- setSelected(selected);
55
+ useImperativeHandle(ref, () => ({
56
+ setSelected: (index) => {
57
+ if (typeof index === `number` && index != _selected) {
58
+ setSelected(index);
59
+ }
55
60
  }
56
- }, [selected, _selected]);
61
+ }), [_selected]);
62
+ // useEffect(() => {
63
+ // if (typeof selected === `number` && selected != _selected) {
64
+ // prevSelectedRef.current = _selected;
65
+ // setSelected(selected);
66
+ // }
67
+ // }, [selected, _selected]);
68
+ useEffect(() => {
69
+ hasMounted.current = true;
70
+ }, []);
57
71
  return _jsxs(Box, { ref: _segmented, "data-selected": _selected, className: `${className} --segmented --${variant || Variant.Small} flex aic rel`, style: style, ...rest, children: [_jsx(Box, { ref: _tab, className: `--segment-tab abs` }), items.map((item, i) => _jsx(SegmentItem, { onSelect: handleSelect, selected: _selected == i, meta: {
58
72
  ...item,
59
73
  index: i
@@ -36,3 +36,6 @@ export type SegmentItemProps = {
36
36
  selected: boolean;
37
37
  onSelect: (index: number, width: number, x: number, segment: Segment, force: boolean) => void;
38
38
  };
39
+ export interface SegmentController {
40
+ setSelected: (index: number) => void;
41
+ }
@@ -1,6 +1,6 @@
1
1
  import { JSX, Ref } from "react";
2
- import type { TableProps } from "./types";
2
+ import type { TableController, TableProps } from "./types";
3
3
  declare const ForwardedTable: <T>(props: TableProps<T> & {
4
- ref?: Ref<HTMLDivElement>;
4
+ ref?: Ref<TableController>;
5
5
  }) => JSX.Element;
6
6
  export default ForwardedTable;
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { forwardRef, useEffect, useMemo, useRef, useState } from "react";
3
+ import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
4
4
  import { PubSub, SPINNER, Spinner, Text, TRANSITION_CURVES, TRANSITIONS, uuid } from "../..";
5
5
  import { useBase } from "../../hooks";
6
6
  import Box from "../Box";
@@ -63,7 +63,7 @@ import TRow from "./row";
63
63
  * ```
64
64
  */
65
65
  const Table = (props, ref) => {
66
- const { schema, rows, rowCount, rowsPerPage, currentPage, pagination, paginationHash, showPaginationOnZeroPageCount, animateRows, header, rowClassName, selectableRows, hoverable, sortBy, loading, loadingRowCount, loadingMessage, spinner, onSort, onRowSelectToggle, onPageChange, onRowContextMenu, ...pops } = props;
66
+ const { schema, rows, rowCount, rowsPerPage, currentPage, pagination, paginationHash, showPaginationOnZeroPageCount, animateRows, header, rowClassName, selectableRows, hoverable, sortBy, loading: _loading, loadingRowCount, loadingMessage, spinner, emptyMessage, onSort, onRowSelectToggle, onPageChange, onRowContextMenu, ...pops } = props;
67
67
  const _pagination = useRef(null);
68
68
  const _schemaParsed = useMemo(() => schema.reduce((prev, c) => {
69
69
  prev[c.id] = {
@@ -85,22 +85,43 @@ const Table = (props, ref) => {
85
85
  const pubsub = useMemo(() => new PubSub(), []);
86
86
  const rowKeys = useRef(rows.map(() => uuid()));
87
87
  const [_sortBy, setSortBy] = useState(sortBy || null);
88
- // const placeholderRows = useMemo(() => {
89
- // }, [loading, loadingRowCount])
88
+ // const [ _loading, _setLoading ] = useState(loading || false)
89
+ // const _loading = useRef<boolean>(loading || false)
90
+ const [_loadingMessage, _setLoadingMessage] = useState(loadingMessage || `loading`);
91
+ const isEmpty = useMemo(() => !_loading && rows && rows.length == 0, [_loading, rows]);
92
+ const renderEmpty = useCallback(() => {
93
+ if (emptyMessage) {
94
+ if (typeof emptyMessage == `function`) {
95
+ const Empty = emptyMessage;
96
+ return _jsx(Empty, {});
97
+ }
98
+ return emptyMessage;
99
+ }
100
+ return _jsx(Text, { as: `tac s:18 mt:75`, children: "No Record Found" });
101
+ }, [_loading, rows]);
90
102
  useEffect(() => {
91
103
  rowKeys.current = rows.map(() => uuid()); // Update only when rows change
92
104
  }, [rows]);
105
+ // useEffect(() => {
106
+ // if ( loading != undefined && loading != _loading ) _setLoading(loading)
107
+ // }, [loading])
93
108
  const handleSort = (col, dir) => {
94
109
  setSortBy(col);
95
110
  onSort?.(col, dir);
96
111
  };
97
112
  const possiblePage = (rowCount || (rows ? rows.length : 0)) / (rowsPerPage || 10);
98
113
  const _paginated = useMemo(() => (showPaginationOnZeroPageCount || (possiblePage > 1)) ? _jsx(Pagination, { hash: paginationHash, ref: _pagination, renderOnZeroPageCount: showPaginationOnZeroPageCount, onPageChange: onPageChange, paginationStyle: PaginationStyle.Table, startPage: currentPage, itemCount: rowCount || (rows ? rows.length : 0), itemsPerPage: rowsPerPage || 10 }) : null, [currentPage, rowCount]);
99
- return _jsxs(Box, { as: `--table ${(hoverable ?? true) ? `--hoverable` : ``} flex cols rel ${className}`, ref: _tableRef, children: [_header == true && _jsx(TRow, { sortBy: _sortBy, onSort: handleSort, tableRef: _tableRef, pubsub: pubsub, selectable: selectableRows, index: -1, schema: schema, loading: true, styles: _schemaParsed }), loading && _jsxs(Box, { as: `abs center-x flex aic --table-spinner`, children: [_jsx(Spinner, { type: spinner || SPINNER.Simple }), loadingMessage && _jsx(Text, { as: `--table-loading-message`, children: loadingMessage })] }), loading && Array(loadingRowCount || 5).fill({}).map((row, index) => _jsx(TRow, { tableRef: _tableRef, index: index, pubsub: pubsub, schema: schema, styles: _schemaParsed, loading: true, animate: animateRows }, `--trow-loading-${index}-${schema[0].id}`)), !loading && rows && rows.map((row, index) => _jsx(TRow, { tableRef: _tableRef, pubsub: pubsub, loading: false, index: index, schema: schema, ids: _cols, styles: _schemaParsed, animate: animateRows, data: row, rowClassName: rowClassName, selectable: selectableRows, onSelect: onRowSelectToggle, onContextMenu: onRowContextMenu }, `--trow-${rowKeys.current[index] || index}-${schema[0].id}`)), _jsx(Box, { "aria-hidden": !pagination || !_paginated, ...(animateRows ? { fx: {
114
+ useImperativeHandle(ref, () => ({
115
+ setLoading(mod) {
116
+ // _setLoading(mod)
117
+ // _loading.current = mod
118
+ }
119
+ }));
120
+ return _jsxs(Box, { as: `--table ${(hoverable ?? true) ? `--hoverable` : ``} flex cols rel ${className}`, ref: _tableRef, children: [_header == true && _jsx(TRow, { sortBy: _sortBy, onSort: handleSort, tableRef: _tableRef, pubsub: pubsub, selectable: selectableRows, index: -1, schema: schema, loading: true, styles: _schemaParsed }), _loading && _jsxs(Box, { as: `abs center-x flex aic --table-spinner`, children: [_jsx(Spinner, { type: spinner || SPINNER.Simple }), _loadingMessage && _jsx(Text, { as: `--table-loading-message`, children: _loadingMessage })] }), _loading && Array(loadingRowCount || 5).fill({}).map((row, index) => _jsx(TRow, { tableRef: _tableRef, index: index, pubsub: pubsub, schema: schema, styles: _schemaParsed, loading: true, animate: animateRows }, `--trow-loading-${index}-${schema[0].id}`)), !_loading && rows && rows.map((row, index) => _jsx(TRow, { tableRef: _tableRef, pubsub: pubsub, loading: false, index: index, schema: schema, ids: _cols, styles: _schemaParsed, animate: animateRows, data: row, rowClassName: rowClassName, selectable: selectableRows, onSelect: onRowSelectToggle, onContextMenu: onRowContextMenu }, `--trow-${rowKeys.current[index] || index}-${schema[0].id}`)), isEmpty && renderEmpty(), _jsx(Box, { "aria-hidden": !pagination || !_paginated, ...(animateRows ? { fx: {
100
121
  transition: TRANSITIONS.SlideInBottom,
101
122
  curve: TRANSITION_CURVES.EaseInOut,
102
123
  delay: .02 * (rows.length + 1),
103
- when: !loading && rows && pagination && _paginated != null
124
+ when: !_loading && rows && pagination && _paginated != null
104
125
  } } : {}), as: `--row flex aic --row-footer`, children: pagination && _paginated ? _paginated : null })] });
105
126
  };
106
127
  Table.displayName = `Zuz.Table`;
@@ -1,8 +1,11 @@
1
- import { ReactNode, RefObject } from "react";
1
+ import { FC, ReactNode, RefObject } from "react";
2
2
  import { PubSub, SPINNER } from "../..";
3
3
  import { dynamicObject } from "../../types";
4
4
  import { BoxProps } from "../Box";
5
5
  import { PaginationCallback } from "../Pagination/types";
6
+ export interface TableController {
7
+ setLoading: (mod: boolean) => void;
8
+ }
6
9
  /**
7
10
  * Callback function for row selection.
8
11
  *
@@ -140,6 +143,7 @@ export type TableProps<T> = BoxProps & {
140
143
  loadingRowCount?: number;
141
144
  loadingMessage?: string;
142
145
  spinner?: SPINNER;
146
+ emptyMessage?: ReactNode | FC;
143
147
  onRowSelectToggle?: RowSelectCallback<T>;
144
148
  onRowContextMenu?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>, row: T) => void;
145
149
  onPageChange?: PaginationCallback;
@@ -899,12 +899,6 @@ export const buildWithStyles = (source) => {
899
899
  for (const _prop in source) {
900
900
  let prop = _prop;
901
901
  if (prop in cssWithKeys) {
902
- // console.log(
903
- // prop,
904
- // cssTransformKeys.includes(cssWithKeys[prop].toString()),
905
- // cssFilterKeys.includes(cssWithKeys[prop].toString()),
906
- // cssWithKeys[prop]
907
- // )
908
902
  if (cssTransformKeys.includes(cssWithKeys[prop].toString())) {
909
903
  _transform.push(`${cssWithKeys[prop]}(${source[prop]}${_css.makeUnit(prop, source[prop])})`);
910
904
  }
@@ -931,7 +925,6 @@ export const buildWithStyles = (source) => {
931
925
  if (_filter.length > 0) {
932
926
  _.filter = _filter.join(` `);
933
927
  }
934
- // console.log(_, _transform)
935
928
  }
936
929
  return _;
937
930
  };
@@ -22,6 +22,7 @@ export { default as useImage } from './useImage';
22
22
  export { default as useImageCropper } from './useImageCropper';
23
23
  export { default as useIntersectionObserver } from './useIntersectionObserver';
24
24
  export { default as useScrollbar } from './useScrollbar';
25
+ export { default as useScrollPhysics } from './useScrollPhysics';
25
26
  export { default as useSheet } from './useSheet';
26
27
  export { default as useShortcuts } from './useShortcuts';
27
28
  export { default as useNetworkStatus } from './useNetworkStatus';
@@ -23,6 +23,7 @@ export { default as useImage } from './useImage';
23
23
  export { default as useImageCropper } from './useImageCropper';
24
24
  export { default as useIntersectionObserver } from './useIntersectionObserver';
25
25
  export { default as useScrollbar } from './useScrollbar';
26
+ export { default as useScrollPhysics } from './useScrollPhysics';
26
27
  export { default as useSheet } from './useSheet';
27
28
  export { default as useShortcuts } from './useShortcuts';
28
29
  export { default as useNetworkStatus } from './useNetworkStatus';
@@ -1,6 +1,6 @@
1
- import { ComponentPropsWithRef, CSSProperties, JSX } from "react";
1
+ import { ComponentPropsWithRef, CSSProperties, JSX, RefObject } from "react";
2
2
  import { Props } from "../types";
3
- declare const useBase: <T extends keyof JSX.IntrinsicElements>(props: Props<T>) => {
3
+ declare const useBase: <T extends keyof JSX.IntrinsicElements>(props: Props<T>, ref?: RefObject<HTMLElement>) => {
4
4
  style: CSSProperties;
5
5
  className: string;
6
6
  rest: ComponentPropsWithRef<T>;
@@ -1,3 +1,5 @@
1
+ import { animateCSSVar } from "@zuzjs/core";
2
+ import { useEffect, useRef } from "react";
1
3
  import { cleanProps, css } from "../funs";
2
4
  import { buildWithStyles, getAnimationCurve, getAnimationTransition } from "../funs/css";
3
5
  import { cssFilterKeys, cssProps, cssTransformKeys, cssWithKeys } from "../funs/stylesheet";
@@ -28,13 +30,15 @@ const buildSkeletonStyle = (s) => {
28
30
  }
29
31
  return style;
30
32
  };
31
- const useBase = (props) => {
32
- const { as, fx, animate, transition: autoTransition, skeleton, className, shimmer, propsToRemove, draggable, dragOptions, ...rest } = props || {};
33
+ const useBase = (props, ref) => {
34
+ const { as, fx, animate, timeline, transition: autoTransition, skeleton, className, shimmer, propsToRemove, draggable, dragOptions, ...rest } = props || {};
35
+ const currentScroll = useRef({ x: 0, y: 0 });
36
+ const lastTime = useRef(performance.now());
33
37
  let cx = [];
34
38
  if (as) {
35
39
  cx = css().Build(`string` == typeof as ? as : as.join(` `)).cx;
36
40
  }
37
- const { transition, from, to, exit, when, duration, delay, curve } = autoTransition ? {
41
+ const { transition, from, to, exit, when, duration, delay, curve, scroll } = autoTransition ? {
38
42
  transition: autoTransition,
39
43
  duration: 0.3
40
44
  } : fx || animate || {};
@@ -48,23 +52,6 @@ const useBase = (props) => {
48
52
  else {
49
53
  _style = transition ? getAnimationTransition(transition, false, true) : from || {};
50
54
  }
51
- // Track previous value of 'when'
52
- // const prevWhenRef = useRef<boolean | undefined>(when);
53
- // if (undefined === when) {
54
- // _style = transition ? getAnimationTransition(transition, true) : { ...from, ...to };
55
- // } else if (true === when) {
56
- // _style = transition ? getAnimationTransition(transition, false) : { ...(to || {}) };
57
- // } else {
58
- // // Check if exit style should be applied
59
- // const wasTrue = prevWhenRef.current === true;
60
- // if (wasTrue && exit) {
61
- // _style = { ...(exit || {}) };
62
- // } else {
63
- // _style = transition ? getAnimationTransition(transition, false, true) : from || {};
64
- // }
65
- // }
66
- // // Update previous value for next render
67
- // prevWhenRef.current = when;
68
55
  const _transition = {};
69
56
  if (transition || (from && to)) {
70
57
  // { transition: `all ${duration || `0.2`}s ${getAnimationCurve(curve)} ${delay || 0}s` }
@@ -111,6 +98,36 @@ const useBase = (props) => {
111
98
  };
112
99
  }
113
100
  }
101
+ const handleScroll = () => {
102
+ if (fx && fx.scroll && typeof window !== 'undefined') {
103
+ const now = performance.now();
104
+ const dt = (now - lastTime.current) / 1000;
105
+ lastTime.current = now;
106
+ const { lerpFactor, x, y, multiplier, xMultiplier, yMultiplier } = fx.scroll;
107
+ const delta = window.scrollY - currentScroll.current.y;
108
+ const velocity = delta / dt;
109
+ currentScroll.current.y += delta * (lerpFactor || .1);
110
+ if (ref?.current) {
111
+ const translateX = x ? currentScroll.current.x * x * (multiplier || xMultiplier || .25) : 0;
112
+ const translateY = y ? currentScroll.current.y * y * (multiplier || yMultiplier || .25) : 0;
113
+ // ref.current.style.setProperty(`--scroll-y`, `${translateY}px`)
114
+ // transform = `translate3d(${translateX}px, ${translateY}px, 0)`.trim()
115
+ animateCSSVar(ref, "--scroll-y", translateY);
116
+ }
117
+ }
118
+ };
119
+ useEffect(() => {
120
+ if (fx && fx.scroll && typeof window !== 'undefined') {
121
+ if (ref) {
122
+ ref.current.style.transform = `translate3d(0px, var(--scroll-y), 0)`;
123
+ ref.current.style.transition = `transform 0.1s ${fx.curve ? getAnimationCurve(fx.curve) : `var(--spring)`}`;
124
+ }
125
+ window.addEventListener('scroll', handleScroll, { passive: true });
126
+ return () => {
127
+ window.removeEventListener('scroll', handleScroll);
128
+ };
129
+ }
130
+ }, [ref]);
114
131
  return {
115
132
  style: {
116
133
  ...buildWithStyles(_style),
@@ -0,0 +1,22 @@
1
+ import { RefObject } from 'react';
2
+ type ScrollPhysicsOptions = {
3
+ lerpFactor?: number;
4
+ x?: number;
5
+ y?: number;
6
+ xMultiplier?: number;
7
+ yMultiplier?: number;
8
+ scale?: {
9
+ min: number;
10
+ max: number;
11
+ factor: number;
12
+ };
13
+ rotate?: {
14
+ direction?: 1 | -1;
15
+ multiplier?: number;
16
+ };
17
+ };
18
+ declare const useScrollPhysics: (ref: RefObject<HTMLElement>, options: ScrollPhysicsOptions) => {
19
+ position: RefObject<number>;
20
+ velocity: RefObject<number>;
21
+ };
22
+ export default useScrollPhysics;
@@ -0,0 +1,69 @@
1
+ import { clamp } from '@zuzjs/core';
2
+ import { useEffect, useRef } from 'react';
3
+ const useScrollPhysics = (ref, options) => {
4
+ const { lerpFactor = 0.1, x, y, xMultiplier = 0.25, yMultiplier = 0.25, scale, rotate, } = options;
5
+ const position = useRef(0);
6
+ const velocity = useRef(0);
7
+ const current = useRef(0);
8
+ const target = useRef(0);
9
+ const lastTime = useRef(performance.now());
10
+ const raf = useRef(null);
11
+ const isRunning = useRef(false);
12
+ const smoothedVelocity = useRef(0);
13
+ const threshold = 0.2; // minimum delta to trigger animation
14
+ useEffect(() => {
15
+ if (typeof window === 'undefined')
16
+ return;
17
+ const tick = () => {
18
+ const now = performance.now();
19
+ const dt = (now - lastTime.current) / 1000;
20
+ lastTime.current = now;
21
+ const delta = target.current - current.current;
22
+ const v = delta / dt;
23
+ // Update position & velocity
24
+ current.current += delta * lerpFactor;
25
+ position.current = current.current;
26
+ velocity.current = v;
27
+ smoothedVelocity.current += (v - smoothedVelocity.current) * lerpFactor;
28
+ // Apply transform
29
+ if (ref.current) {
30
+ const translateX = x ? position.current * x * xMultiplier : 0;
31
+ const translateY = y ? position.current * y * yMultiplier : 0;
32
+ const scaleValue = scale
33
+ ? clamp(scale.max - Math.abs(smoothedVelocity.current) * scale.factor, scale.min, scale.max) : 1;
34
+ const rotateValue = rotate
35
+ ? velocity.current * (rotate.multiplier ?? 1) * (rotate.direction ?? 1)
36
+ : 0;
37
+ ref.current.style.transform = `
38
+ translate3d(${translateX}px, ${translateY}px, 0)
39
+ scale(${scaleValue})
40
+ rotate(${rotateValue}deg)
41
+ `.trim();
42
+ }
43
+ // If still moving, continue animating
44
+ if (Math.abs(delta) > threshold || Math.abs(v) > threshold) {
45
+ raf.current = requestAnimationFrame(tick);
46
+ }
47
+ else {
48
+ isRunning.current = false;
49
+ raf.current = null;
50
+ }
51
+ };
52
+ const handleScroll = () => {
53
+ target.current = window.scrollY;
54
+ if (!isRunning.current) {
55
+ lastTime.current = performance.now();
56
+ raf.current = requestAnimationFrame(tick);
57
+ isRunning.current = true;
58
+ }
59
+ };
60
+ window.addEventListener('scroll', handleScroll, { passive: true });
61
+ return () => {
62
+ window.removeEventListener('scroll', handleScroll);
63
+ if (raf.current)
64
+ cancelAnimationFrame(raf.current);
65
+ };
66
+ }, [lerpFactor, x, y, xMultiplier, yMultiplier, scale, rotate]);
67
+ return { position, velocity };
68
+ };
69
+ export default useScrollPhysics;
@@ -18,7 +18,8 @@ export declare enum FORMVALIDATION {
18
18
  Uri = "URI",
19
19
  Password = "PASSWORD",
20
20
  MatchField = "MATCHFIELD",
21
- Pattern = "*"
21
+ Pattern = "*",
22
+ GreaterThan = "GREATER_THAN"
22
23
  }
23
24
  export declare enum SHEET {
24
25
  Dialog = "DIALOG",
@@ -24,6 +24,7 @@ export var FORMVALIDATION;
24
24
  FORMVALIDATION["Password"] = "PASSWORD";
25
25
  FORMVALIDATION["MatchField"] = "MATCHFIELD";
26
26
  FORMVALIDATION["Pattern"] = "*";
27
+ FORMVALIDATION["GreaterThan"] = "GREATER_THAN";
27
28
  })(FORMVALIDATION || (FORMVALIDATION = {}));
28
29
  export var SHEET;
29
30
  (function (SHEET) {
@@ -48,6 +48,7 @@ export interface ZuzProps {
48
48
  animate?: animationProps;
49
49
  /** Animation configuration using {@link animationProps} */
50
50
  fx?: animationProps;
51
+ timeline?: animationProps[];
51
52
  transition?: TRANSITIONS;
52
53
  /** Skeleton placeholder configuration using {@link Skeleton} */
53
54
  skeleton?: Skeleton;
@@ -1,5 +1,13 @@
1
1
  import { dynamicObject } from ".";
2
2
  import { SKELETON, TRANSITION_CURVES, TRANSITIONS } from "./enums";
3
+ export interface scrollEffectProps {
4
+ lerpFactor?: number;
5
+ x?: number;
6
+ y?: number;
7
+ multiplier?: number;
8
+ xMultiplier?: number;
9
+ yMultiplier?: number;
10
+ }
3
11
  /**
4
12
  * `animationProps` defines the properties to control animation effects
5
13
  * applied to elements. Supports transitions with timing configurations.
@@ -26,6 +34,7 @@ export interface animationProps {
26
34
  delay?: number;
27
35
  /** Easing curve applied to the animation, as a string or {@link TRANSITION_CURVES} */
28
36
  curve?: string | TRANSITION_CURVES;
37
+ scroll?: scrollEffectProps;
29
38
  }
30
39
  /**
31
40
  * `Skeleton` defines properties for a skeleton loader, used to indicate