feat(mui): PageEnter kit primitive (consistent staggered page entrance) + adopt in WarehouseSuppliers
Each top-level child rises+fades in, staggered (0.4s, ease-out), so every page enters the same way and nothing appears instantly; honors prefers-reduced-motion. Replaces ad-hoc per-page motion.div entrance wrappers. One component → the look is tunable in one place. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { motion } from "framer-motion";
|
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
@@ -24,6 +23,7 @@ import {
|
|||||||
TextField,
|
TextField,
|
||||||
StatusChip,
|
StatusChip,
|
||||||
PageHeader,
|
PageHeader,
|
||||||
|
PageEnter,
|
||||||
FilterBar,
|
FilterBar,
|
||||||
EmptyState,
|
EmptyState,
|
||||||
LoadingState,
|
LoadingState,
|
||||||
@@ -344,22 +344,16 @@ export default function WarehouseSuppliers() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<PageEnter>
|
||||||
<motion.div
|
<PageHeader
|
||||||
initial={{ opacity: 0, y: 12 }}
|
title="Dodavatelé"
|
||||||
animate={{ opacity: 1, y: 0 }}
|
subtitle={subtitle}
|
||||||
transition={{ duration: 0.25 }}
|
actions={
|
||||||
>
|
<Button startIcon={PlusIcon} onClick={openCreateModal}>
|
||||||
<PageHeader
|
Přidat dodavatele
|
||||||
title="Dodavatelé"
|
</Button>
|
||||||
subtitle={subtitle}
|
}
|
||||||
actions={
|
/>
|
||||||
<Button startIcon={PlusIcon} onClick={openCreateModal}>
|
|
||||||
Přidat dodavatele
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</motion.div>
|
|
||||||
|
|
||||||
<FilterBar>
|
<FilterBar>
|
||||||
<Box sx={{ flex: "1 1 320px" }}>
|
<Box sx={{ flex: "1 1 320px" }}>
|
||||||
@@ -525,6 +519,6 @@ export default function WarehouseSuppliers() {
|
|||||||
confirmVariant="danger"
|
confirmVariant="danger"
|
||||||
loading={deleteMutation.isPending}
|
loading={deleteMutation.isPending}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</PageEnter>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
51
src/admin/ui/PageEnter.tsx
Normal file
51
src/admin/ui/PageEnter.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { Children, isValidElement, type ReactNode } from "react";
|
||||||
|
import { motion, useReducedMotion, type Variants } from "framer-motion";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import type { SxProps, Theme } from "@mui/material/styles";
|
||||||
|
|
||||||
|
const MotionBox = motion(Box);
|
||||||
|
|
||||||
|
const container: Variants = {
|
||||||
|
hidden: {},
|
||||||
|
show: { transition: { staggerChildren: 0.05, delayChildren: 0.03 } },
|
||||||
|
};
|
||||||
|
|
||||||
|
const itemVariant: Variants = {
|
||||||
|
hidden: { opacity: 0, y: 12 },
|
||||||
|
show: {
|
||||||
|
opacity: 1,
|
||||||
|
y: 0,
|
||||||
|
transition: { duration: 0.4, ease: [0.16, 1, 0.3, 1] },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consistent page entrance. Use as a page's OUTERMOST wrapper (in place of the
|
||||||
|
* page's root `<Box>`): each top-level child rises + fades in, staggered, so the
|
||||||
|
* whole page comes in the same way everywhere — no element appears instantly.
|
||||||
|
* Wrapping each child in a block `motion.div` is layout-safe for the vertical
|
||||||
|
* section stacks our pages use (header → filters → card/table); nested grids/
|
||||||
|
* flex inside a section are untouched. Honors prefers-reduced-motion.
|
||||||
|
*/
|
||||||
|
export default function PageEnter({
|
||||||
|
children,
|
||||||
|
sx,
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
sx?: SxProps<Theme>;
|
||||||
|
}) {
|
||||||
|
const reduce = useReducedMotion();
|
||||||
|
if (reduce) return <Box sx={sx}>{children}</Box>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MotionBox variants={container} initial="hidden" animate="show" sx={sx}>
|
||||||
|
{Children.map(children, (child) =>
|
||||||
|
isValidElement(child) ? (
|
||||||
|
<motion.div variants={itemVariant}>{child}</motion.div>
|
||||||
|
) : (
|
||||||
|
child
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</MotionBox>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -28,3 +28,4 @@ export { default as FileUpload } from "./FileUpload";
|
|||||||
export { default as ProgressBar } from "./ProgressBar";
|
export { default as ProgressBar } from "./ProgressBar";
|
||||||
export type { ProgressColor } from "./ProgressBar";
|
export type { ProgressColor } from "./ProgressBar";
|
||||||
export { default as ThemeToggle } from "./ThemeToggle";
|
export { default as ThemeToggle } from "./ThemeToggle";
|
||||||
|
export { default as PageEnter } from "./PageEnter";
|
||||||
|
|||||||
Reference in New Issue
Block a user