mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-17 04:25:42 +00:00
233 lines
5.2 KiB
TypeScript
233 lines
5.2 KiB
TypeScript
import { Trans } from '@lingui/macro';
|
|
import {
|
|
ActionIcon,
|
|
Container,
|
|
Group,
|
|
Indicator,
|
|
Menu,
|
|
Text
|
|
} from '@mantine/core';
|
|
import { useDisclosure, useHotkeys } from '@mantine/hooks';
|
|
import {
|
|
IconArrowBackUpDouble,
|
|
IconDotsVertical,
|
|
IconLayout2,
|
|
IconSquare,
|
|
IconSquareCheck
|
|
} from '@tabler/icons-react';
|
|
import { useEffect, useState } from 'react';
|
|
import { Responsive, WidthProvider } from 'react-grid-layout';
|
|
|
|
import * as classes from './WidgetLayout.css';
|
|
|
|
const ReactGridLayout = WidthProvider(Responsive);
|
|
|
|
interface LayoutStorage {
|
|
[key: string]: {};
|
|
}
|
|
|
|
const compactType = 'vertical';
|
|
|
|
export interface LayoutItemType {
|
|
i: number;
|
|
val: string | JSX.Element | JSX.Element[] | (() => JSX.Element);
|
|
w?: number;
|
|
h?: number;
|
|
x?: number;
|
|
y?: number;
|
|
minH?: number;
|
|
}
|
|
|
|
export function WidgetLayout({
|
|
items = [],
|
|
className = 'layout',
|
|
localstorageName = 'argl',
|
|
rowHeight = 30
|
|
}: Readonly<{
|
|
items: LayoutItemType[];
|
|
className?: string;
|
|
localstorageName?: string;
|
|
rowHeight?: number;
|
|
}>) {
|
|
const [layouts, setLayouts] = useState({});
|
|
const [editable, setEditable] = useDisclosure(false);
|
|
const [boxShown, setBoxShown] = useDisclosure(true);
|
|
|
|
useEffect(() => {
|
|
let layout = getFromLS('layouts') || [];
|
|
const new_layout = JSON.parse(JSON.stringify(layout));
|
|
setLayouts(new_layout);
|
|
}, []);
|
|
|
|
function getFromLS(key: string) {
|
|
let ls: LayoutStorage = {};
|
|
if (localStorage) {
|
|
try {
|
|
ls = JSON.parse(localStorage.getItem(localstorageName) || '') || {};
|
|
} catch (e) {
|
|
/*Ignore*/
|
|
}
|
|
}
|
|
return ls[key];
|
|
}
|
|
|
|
function saveToLS(key: string, value: any) {
|
|
if (localStorage) {
|
|
localStorage.setItem(
|
|
localstorageName,
|
|
JSON.stringify({
|
|
[key]: value
|
|
})
|
|
);
|
|
}
|
|
}
|
|
|
|
function resetLayout() {
|
|
setLayouts({});
|
|
}
|
|
|
|
function onLayoutChange(layout: any, layouts: any) {
|
|
saveToLS('layouts', layouts);
|
|
setLayouts(layouts);
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<WidgetControlBar
|
|
editable={editable}
|
|
editFnc={setEditable.toggle}
|
|
resetLayout={resetLayout}
|
|
boxShown={boxShown}
|
|
boxFnc={setBoxShown.toggle}
|
|
/>
|
|
{layouts ? (
|
|
<ReactGridLayout
|
|
className={className}
|
|
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
|
|
rowHeight={rowHeight}
|
|
layouts={layouts}
|
|
onLayoutChange={(layout, layouts) => onLayoutChange(layout, layouts)}
|
|
compactType={compactType}
|
|
isDraggable={editable}
|
|
isResizable={editable}
|
|
>
|
|
{items.map((item) => {
|
|
return LayoutItem(item, boxShown, classes);
|
|
})}
|
|
</ReactGridLayout>
|
|
) : (
|
|
<div>
|
|
<Trans>Loading</Trans>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function WidgetControlBar({
|
|
editable,
|
|
editFnc,
|
|
resetLayout,
|
|
boxShown,
|
|
boxFnc
|
|
}: Readonly<{
|
|
editable: boolean;
|
|
editFnc: () => void;
|
|
resetLayout: () => void;
|
|
boxShown: boolean;
|
|
boxFnc: () => void;
|
|
}>) {
|
|
useHotkeys([['mod+E', () => editFnc()]]);
|
|
|
|
return (
|
|
<Group justify="right">
|
|
<Menu
|
|
shadow="md"
|
|
width={200}
|
|
openDelay={100}
|
|
closeDelay={400}
|
|
position="bottom-end"
|
|
>
|
|
<Menu.Target>
|
|
<Indicator
|
|
color="red"
|
|
position="bottom-start"
|
|
processing
|
|
disabled={!editable}
|
|
>
|
|
<ActionIcon variant="transparent">
|
|
<IconDotsVertical />
|
|
</ActionIcon>
|
|
</Indicator>
|
|
</Menu.Target>
|
|
|
|
<Menu.Dropdown>
|
|
<Menu.Label>
|
|
<Trans>Layout</Trans>
|
|
</Menu.Label>
|
|
<Menu.Item
|
|
leftSection={<IconArrowBackUpDouble size={14} />}
|
|
onClick={resetLayout}
|
|
>
|
|
<Trans>Reset Layout</Trans>
|
|
</Menu.Item>
|
|
<Menu.Item
|
|
leftSection={
|
|
<IconLayout2 size={14} color={editable ? 'red' : undefined} />
|
|
}
|
|
onClick={editFnc}
|
|
rightSection={
|
|
<Text size="xs" c="dimmed">
|
|
⌘E
|
|
</Text>
|
|
}
|
|
>
|
|
{editable ? <Trans>Stop Edit</Trans> : <Trans>Edit Layout</Trans>}
|
|
</Menu.Item>
|
|
|
|
<Menu.Divider />
|
|
|
|
<Menu.Label>
|
|
<Trans>Appearance</Trans>
|
|
</Menu.Label>
|
|
<Menu.Item
|
|
leftSection={
|
|
boxShown ? (
|
|
<IconSquareCheck size={14} />
|
|
) : (
|
|
<IconSquare size={14} />
|
|
)
|
|
}
|
|
onClick={boxFnc}
|
|
>
|
|
<Trans>Show Boxes</Trans>
|
|
</Menu.Item>
|
|
</Menu.Dropdown>
|
|
</Menu>
|
|
</Group>
|
|
);
|
|
}
|
|
|
|
function LayoutItem(
|
|
item: any,
|
|
backgroundColor: boolean,
|
|
classes: { backgroundItem: string; baseItem: string }
|
|
) {
|
|
return (
|
|
<Container
|
|
key={item.i}
|
|
data-grid={{
|
|
w: item.w || 3,
|
|
h: item.h || 3,
|
|
x: item.x || 0,
|
|
y: item.y || 0,
|
|
minH: item.minH || undefined,
|
|
minW: item.minW || undefined
|
|
}}
|
|
className={backgroundColor ? classes.backgroundItem : classes.baseItem}
|
|
>
|
|
{item.val}
|
|
</Container>
|
|
);
|
|
}
|