114 lines
3.5 KiB
TypeScript
114 lines
3.5 KiB
TypeScript
/* eslint-disable react-refresh/only-export-components */
|
|
import { type MenuItemProps, type MenuProps } from '@mantine/core';
|
|
import { getUUID } from '@warkypublic/artemis-kit';
|
|
import { createSyncStore } from '@warkypublic/zustandsyncstore';
|
|
import { produce } from 'immer';
|
|
import { type ReactNode } from 'react';
|
|
|
|
export interface MantineBetterMenuInstance {
|
|
id: string;
|
|
items?: Array<MantineBetterMenuInstanceItem>;
|
|
menuProps?: MenuProps;
|
|
renderer?: ReactNode;
|
|
visible: boolean;
|
|
|
|
x: number;
|
|
y: number;
|
|
}
|
|
|
|
export interface MantineBetterMenuInstanceItem extends Partial<MenuItemProps> {
|
|
id?: string;
|
|
isDivider?: boolean;
|
|
items?: Array<MantineBetterMenuInstanceItem>;
|
|
label?: string;
|
|
onClick?: (e?: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
|
onClickAsync?: () => Promise<void>;
|
|
renderer?:
|
|
| ((props: MantineBetterMenuInstanceItem & Record<string, unknown>) => ReactNode)
|
|
| ReactNode;
|
|
}
|
|
|
|
export interface MantineBetterMenuStoreProps {
|
|
menus?: Array<MantineBetterMenuInstance>;
|
|
providerID?: string;
|
|
width?: number;
|
|
}
|
|
|
|
export type MantineBetterMenuStoreState = MantineBetterMenuStoreProps &
|
|
MantineBetterMenuStoreStateOnly;
|
|
|
|
export interface MantineBetterMenuStoreStateOnly {
|
|
hide: (id: string) => void;
|
|
menus: Array<MantineBetterMenuInstance>;
|
|
setInstanceState: <K extends keyof MantineBetterMenuInstance>(
|
|
instanceID: string,
|
|
key: K,
|
|
value: MantineBetterMenuInstance[K]
|
|
) => void;
|
|
setState: <K extends keyof MantineBetterMenuStoreState>(
|
|
key: K,
|
|
value: Partial<MantineBetterMenuStoreState[K]>
|
|
) => void;
|
|
show: (id: string, options?: Partial<MantineBetterMenuInstance>) => void;
|
|
}
|
|
|
|
const { Provider: MantineBetterMenusStoreProvider, useStore: useMantineBetterMenus } =
|
|
createSyncStore<MantineBetterMenuStoreState, MantineBetterMenuStoreProps>(
|
|
(set, get) => ({
|
|
hide: (id: string) => {
|
|
const s = get();
|
|
s.setInstanceState(id, 'visible', false);
|
|
},
|
|
menus: [],
|
|
setInstanceState: (id, key, value) => {
|
|
//@ts-expect-error Type instantiation is excessively deep and possibly infinite.
|
|
set(
|
|
produce((state: MantineBetterMenuStoreState) => {
|
|
const idx = state?.menus?.findIndex((m: MantineBetterMenuInstance) => m.id === id);
|
|
if (idx >= 0) {
|
|
state.menus[idx][key] = value;
|
|
}
|
|
})
|
|
);
|
|
},
|
|
setState: (key, value) => {
|
|
set(
|
|
produce((state) => {
|
|
state[key] = value;
|
|
})
|
|
);
|
|
},
|
|
show: (id: string, options?: Partial<Omit<MantineBetterMenuInstance, 'id'>>) => {
|
|
const s = get();
|
|
const menuIndex = s.menus.findIndex((m) => m.id === id);
|
|
const menu: Partial<MantineBetterMenuInstance> = s.menus[menuIndex]
|
|
? { ...s.menus[menuIndex] }
|
|
: {};
|
|
|
|
Object.assign(menu, options);
|
|
menu.id = menu.id ?? id;
|
|
menu.visible = !(menu.visible ?? false);
|
|
|
|
if (menuIndex < 0) {
|
|
s.setState('menus', [...s.menus, menu as MantineBetterMenuInstance]);
|
|
} else {
|
|
set(
|
|
produce((state: MantineBetterMenuStoreState) => {
|
|
if (!state.menus) {
|
|
state.menus = [];
|
|
}
|
|
state.menus[menuIndex] = { ...state.menus[menuIndex], ...menu };
|
|
})
|
|
);
|
|
}
|
|
},
|
|
}),
|
|
(props) => {
|
|
return {
|
|
providerID: props.providerID ?? `MenuStore-${getUUID()}`,
|
|
};
|
|
}
|
|
);
|
|
|
|
export { MantineBetterMenusStoreProvider, useMantineBetterMenus };
|