k
This commit is contained in:
34
src/components/AnimatedOutlet/au.tsx
Normal file
34
src/components/AnimatedOutlet/au.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import {forwardRef, RefObject, useContext, useRef} from "react";
|
||||
import {getRouterContext, Outlet} from "@tanstack/react-router";
|
||||
import {motion, useIsPresent} from "framer-motion";
|
||||
import {cloneDeep} from "lodash";
|
||||
|
||||
const AnimatedOutlet = forwardRef<HTMLDivElement>((_, ref) => {
|
||||
const RouterContext = getRouterContext();
|
||||
|
||||
const routerContext = useContext(RouterContext);
|
||||
|
||||
const renderedContext = useRef(routerContext);
|
||||
|
||||
const isPresent = useIsPresent();
|
||||
|
||||
if (isPresent) {
|
||||
renderedContext.current = cloneDeep(routerContext);
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div ref={ref}
|
||||
initial={{x: "-100%"}}
|
||||
animate={{x: 0}}
|
||||
transition={{duration: 0.3}}
|
||||
onAnimationComplete={()=>{
|
||||
(ref as RefObject<HTMLDivElement>).current?.style.removeProperty("transform")
|
||||
}}
|
||||
>
|
||||
<RouterContext.Provider value={renderedContext.current}>
|
||||
<Outlet/>
|
||||
</RouterContext.Provider>
|
||||
</motion.div>
|
||||
);
|
||||
});
|
||||
export default AnimatedOutlet
|
||||
@@ -26,7 +26,7 @@ export type BaseTableRef<T extends MRT_RowData> = {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
export const BaseTable = forwardRef<BaseTableRef<never>, Props<never>>((props, ref) => {
|
||||
const {data, columns, restProps, striped, onSelectionChange} = props;
|
||||
const {data, columns, restProps, striped = true, onSelectionChange} = props;
|
||||
|
||||
const table = useMantineReactTable({
|
||||
localization: MRT_Localization_RU,
|
||||
|
||||
@@ -2,7 +2,7 @@ import {Center, Image, rem, Stack, Tooltip, UnstyledButton, useMantineColorSchem
|
||||
import {
|
||||
IconBarcode,
|
||||
IconBox,
|
||||
IconCash,
|
||||
IconCash, IconDashboard,
|
||||
IconFileBarcode,
|
||||
IconHome2,
|
||||
IconLogout,
|
||||
@@ -76,7 +76,7 @@ export function Navbar() {
|
||||
const dispatch = useAppDispatch();
|
||||
const navigate = useNavigate();
|
||||
const router = useRouterState();
|
||||
const {colorScheme, toggleColorScheme} = useMantineColorScheme({keepTransitions: false});
|
||||
const {colorScheme, toggleColorScheme} = useMantineColorScheme({keepTransitions: true});
|
||||
const onLogoutClick = () => {
|
||||
dispatch(logout());
|
||||
navigate({to: '/login'});
|
||||
@@ -110,6 +110,12 @@ export function Navbar() {
|
||||
</div>
|
||||
|
||||
<Stack w={"100%"} justify="center" gap={0}>
|
||||
<NavbarLink icon={IconDashboard}
|
||||
href={"/admin"}
|
||||
index={-1}
|
||||
label={"Панель администратора"}
|
||||
onClick={() => onNavlinkClick({href: "/admin", index: -1, icon: IconDashboard})}
|
||||
/>
|
||||
<NavbarLink label={"Сменить тему"} onClick={toggleColorScheme}
|
||||
icon={colorScheme == "dark" ? IconSun : IconMoon} href={"#"} index={-1}/>
|
||||
<NavbarLink index={-1} href={"#"} onClick={onLogoutClick} icon={IconLogout} label="Выйти"/>
|
||||
|
||||
@@ -1,35 +1,55 @@
|
||||
import {Select, SelectProps} from "@mantine/core";
|
||||
import {useEffect, useMemo, useState} from "react";
|
||||
import {ObjectWithNameAndId} from "../../types/utils.ts";
|
||||
import {groupBy, omit} from "lodash";
|
||||
|
||||
interface ObjectWithIdAndName {
|
||||
id: number,
|
||||
name: string
|
||||
}
|
||||
|
||||
export type SelectObjectType<T extends ObjectWithNameAndId> = T;
|
||||
export type SelectObjectType<T> = T;
|
||||
|
||||
type ControlledValueProps<T extends ObjectWithNameAndId> = {
|
||||
type ControlledValueProps<T> = {
|
||||
value: SelectObjectType<T>,
|
||||
onChange: (value: SelectObjectType<T>) => void;
|
||||
}
|
||||
type CustomLabelAndKeyProps<T> = {
|
||||
getLabelFn: (item: SelectObjectType<T>) => string;
|
||||
getValueFn: (item: SelectObjectType<T>) => string;
|
||||
}
|
||||
|
||||
type RestProps<T extends ObjectWithNameAndId> = {
|
||||
type RestProps<T> = {
|
||||
defaultValue?: SelectObjectType<T>
|
||||
onChange: (value: SelectObjectType<T>) => void;
|
||||
data: SelectObjectType<T>[];
|
||||
groupBy?: (item: SelectObjectType<T>) => string;
|
||||
filterBy?: (item: SelectObjectType<T>) => boolean;
|
||||
};
|
||||
const defaultGetLabelFn = <T extends { name: string }>(item: T): string => {
|
||||
return item.name;
|
||||
}
|
||||
|
||||
export type ObjectSelectProps<T extends ObjectWithNameAndId> =
|
||||
const defaultGetValueFn = <T extends { id: number }>(item: T): string => {
|
||||
return item.id.toString();
|
||||
}
|
||||
export type ObjectSelectProps<T> =
|
||||
(RestProps<T> & Partial<ControlledValueProps<T>>)
|
||||
& Omit<SelectProps, 'value' | 'onChange' | 'data'>;
|
||||
& Omit<SelectProps, 'value' | 'onChange' | 'data'>
|
||||
& (T extends ObjectWithIdAndName ? Partial<CustomLabelAndKeyProps<T>> : CustomLabelAndKeyProps<T>)
|
||||
|
||||
const ObjectSelect = <T extends ObjectWithNameAndId, >(props: ObjectSelectProps<T>) => {
|
||||
const ObjectSelect = <T, >(props: ObjectSelectProps<T>) => {
|
||||
|
||||
const isControlled = 'value' in props;
|
||||
const haveGetValueFn = 'getValueFn' in props;
|
||||
const haveGetLabelFn = 'getLabelFn' in props;
|
||||
const [internalValue, setInternalValue] = useState<SelectObjectType<T> | undefined>(props.defaultValue);
|
||||
|
||||
const value = isControlled ? props.value : internalValue;
|
||||
|
||||
|
||||
const getValueFn = (haveGetValueFn && props.getValueFn) || defaultGetValueFn;
|
||||
const getLabelFn = (haveGetLabelFn && props.getLabelFn) || defaultGetLabelFn;
|
||||
|
||||
const data = useMemo(() => {
|
||||
const propsData = props.filterBy ? props.data.filter(props.filterBy) : props.data;
|
||||
if (props.groupBy) {
|
||||
@@ -38,21 +58,21 @@ const ObjectSelect = <T extends ObjectWithNameAndId, >(props: ObjectSelectProps<
|
||||
return Object.entries(groupedData).map(([group, items]) => ({
|
||||
group,
|
||||
items: items.map(item => ({
|
||||
label: item.name,
|
||||
value: item.id.toString()
|
||||
label: getLabelFn(item),
|
||||
value: getValueFn(item)
|
||||
}))
|
||||
}));
|
||||
} else {
|
||||
return propsData.map(item => ({
|
||||
label: item.name,
|
||||
value: item.id.toString()
|
||||
label: getLabelFn(item),
|
||||
value: getValueFn(item)
|
||||
}));
|
||||
}
|
||||
}, [props.data, props.groupBy]);
|
||||
|
||||
const handleOnChange = (event: string | null) => {
|
||||
if (!event) return;
|
||||
const object = props.data.find(item => parseInt(event) == item.id);
|
||||
const object = props.data.find(item => event == getValueFn(item));
|
||||
if (!object) return;
|
||||
if (isControlled) {
|
||||
props.onChange(object);
|
||||
@@ -65,11 +85,11 @@ const ObjectSelect = <T extends ObjectWithNameAndId, >(props: ObjectSelectProps<
|
||||
if (isControlled || !internalValue) return;
|
||||
props.onChange(internalValue);
|
||||
}, [internalValue]);
|
||||
const restProps = omit(props, ['filterBy', 'groupBy']);
|
||||
const restProps = omit(props, ['filterBy', 'groupBy', 'getValueFn', 'getLabelFn']);
|
||||
return (
|
||||
<Select
|
||||
{...restProps}
|
||||
value={value?.id.toString()}
|
||||
value={value && getValueFn(value)}
|
||||
onChange={handleOnChange}
|
||||
data={data}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user