@lumx/react 3.8.1 → 3.8.2-alpha.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.
@@ -0,0 +1,56 @@
1
+ import ReactDOM from 'react-dom';
2
+
3
+ import { MaybeElementOrRef } from '@lumx/react/utils/type';
4
+
5
+ import { unref } from '../react/unref';
6
+ import { getPrefersReducedMotion } from '../browser/getPrefersReducedMotion';
7
+
8
+ function setTransitionViewName(elementRef: MaybeElementOrRef<HTMLElement>, name: string | null | undefined) {
9
+ const element = unref(elementRef) as any;
10
+ if (element) element.style.viewTransitionName = name;
11
+ }
12
+
13
+ /**
14
+ * Wrapper around the `document.startViewTransition` handling browser incompatibilities, react DOM flush and
15
+ * user preference.
16
+ *
17
+ * @param changes callback containing the changes to apply within the view transition.
18
+ * @param setViewTransitionName set the `viewTransitionName` style on a `source` & `target` to morph these elements.
19
+ */
20
+ export async function startViewTransition({
21
+ changes,
22
+ viewTransitionName,
23
+ }: {
24
+ changes: () => void;
25
+ viewTransitionName: {
26
+ source: MaybeElementOrRef<HTMLElement>;
27
+ target: MaybeElementOrRef<HTMLElement>;
28
+ name: string;
29
+ };
30
+ }) {
31
+ const start = (document as any)?.startViewTransition?.bind(document);
32
+ const prefersReducedMotion = getPrefersReducedMotion();
33
+ const { flushSync } = ReactDOM as any;
34
+ if (prefersReducedMotion || !start || !flushSync || !viewTransitionName?.source || !viewTransitionName?.target) {
35
+ // Skip, apply changes without a transition
36
+ changes();
37
+ return;
38
+ }
39
+
40
+ // Set transition name on source element
41
+ setTransitionViewName(viewTransitionName.source, viewTransitionName.name);
42
+
43
+ // Start view transition, apply changes & flush to DOM
44
+ await start(() => {
45
+ // Un-set transition name on source element
46
+ setTransitionViewName(viewTransitionName.source, null);
47
+
48
+ flushSync(changes);
49
+
50
+ // Set transition name on target element
51
+ setTransitionViewName(viewTransitionName.target, viewTransitionName.name);
52
+ }).updateCallbackDone;
53
+
54
+ // Un-set transition name on target element
55
+ setTransitionViewName(viewTransitionName.target, null);
56
+ }
@@ -0,0 +1,6 @@
1
+ import { WINDOW } from '@lumx/react/constants';
2
+
3
+ /** Check if user prefers reduced motion */
4
+ export function getPrefersReducedMotion() {
5
+ return WINDOW?.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
6
+ }
@@ -0,0 +1,25 @@
1
+ import { isEqual } from './isEqual';
2
+
3
+ test(isEqual.name, () => {
4
+ expect(isEqual('', '')).toBe(true);
5
+ expect(isEqual(0, 0)).toBe(true);
6
+ expect(isEqual(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY)).toBe(true);
7
+
8
+ expect(isEqual('', 0)).toBe(false);
9
+
10
+ expect(isEqual({}, {})).toBe(true);
11
+ expect(isEqual({ a: 1 }, { a: 1 })).toBe(true);
12
+ expect(isEqual({ a: { a: 1 } }, { a: { a: 1 } })).toBe(true);
13
+
14
+ expect(isEqual([], [])).toBe(true);
15
+
16
+ expect(isEqual([1], [2])).toBe(false);
17
+ expect(isEqual([1], [1, 2])).toBe(false);
18
+ expect(isEqual([1, 2], [2, 1])).toBe(false);
19
+
20
+ expect(isEqual({ a: 1 }, { a: 2 })).toBe(false);
21
+ expect(isEqual({ a: 1 }, {})).toBe(false);
22
+ expect(isEqual({}, { a: 1 })).toBe(false);
23
+ expect(isEqual({ a: { a: 1 } }, { a: { a: 2 } })).toBe(false);
24
+ expect(isEqual({ a: 1 }, { a: 1, b: 1 })).toBe(false);
25
+ });
@@ -0,0 +1,11 @@
1
+ /** Minimal recursive deep equal of JS values */
2
+ export function isEqual(obj1: any, obj2: any): boolean {
3
+ if (obj1 === obj2) return true;
4
+ if (typeof obj1 === 'object' && typeof obj2 === 'object') {
5
+ const keys1 = Object.keys(obj1);
6
+ const keys2 = Object.keys(obj2);
7
+ if (keys1.length !== keys2.length) return false;
8
+ return keys1.every((key1) => isEqual(obj1[key1], obj2[key1]));
9
+ }
10
+ return false;
11
+ }
@@ -0,0 +1,7 @@
1
+ import { MaybeElementOrRef } from '@lumx/react/utils/type';
2
+
3
+ /** Unref a react ref or element */
4
+ export function unref(maybeElement: MaybeElementOrRef<HTMLElement>) {
5
+ if (maybeElement instanceof HTMLElement) return maybeElement;
6
+ return maybeElement?.current;
7
+ }
package/src/utils/type.ts CHANGED
@@ -139,3 +139,18 @@ export type ComponentRef<C> = C extends keyof JSX.IntrinsicElements
139
139
  : C extends React.JSXElementConstructor<{ ref?: infer R }>
140
140
  ? R
141
141
  : never;
142
+
143
+ /**
144
+ * Rectangle size
145
+ */
146
+ export type RectSize = { width: number; height: number };
147
+
148
+ /**
149
+ * Maybe a HTMLElement or a React ref of a HTMLElement
150
+ */
151
+ export type MaybeElementOrRef<E extends HTMLElement> = E | React.RefObject<E | null> | null | undefined;
152
+
153
+ /**
154
+ * A point coordinate in 2D space
155
+ */
156
+ export type Point = { x: number; y: number };
File without changes