@wallarm-org/design-system 0.53.1 → 0.54.0-rc-feature-shell.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/AnimatedBackground/AnimatedBackground.d.ts +0 -2
- package/dist/components/AnimatedBackground/lib.js +3 -4
- package/dist/components/AnimatedBackground/types.d.ts +0 -1
- package/dist/components/AppShell/AppShell.d.ts +4 -1
- package/dist/components/AppShell/AppShell.js +74 -9
- package/dist/components/AppShell/AppShellContext.d.ts +6 -0
- package/dist/components/AppShell/AppShellContext.js +6 -0
- package/dist/components/AppShell/AppShellHeader.js +3 -1
- package/dist/components/AppShell/AppShellRail.js +3 -1
- package/dist/components/SplashScreen/SplashScreen.d.ts +2 -5
- package/dist/components/SplashScreen/SplashScreen.js +36 -44
- package/dist/components/SplashScreen/classes.d.ts +6 -3
- package/dist/components/SplashScreen/classes.js +16 -1
- package/dist/components/SplashScreen/index.d.ts +2 -1
- package/dist/components/SplashScreen/lib.d.ts +4 -0
- package/dist/components/SplashScreen/lib.js +31 -0
- package/dist/components/SplashScreen/types.d.ts +14 -0
- package/dist/components/SplashScreen/types.js +0 -0
- package/dist/components/SplashScreen/useSplashPhase.d.ts +8 -0
- package/dist/components/SplashScreen/useSplashPhase.js +55 -0
- package/dist/metadata/components.json +26 -10
- package/dist/theme/components/login-background.css +4 -8
- package/package.json +1 -1
|
@@ -26,8 +26,6 @@ export interface AnimatedBackgroundProps extends Omit<CanvasHTMLAttributes<HTMLC
|
|
|
26
26
|
accentColorVar?: string;
|
|
27
27
|
/** CSS custom-property name for the base fill. */
|
|
28
28
|
baseColorVar?: string;
|
|
29
|
-
/** CSS custom-property name for the clean leading-edge scan line. */
|
|
30
|
-
sweepColorVar?: string;
|
|
31
29
|
/** Force a single static frame (also auto-true under reduced motion). */
|
|
32
30
|
paused?: boolean;
|
|
33
31
|
}
|
|
@@ -23,10 +23,9 @@ const resolveOptions = (props)=>{
|
|
|
23
23
|
bloomAlpha: props.bloomAlpha ?? 0.2,
|
|
24
24
|
maxDotSize: props.maxDotSize ?? d.maxDotSize,
|
|
25
25
|
tilt: props.tilt ?? 16,
|
|
26
|
-
dotColorVar: props.dotColorVar ?? '--
|
|
27
|
-
accentColorVar: props.accentColorVar ?? '--
|
|
28
|
-
baseColorVar: props.baseColorVar ?? '--
|
|
29
|
-
sweepColorVar: props.sweepColorVar ?? '--login-bg-sweep'
|
|
26
|
+
dotColorVar: props.dotColorVar ?? '--animated-bg-dot',
|
|
27
|
+
accentColorVar: props.accentColorVar ?? '--animated-bg-accent-dot',
|
|
28
|
+
baseColorVar: props.baseColorVar ?? '--color-component-app-shell-bg'
|
|
30
29
|
};
|
|
31
30
|
};
|
|
32
31
|
export { resolveOptions };
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type FC, type HTMLAttributes, type ReactNode, type Ref } from 'react';
|
|
2
2
|
import { type TestableProps } from '../../utils/testId';
|
|
3
3
|
export interface AppShellProps extends HTMLAttributes<HTMLDivElement>, TestableProps {
|
|
4
4
|
ref?: Ref<HTMLDivElement>;
|
|
5
5
|
children?: ReactNode;
|
|
6
|
+
reveal?: boolean;
|
|
7
|
+
onRevealed?: () => void;
|
|
8
|
+
appeared?: boolean;
|
|
6
9
|
}
|
|
7
10
|
export declare const AppShell: FC<AppShellProps>;
|
|
@@ -1,16 +1,81 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { composeRefs } from "@radix-ui/react-compose-refs";
|
|
2
4
|
import { cn } from "../../utils/cn.js";
|
|
3
5
|
import { TestIdProvider } from "../../utils/testId.js";
|
|
4
|
-
|
|
6
|
+
import { AppShellContext } from "./AppShellContext.js";
|
|
7
|
+
const AppShell = ({ ref, className, children, reveal, onRevealed, appeared: appearedProp, 'data-testid': testId, ...props })=>{
|
|
8
|
+
const internalRef = useRef(null);
|
|
9
|
+
const onRevealedRef = useRef(onRevealed);
|
|
10
|
+
onRevealedRef.current = onRevealed;
|
|
11
|
+
const [phase, setPhase] = useState(reveal ? 'initial' : 'done');
|
|
12
|
+
const [targetRect, setTargetRect] = useState(null);
|
|
13
|
+
useEffect(()=>{
|
|
14
|
+
if ('initial' !== phase) return;
|
|
15
|
+
const shell = internalRef.current;
|
|
16
|
+
if (!shell) return;
|
|
17
|
+
let cancelled = false;
|
|
18
|
+
requestAnimationFrame(()=>{
|
|
19
|
+
requestAnimationFrame(()=>{
|
|
20
|
+
if (cancelled) return;
|
|
21
|
+
const remote = shell.querySelector('[data-slot="app-shell-remote"]');
|
|
22
|
+
if (!remote) {
|
|
23
|
+
setPhase('done');
|
|
24
|
+
onRevealedRef.current?.();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const shellRect = shell.getBoundingClientRect();
|
|
28
|
+
const remoteRect = remote.getBoundingClientRect();
|
|
29
|
+
setTargetRect({
|
|
30
|
+
top: remoteRect.top - shellRect.top,
|
|
31
|
+
left: remoteRect.left - shellRect.left
|
|
32
|
+
});
|
|
33
|
+
setPhase('revealing');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
return ()=>{
|
|
37
|
+
cancelled = true;
|
|
38
|
+
};
|
|
39
|
+
}, [
|
|
40
|
+
phase
|
|
41
|
+
]);
|
|
42
|
+
const handleTransitionEnd = useCallback((e)=>{
|
|
43
|
+
if ('top' === e.propertyName) {
|
|
44
|
+
setPhase('done');
|
|
45
|
+
onRevealedRef.current?.();
|
|
46
|
+
}
|
|
47
|
+
}, []);
|
|
48
|
+
const appeared = appearedProp ?? 'done' === phase;
|
|
49
|
+
const shellContext = useMemo(()=>({
|
|
50
|
+
appeared
|
|
51
|
+
}), [
|
|
52
|
+
appeared
|
|
53
|
+
]);
|
|
54
|
+
return /*#__PURE__*/ jsx(TestIdProvider, {
|
|
5
55
|
value: testId,
|
|
6
|
-
children: /*#__PURE__*/ jsx(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
56
|
+
children: /*#__PURE__*/ jsx(AppShellContext.Provider, {
|
|
57
|
+
value: shellContext,
|
|
58
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
59
|
+
...props,
|
|
60
|
+
ref: composeRefs(internalRef, ref),
|
|
61
|
+
"data-slot": "app-shell",
|
|
62
|
+
"data-testid": testId,
|
|
63
|
+
className: cn('relative grid h-screen overscroll-none [grid-template-areas:"header_header""rail_remote"] [grid-template-columns:auto_1fr] [grid-template-rows:auto_1fr] bg-component-app-shell-bg', className),
|
|
64
|
+
children: [
|
|
65
|
+
children,
|
|
66
|
+
'done' !== phase && /*#__PURE__*/ jsx("div", {
|
|
67
|
+
className: cn('bg-bg-page-bg pointer-events-none z-50 absolute right-0 bottom-0', 'revealing' === phase && 'rounded-tl-12'),
|
|
68
|
+
style: {
|
|
69
|
+
top: 'revealing' === phase && targetRect ? targetRect.top : 0,
|
|
70
|
+
left: 'revealing' === phase && targetRect ? targetRect.left : 0,
|
|
71
|
+
transition: 'revealing' === phase ? 'top 500ms ease-in-out, left 500ms ease-in-out, border-radius 500ms ease-in-out' : void 0
|
|
72
|
+
},
|
|
73
|
+
onTransitionEnd: handleTransitionEnd
|
|
74
|
+
})
|
|
75
|
+
]
|
|
76
|
+
})
|
|
13
77
|
})
|
|
14
78
|
});
|
|
79
|
+
};
|
|
15
80
|
AppShell.displayName = 'AppShell';
|
|
16
81
|
export { AppShell };
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../../utils/cn.js";
|
|
3
3
|
import { useTestId } from "../../utils/testId.js";
|
|
4
|
+
import { useAppShellAppeared } from "./AppShellContext.js";
|
|
4
5
|
const AppShellHeader = ({ ref, className, children, ...props })=>{
|
|
5
6
|
const testId = useTestId('header');
|
|
7
|
+
const appeared = useAppShellAppeared();
|
|
6
8
|
return /*#__PURE__*/ jsx("header", {
|
|
7
9
|
...props,
|
|
8
10
|
ref: ref,
|
|
9
11
|
"data-slot": "app-shell-header",
|
|
10
12
|
"data-testid": testId,
|
|
11
|
-
className: cn('[grid-area:header]', className),
|
|
13
|
+
className: cn('[grid-area:header] transition-opacity duration-200 ease-in-out', !appeared && 'opacity-0', className),
|
|
12
14
|
children: children
|
|
13
15
|
});
|
|
14
16
|
};
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../../utils/cn.js";
|
|
3
3
|
import { useTestId } from "../../utils/testId.js";
|
|
4
|
+
import { useAppShellAppeared } from "./AppShellContext.js";
|
|
4
5
|
const AppShellRail = ({ ref, className, children, ...props })=>{
|
|
5
6
|
const testId = useTestId('rail');
|
|
7
|
+
const appeared = useAppShellAppeared();
|
|
6
8
|
return /*#__PURE__*/ jsx("div", {
|
|
7
9
|
...props,
|
|
8
10
|
ref: ref,
|
|
9
11
|
"data-slot": "app-shell-rail",
|
|
10
12
|
"data-testid": testId,
|
|
11
|
-
className: cn('[grid-area:rail]', className),
|
|
13
|
+
className: cn('[grid-area:rail] transition-opacity duration-200 ease-in-out', !appeared && 'opacity-0', className),
|
|
12
14
|
children: children
|
|
13
15
|
});
|
|
14
16
|
};
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import type { FC
|
|
2
|
-
|
|
3
|
-
ref?: Ref<HTMLDivElement>;
|
|
4
|
-
visible?: boolean;
|
|
5
|
-
}
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
import type { SplashScreenProps } from './types';
|
|
6
3
|
export declare const SplashScreen: FC<SplashScreenProps>;
|
|
@@ -1,57 +1,49 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
2
|
import { cn } from "../../utils/cn.js";
|
|
4
3
|
import { Logo } from "../Logo/index.js";
|
|
5
4
|
import { Progress } from "../Progress/index.js";
|
|
6
|
-
import { splashContentVariants, splashLogoVariants, splashProgressVariants } from "./classes.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
setPhase('enter-start');
|
|
12
|
-
let inner;
|
|
13
|
-
const outer = requestAnimationFrame(()=>{
|
|
14
|
-
inner = requestAnimationFrame(()=>{
|
|
15
|
-
setPhase('entered');
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
return ()=>{
|
|
19
|
-
cancelAnimationFrame(outer);
|
|
20
|
-
cancelAnimationFrame(inner);
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
setPhase((prev)=>'exited' === prev || 'enter-start' === prev ? 'exited' : 'exiting');
|
|
24
|
-
}, [
|
|
25
|
-
visible
|
|
26
|
-
]);
|
|
5
|
+
import { splashContainerVariants, splashContentVariants, splashLogoVariants, splashProgressVariants } from "./classes.js";
|
|
6
|
+
import { SPLASH_PHASES, getContainerStyle } from "./lib.js";
|
|
7
|
+
import { useSplashPhase } from "./useSplashPhase.js";
|
|
8
|
+
const SplashScreen = ({ ref, visible = true, shrinkTarget, className, children, ...props })=>{
|
|
9
|
+
const { phase, childrenRevealed, handleContainerTransitionEnd, handleContentTransitionEnd } = useSplashPhase(visible, shrinkTarget);
|
|
27
10
|
if ('exited' === phase) return null;
|
|
28
|
-
const
|
|
29
|
-
|
|
11
|
+
const containerPhase = phase;
|
|
12
|
+
const contentPhase = phase;
|
|
13
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
30
14
|
...props,
|
|
31
15
|
"data-slot": "splash-screen",
|
|
32
16
|
ref: ref,
|
|
33
|
-
className:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
/*#__PURE__*/ jsx(Logo, {
|
|
43
|
-
className: splashLogoVariants({
|
|
44
|
-
phase: animPhase
|
|
45
|
-
})
|
|
17
|
+
className: cn(splashContainerVariants({
|
|
18
|
+
phase: containerPhase
|
|
19
|
+
}), className),
|
|
20
|
+
style: getContainerStyle(phase, shrinkTarget),
|
|
21
|
+
onTransitionEnd: handleContainerTransitionEnd,
|
|
22
|
+
children: [
|
|
23
|
+
SPLASH_PHASES[contentPhase] && /*#__PURE__*/ jsxs("div", {
|
|
24
|
+
className: splashContentVariants({
|
|
25
|
+
phase: contentPhase
|
|
46
26
|
}),
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
27
|
+
onTransitionEnd: handleContentTransitionEnd,
|
|
28
|
+
children: [
|
|
29
|
+
/*#__PURE__*/ jsx(Logo, {
|
|
30
|
+
className: splashLogoVariants({
|
|
31
|
+
phase: contentPhase
|
|
32
|
+
})
|
|
33
|
+
}),
|
|
34
|
+
/*#__PURE__*/ jsx(Progress, {
|
|
35
|
+
value: null,
|
|
36
|
+
className: splashProgressVariants({
|
|
37
|
+
phase: contentPhase
|
|
38
|
+
})
|
|
51
39
|
})
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
40
|
+
]
|
|
41
|
+
}),
|
|
42
|
+
'settled' === phase && children && /*#__PURE__*/ jsx("div", {
|
|
43
|
+
className: cn('h-full w-full transition-opacity duration-300', childrenRevealed ? 'opacity-100' : 'opacity-0'),
|
|
44
|
+
children: children
|
|
45
|
+
})
|
|
46
|
+
]
|
|
55
47
|
});
|
|
56
48
|
};
|
|
57
49
|
SplashScreen.displayName = 'SplashScreen';
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
export declare const splashContainerVariants: (props?: ({
|
|
2
|
+
phase?: "enter-start" | "entered" | "content-fading" | "shrinking" | "settled" | "exiting" | null | undefined;
|
|
3
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
1
4
|
export declare const splashContentVariants: (props?: ({
|
|
2
|
-
phase?: "enter-start" | "entered" | "exiting" | null | undefined;
|
|
5
|
+
phase?: "enter-start" | "entered" | "content-fading" | "exiting" | null | undefined;
|
|
3
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
4
7
|
export declare const splashLogoVariants: (props?: ({
|
|
5
|
-
phase?: "enter-start" | "entered" | "exiting" | null | undefined;
|
|
8
|
+
phase?: "enter-start" | "entered" | "content-fading" | "exiting" | null | undefined;
|
|
6
9
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
10
|
export declare const splashProgressVariants: (props?: ({
|
|
8
|
-
phase?: "enter-start" | "entered" | "exiting" | null | undefined;
|
|
11
|
+
phase?: "enter-start" | "entered" | "content-fading" | "exiting" | null | undefined;
|
|
9
12
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import { cva } from "class-variance-authority";
|
|
2
|
+
const splashContainerVariants = cva('flex items-center justify-center', {
|
|
3
|
+
variants: {
|
|
4
|
+
phase: {
|
|
5
|
+
'enter-start': 'h-full w-full',
|
|
6
|
+
entered: 'h-full w-full',
|
|
7
|
+
'content-fading': 'h-full w-full',
|
|
8
|
+
shrinking: 'h-full w-full',
|
|
9
|
+
settled: 'overflow-hidden',
|
|
10
|
+
exiting: 'h-full w-full'
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
});
|
|
2
14
|
const splashContentVariants = cva('flex h-max w-max flex-col items-center justify-center gap-12', {
|
|
3
15
|
variants: {
|
|
4
16
|
phase: {
|
|
5
17
|
'enter-start': 'opacity-0',
|
|
6
18
|
entered: 'opacity-100 transition-opacity duration-500 ease-out',
|
|
19
|
+
'content-fading': 'opacity-0 transition-opacity duration-300 ease-out',
|
|
7
20
|
exiting: 'opacity-0 transition-opacity duration-300 ease-out'
|
|
8
21
|
}
|
|
9
22
|
}
|
|
@@ -13,6 +26,7 @@ const splashLogoVariants = cva('', {
|
|
|
13
26
|
phase: {
|
|
14
27
|
'enter-start': 'translate-y-8',
|
|
15
28
|
entered: 'translate-y-0 transition-transform duration-500 ease-out',
|
|
29
|
+
'content-fading': 'translate-y-0',
|
|
16
30
|
exiting: 'translate-y-0 transition-transform duration-500 ease-out'
|
|
17
31
|
}
|
|
18
32
|
}
|
|
@@ -22,8 +36,9 @@ const splashProgressVariants = cva('', {
|
|
|
22
36
|
phase: {
|
|
23
37
|
'enter-start': 'translate-y-16',
|
|
24
38
|
entered: 'translate-y-0 transition-transform duration-500 ease-out',
|
|
39
|
+
'content-fading': 'translate-y-0',
|
|
25
40
|
exiting: 'translate-y-0 transition-transform duration-500 ease-out'
|
|
26
41
|
}
|
|
27
42
|
}
|
|
28
43
|
});
|
|
29
|
-
export { splashContentVariants, splashLogoVariants, splashProgressVariants };
|
|
44
|
+
export { splashContainerVariants, splashContentVariants, splashLogoVariants, splashProgressVariants };
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { SplashScreen
|
|
1
|
+
export { SplashScreen } from './SplashScreen';
|
|
2
|
+
export type { SplashScreenProps, SplashScreenShrinkTarget } from './types';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
import type { ContentPhase, PhaseType, SplashScreenShrinkTarget } from './types';
|
|
3
|
+
export declare const SPLASH_PHASES: Record<ContentPhase, boolean>;
|
|
4
|
+
export declare function getContainerStyle(phase: PhaseType, shrinkTarget?: SplashScreenShrinkTarget): CSSProperties | undefined;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const SPLASH_PHASES = {
|
|
2
|
+
'enter-start': true,
|
|
3
|
+
entered: true,
|
|
4
|
+
'content-fading': true,
|
|
5
|
+
exiting: true
|
|
6
|
+
};
|
|
7
|
+
function getContainerStyle(phase, shrinkTarget) {
|
|
8
|
+
if (!shrinkTarget) return;
|
|
9
|
+
const { width, height, borderRadius = 0 } = shrinkTarget;
|
|
10
|
+
switch(phase){
|
|
11
|
+
case 'content-fading':
|
|
12
|
+
return {
|
|
13
|
+
clipPath: 'inset(0 0 round 0px)',
|
|
14
|
+
transition: 'clip-path 500ms ease-in-out'
|
|
15
|
+
};
|
|
16
|
+
case 'shrinking':
|
|
17
|
+
return {
|
|
18
|
+
clipPath: `inset(calc(50% - ${height / 2}px) calc(50% - ${width / 2}px) round ${borderRadius}px)`,
|
|
19
|
+
transition: 'clip-path 500ms ease-in-out'
|
|
20
|
+
};
|
|
21
|
+
case 'settled':
|
|
22
|
+
return {
|
|
23
|
+
width,
|
|
24
|
+
height,
|
|
25
|
+
borderRadius
|
|
26
|
+
};
|
|
27
|
+
default:
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export { SPLASH_PHASES, getContainerStyle };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode, Ref } from 'react';
|
|
2
|
+
export type ContentPhase = 'enter-start' | 'entered' | 'content-fading' | 'exiting';
|
|
3
|
+
export type PhaseType = ContentPhase | 'shrinking' | 'settled' | 'exited';
|
|
4
|
+
export interface SplashScreenShrinkTarget {
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
borderRadius?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface SplashScreenProps extends HTMLAttributes<HTMLDivElement> {
|
|
10
|
+
ref?: Ref<HTMLDivElement>;
|
|
11
|
+
visible?: boolean;
|
|
12
|
+
shrinkTarget?: SplashScreenShrinkTarget;
|
|
13
|
+
children?: ReactNode;
|
|
14
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TransitionEvent } from 'react';
|
|
2
|
+
import type { PhaseType, SplashScreenShrinkTarget } from './types';
|
|
3
|
+
export declare const useSplashPhase: (visible: boolean, shrinkTarget?: SplashScreenShrinkTarget) => {
|
|
4
|
+
phase: PhaseType;
|
|
5
|
+
childrenRevealed: boolean;
|
|
6
|
+
handleContainerTransitionEnd: (e: TransitionEvent<HTMLDivElement>) => void;
|
|
7
|
+
handleContentTransitionEnd: (e: TransitionEvent<HTMLDivElement>) => void;
|
|
8
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
const doubleRaf = (callback)=>{
|
|
3
|
+
let id2;
|
|
4
|
+
const id1 = requestAnimationFrame(()=>{
|
|
5
|
+
id2 = requestAnimationFrame(callback);
|
|
6
|
+
});
|
|
7
|
+
return ()=>{
|
|
8
|
+
cancelAnimationFrame(id1);
|
|
9
|
+
cancelAnimationFrame(id2);
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
const useSplashPhase = (visible, shrinkTarget)=>{
|
|
13
|
+
const [phase, setPhase] = useState(()=>{
|
|
14
|
+
if (visible) return 'enter-start';
|
|
15
|
+
if (shrinkTarget) return 'settled';
|
|
16
|
+
return 'exited';
|
|
17
|
+
});
|
|
18
|
+
const [childrenRevealed, setChildrenRevealed] = useState(!visible && !!shrinkTarget);
|
|
19
|
+
useEffect(()=>{
|
|
20
|
+
if (visible) {
|
|
21
|
+
setPhase('enter-start');
|
|
22
|
+
setChildrenRevealed(false);
|
|
23
|
+
return doubleRaf(()=>setPhase('entered'));
|
|
24
|
+
}
|
|
25
|
+
setPhase((prev)=>{
|
|
26
|
+
if ('exited' === prev || 'enter-start' === prev) return shrinkTarget ? 'settled' : 'exited';
|
|
27
|
+
return shrinkTarget ? 'content-fading' : 'exiting';
|
|
28
|
+
});
|
|
29
|
+
}, [
|
|
30
|
+
visible,
|
|
31
|
+
shrinkTarget
|
|
32
|
+
]);
|
|
33
|
+
useEffect(()=>{
|
|
34
|
+
if ('settled' !== phase) return;
|
|
35
|
+
return doubleRaf(()=>setChildrenRevealed(true));
|
|
36
|
+
}, [
|
|
37
|
+
phase
|
|
38
|
+
]);
|
|
39
|
+
const handleContainerTransitionEnd = (e)=>{
|
|
40
|
+
if (e.target !== e.currentTarget) return;
|
|
41
|
+
if ('shrinking' === phase && 'clip-path' === e.propertyName) setPhase('settled');
|
|
42
|
+
};
|
|
43
|
+
const handleContentTransitionEnd = (e)=>{
|
|
44
|
+
e.stopPropagation();
|
|
45
|
+
if ('exiting' === phase) setPhase('exited');
|
|
46
|
+
else if ('content-fading' === phase) setPhase('shrinking');
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
phase,
|
|
50
|
+
childrenRevealed,
|
|
51
|
+
handleContainerTransitionEnd,
|
|
52
|
+
handleContentTransitionEnd
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
export { useSplashPhase };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.53.
|
|
3
|
-
"generatedAt": "2026-06-
|
|
2
|
+
"version": "0.53.1",
|
|
3
|
+
"generatedAt": "2026-06-04T00:14:48.977Z",
|
|
4
4
|
"components": [
|
|
5
5
|
{
|
|
6
6
|
"name": "Accordion",
|
|
@@ -3406,12 +3406,6 @@
|
|
|
3406
3406
|
"required": false,
|
|
3407
3407
|
"description": "CSS custom-property name for the base fill."
|
|
3408
3408
|
},
|
|
3409
|
-
{
|
|
3410
|
-
"name": "sweepColorVar",
|
|
3411
|
-
"type": "string | undefined",
|
|
3412
|
-
"required": false,
|
|
3413
|
-
"description": "CSS custom-property name for the clean leading-edge scan line."
|
|
3414
|
-
},
|
|
3415
3409
|
{
|
|
3416
3410
|
"name": "paused",
|
|
3417
3411
|
"type": "boolean | undefined",
|
|
@@ -3717,7 +3711,7 @@
|
|
|
3717
3711
|
},
|
|
3718
3712
|
{
|
|
3719
3713
|
"name": "WithCard",
|
|
3720
|
-
"code": "() => (\n <div className='relative h-
|
|
3714
|
+
"code": "() => (\n <div className='relative h-screen w-screen'>\n <AnimatedBackground />\n\n <Card className='absolute top-1/2 left-1/2 -translate-1/2 w-[300px] h-max'>\n <CardHeader>\n <CardTitle>Sign In</CardTitle>\n </CardHeader>\n <CardContent>\n <p className='text-sm text-text-secondary'>\n Decorative background renders behind interactive content.\n </p>\n </CardContent>\n </Card>\n </div>\n)"
|
|
3721
3715
|
}
|
|
3722
3716
|
]
|
|
3723
3717
|
},
|
|
@@ -3725,6 +3719,16 @@
|
|
|
3725
3719
|
"name": "AppShell",
|
|
3726
3720
|
"importPath": "@wallarm-org/design-system/AppShell",
|
|
3727
3721
|
"props": [
|
|
3722
|
+
{
|
|
3723
|
+
"name": "reveal",
|
|
3724
|
+
"type": "boolean | undefined",
|
|
3725
|
+
"required": false
|
|
3726
|
+
},
|
|
3727
|
+
{
|
|
3728
|
+
"name": "appeared",
|
|
3729
|
+
"type": "boolean | undefined",
|
|
3730
|
+
"required": false
|
|
3731
|
+
},
|
|
3728
3732
|
{
|
|
3729
3733
|
"name": "defaultChecked",
|
|
3730
3734
|
"type": "boolean | undefined",
|
|
@@ -4827,7 +4831,7 @@
|
|
|
4827
4831
|
"examples": [
|
|
4828
4832
|
{
|
|
4829
4833
|
"name": "Basic",
|
|
4830
|
-
"code": "() => {\n const pathname = useLocationPathname();\n const activeProduct = deriveProduct(pathname);\n\n const [loading, setLoading] = useState(true);\n const [sidebarMode, setSidebarMode] = useState<SidebarMode>('adaptive');\n const { theme, setTheme } = useTheme();\n const collapsed = sidebarMode === 'adaptive' && activeProduct !== 'home';\n\n return (\n <AppShell>\n <AppShellHeader>\n <TopHeader>\n <TopHeaderLogo href='/'>\n <WallarmLogo />\n </TopHeaderLogo>\n\n <TopHeaderActions>\n {loading ? (\n <>\n <Skeleton width='150px' height='20px' rounded={6} />\n <TopHeaderSeparator />\n <Skeleton width='150px' height='20px' rounded={6} />\n </>\n ) : (\n <>\n <Button\n variant='ghost'\n size='small'\n color='neutral'\n className='p-4 gap-6 rounded-6'\n >\n <Code size='s' color='secondary'>\n Search Wallarm\n </Code>\n <Kbd size='xsmall'>⌘ K</Kbd>\n </Button>\n\n <TopHeaderSeparator />\n\n <Button variant='ghost' size='small' color='neutral' className='py-4 rounded-6'>\n <Text size='xs' weight='medium'>\n Tenant Name\n </Text>\n <span className='text-text-tertiary mx-[-2px]'>•</span>\n <Code size='s' color='secondary'>\n 12345\n </Code>\n <ChevronUpDown className='!icon-sm' />\n </Button>\n </>\n )}\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant='ghost' size='small' color='neutral' aria-label='Wallarm Updates'>\n <Bell />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Wallarm updates</TooltipContent>\n </Tooltip>\n\n <QuickHelpDropdown />\n </TopHeaderActions>\n </TopHeader>\n </AppShellHeader>\n\n <AppShellRail>\n <NavRail collapsed={collapsed}>\n <NavRailBody>\n <NavRailItem\n icon={Home}\n label='Home'\n shortcut={['G', 'H']}\n active={activeProduct === 'home'}\n onClick={() => navigateToProduct('home')}\n />\n <RecentDropdown />\n\n <NavRailSeparator />\n\n {loading ? (\n <NavRailSkeleton />\n ) : (\n <>\n <NavRailItem\n icon={CircleDashed}\n label='Edge'\n shortcut={['G', 'E']}\n active={activeProduct === 'edge'}\n onClick={() => navigateToProduct('edge')}\n />\n <NavRailItem\n icon={CircleDashed}\n label='AI Hypervisor'\n shortcut={['G', 'A']}\n active={activeProduct === 'ai-hypervisor'}\n onClick={() => navigateToProduct('ai-hypervisor')}\n />\n <NavRailItem\n icon={CircleDashed}\n label='Infra Discovery'\n shortcut={['G', 'I']}\n active={activeProduct === 'infra-discovery'}\n onClick={() => navigateToProduct('infra-discovery')}\n />\n <NavRailItem\n icon={CircleDashed}\n label='Security Testing'\n shortcut={['G', 'T']}\n active={activeProduct === 'security-testing'}\n onClick={() => navigateToProduct('security-testing')}\n />\n </>\n )}\n </NavRailBody>\n\n <NavRailFooter>\n <NavRailItem\n icon={Settings}\n label='Settings'\n shortcut={['G', 'S']}\n active={activeProduct === 'settings'}\n onClick={() => navigateToProduct('settings')}\n />\n <AccountDropdown\n sidebarMode={sidebarMode}\n onSidebarModeChange={setSidebarMode}\n theme={theme}\n onThemeChange={setTheme}\n />\n </NavRailFooter>\n </NavRail>\n </AppShellRail>\n\n <AppShellRemote>\n <div className='absolute top-4 right-4 z-10'>\n <Button variant='ghost' size='small' color='neutral' onClick={() => setLoading(v => !v)}>\n {loading ? 'Finish loading' : 'Start loading'}\n </Button>\n </div>\n\n <RemoteForProduct product={activeProduct} />\n </AppShellRemote>\n </AppShell>\n );\n}"
|
|
4834
|
+
"code": "() => {\n const pathname = useLocationPathname();\n const activeProduct = deriveProduct(pathname);\n\n const [loading, setLoading] = useState(true);\n const [sidebarMode, setSidebarMode] = useState<SidebarMode>('adaptive');\n const [revealKey, setRevealKey] = useState(0);\n const { theme, setTheme } = useTheme();\n const collapsed = sidebarMode === 'adaptive' && activeProduct !== 'home';\n\n return (\n <AppShell key={revealKey} reveal>\n <AppShellHeader>\n <TopHeader>\n <TopHeaderLogo href='/'>\n <WallarmLogo />\n </TopHeaderLogo>\n\n <TopHeaderActions>\n {loading ? (\n <>\n <Skeleton width='150px' height='20px' rounded={6} />\n <TopHeaderSeparator />\n <Skeleton width='150px' height='20px' rounded={6} />\n </>\n ) : (\n <>\n <Button\n variant='ghost'\n size='small'\n color='neutral'\n className='p-4 gap-6 rounded-6'\n >\n <Code size='s' color='secondary'>\n Search Wallarm\n </Code>\n <Kbd size='xsmall'>⌘ K</Kbd>\n </Button>\n\n <TopHeaderSeparator />\n\n <Button variant='ghost' size='small' color='neutral' className='py-4 rounded-6'>\n <Text size='xs' weight='medium'>\n Tenant Name\n </Text>\n <span className='text-text-tertiary mx-[-2px]'>•</span>\n <Code size='s' color='secondary'>\n 12345\n </Code>\n <ChevronUpDown className='!icon-sm' />\n </Button>\n </>\n )}\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant='ghost' size='small' color='neutral' aria-label='Wallarm Updates'>\n <Bell />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Wallarm updates</TooltipContent>\n </Tooltip>\n\n <QuickHelpDropdown />\n </TopHeaderActions>\n </TopHeader>\n </AppShellHeader>\n\n <AppShellRail>\n <NavRail collapsed={collapsed}>\n <NavRailBody>\n <NavRailItem\n icon={Home}\n label='Home'\n shortcut={['G', 'H']}\n active={activeProduct === 'home'}\n onClick={() => navigateToProduct('home')}\n />\n <RecentDropdown />\n\n <NavRailSeparator />\n\n {loading ? (\n <NavRailSkeleton />\n ) : (\n <>\n <NavRailItem\n icon={CircleDashed}\n label='Edge'\n shortcut={['G', 'E']}\n active={activeProduct === 'edge'}\n onClick={() => navigateToProduct('edge')}\n />\n <NavRailItem\n icon={CircleDashed}\n label='AI Hypervisor'\n shortcut={['G', 'A']}\n active={activeProduct === 'ai-hypervisor'}\n onClick={() => navigateToProduct('ai-hypervisor')}\n />\n <NavRailItem\n icon={CircleDashed}\n label='Infra Discovery'\n shortcut={['G', 'I']}\n active={activeProduct === 'infra-discovery'}\n onClick={() => navigateToProduct('infra-discovery')}\n />\n <NavRailItem\n icon={CircleDashed}\n label='Security Testing'\n shortcut={['G', 'T']}\n active={activeProduct === 'security-testing'}\n onClick={() => navigateToProduct('security-testing')}\n />\n </>\n )}\n </NavRailBody>\n\n <NavRailFooter>\n <NavRailItem\n icon={Settings}\n label='Settings'\n shortcut={['G', 'S']}\n active={activeProduct === 'settings'}\n onClick={() => navigateToProduct('settings')}\n />\n <AccountDropdown\n sidebarMode={sidebarMode}\n onSidebarModeChange={setSidebarMode}\n theme={theme}\n onThemeChange={setTheme}\n />\n </NavRailFooter>\n </NavRail>\n </AppShellRail>\n\n <AppShellRemote>\n <div className='flex gap-8 absolute top-4 right-4 z-10'>\n <Button variant='ghost' size='small' color='neutral' onClick={() => setLoading(v => !v)}>\n {loading ? 'Finish loading' : 'Start loading'}\n </Button>\n\n <Button\n variant='ghost'\n size='small'\n color='neutral'\n onClick={() => setRevealKey(k => k + 1)}\n >\n Replay animation\n </Button>\n </div>\n\n <RemoteForProduct product={activeProduct} />\n </AppShellRemote>\n </AppShell>\n );\n}"
|
|
4831
4835
|
}
|
|
4832
4836
|
]
|
|
4833
4837
|
},
|
|
@@ -49259,6 +49263,11 @@
|
|
|
49259
49263
|
"required": false,
|
|
49260
49264
|
"defaultValue": "true"
|
|
49261
49265
|
},
|
|
49266
|
+
{
|
|
49267
|
+
"name": "shrinkTarget",
|
|
49268
|
+
"type": "SplashScreenShrinkTarget | undefined",
|
|
49269
|
+
"required": false
|
|
49270
|
+
},
|
|
49262
49271
|
{
|
|
49263
49272
|
"name": "defaultChecked",
|
|
49264
49273
|
"type": "boolean | undefined",
|
|
@@ -49536,6 +49545,9 @@
|
|
|
49536
49545
|
"options": [
|
|
49537
49546
|
"enter-start",
|
|
49538
49547
|
"entered",
|
|
49548
|
+
"content-fading",
|
|
49549
|
+
"shrinking",
|
|
49550
|
+
"settled",
|
|
49539
49551
|
"exiting"
|
|
49540
49552
|
]
|
|
49541
49553
|
}
|
|
@@ -49549,6 +49561,10 @@
|
|
|
49549
49561
|
{
|
|
49550
49562
|
"name": "Toggle",
|
|
49551
49563
|
"code": "() => {\n const [visible, setVisible] = useState(true);\n\n return (\n <VStack align='center'>\n <div className='flex h-400 w-400 items-center justify-center'>\n <SplashScreen visible={visible} />\n </div>\n\n <Button type='button' onClick={() => setVisible(v => !v)}>\n {visible ? 'Hide' : 'Show'}\n </Button>\n </VStack>\n );\n}"
|
|
49564
|
+
},
|
|
49565
|
+
{
|
|
49566
|
+
"name": "ShrinkToCard",
|
|
49567
|
+
"code": "() => {\n const [visible, setVisible] = useState(true);\n\n useEffect(() => {\n if (!visible) return;\n\n const timer = window.setTimeout(() => {\n setVisible(false);\n }, 2000);\n\n return () => window.clearTimeout(timer);\n }, [visible]);\n\n return (\n <div className='relative h-screen w-screen'>\n <AnimatedBackground />\n\n <div className='absolute inset-0 flex items-center justify-center'>\n <SplashScreen\n visible={visible}\n shrinkTarget={{ width: 480, height: 600, borderRadius: 12 }}\n className='bg-bg-page-bg shadow-lg'\n >\n <div className='flex h-full flex-col items-center justify-center gap-4 p-8'>\n <h2 className='text-lg font-semibold text-text-primary'>Welcome</h2>\n <p className='text-center text-sm text-text-secondary'>\n Content revealed after splash animation.\n </p>\n <Button type='button' onClick={() => setVisible(true)}>\n Replay\n </Button>\n </div>\n </SplashScreen>\n </div>\n </div>\n );\n}"
|
|
49552
49568
|
}
|
|
49553
49569
|
]
|
|
49554
49570
|
},
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
:root {
|
|
2
|
-
--
|
|
3
|
-
--
|
|
4
|
-
--login-bg-base: var(--color-slate-50);
|
|
5
|
-
--login-bg-sweep: var(--color-slate-950);
|
|
2
|
+
--animated-bg-dot: var(--color-slate-500);
|
|
3
|
+
--animated-bg-accent-dot: var(--color-red-400);
|
|
6
4
|
}
|
|
7
5
|
|
|
8
6
|
[data-theme="dark"] {
|
|
9
|
-
--
|
|
10
|
-
--
|
|
11
|
-
--login-bg-base: var(--color-slate-950);
|
|
12
|
-
--login-bg-sweep: var(--color-slate-50);
|
|
7
|
+
--animated-bg-dot: var(--color-slate-500);
|
|
8
|
+
--animated-bg-accent-dot: var(--color-red-400);
|
|
13
9
|
}
|
package/package.json
CHANGED