mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-10 15:35:55 +01:00
✨ Implemented different layout sizes
This commit is contained in:
@@ -17,84 +17,6 @@
|
||||
}
|
||||
],
|
||||
"apps": [
|
||||
{
|
||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a",
|
||||
"name": "Documentation",
|
||||
"url": "https://homarr.dev",
|
||||
"behaviour": {
|
||||
"onClickUrl": "https://homarr.dev",
|
||||
"externalUrl": "https://homarr.dev",
|
||||
"isOpeningNewTab": true
|
||||
},
|
||||
"network": {
|
||||
"enabledStatusChecker": false,
|
||||
"okStatus": [
|
||||
200
|
||||
]
|
||||
},
|
||||
"appearance": {
|
||||
"iconUrl": "/imgs/logo/logo.png"
|
||||
},
|
||||
"integration": {
|
||||
"type": null,
|
||||
"properties": []
|
||||
},
|
||||
"area": {
|
||||
"type": "wrapper",
|
||||
"properties": {
|
||||
"id": "default"
|
||||
}
|
||||
},
|
||||
"shape": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 11
|
||||
},
|
||||
"size": {
|
||||
"width": 4,
|
||||
"height": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990",
|
||||
"name": "Donate",
|
||||
"url": "https://ko-fi.com/ajnart",
|
||||
"behaviour": {
|
||||
"onClickUrl": "https://ko-fi.com/ajnart",
|
||||
"externalUrl": "https://ko-fi.com/ajnart",
|
||||
"isOpeningNewTab": true
|
||||
},
|
||||
"network": {
|
||||
"enabledStatusChecker": false,
|
||||
"okStatus": [
|
||||
200
|
||||
]
|
||||
},
|
||||
"appearance": {
|
||||
"iconUrl": "https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/61e1116779fc0a9bd5bdbcc7_Frame%206.png"
|
||||
},
|
||||
"integration": {
|
||||
"type": null,
|
||||
"properties": []
|
||||
},
|
||||
"area": {
|
||||
"type": "category",
|
||||
"properties": {
|
||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f"
|
||||
}
|
||||
},
|
||||
"shape": {
|
||||
"location": {
|
||||
"x": 6,
|
||||
"y": 1
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "76217a87-7151-42d0-b0cf-1b72aef63f83",
|
||||
"name": "Small app",
|
||||
@@ -117,13 +39,35 @@
|
||||
}
|
||||
},
|
||||
"shape": {
|
||||
"location": {
|
||||
"x": 4,
|
||||
"y": 0
|
||||
"md": {
|
||||
"location": {
|
||||
"x": 1,
|
||||
"y": 1
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
"sm": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 3
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"lg": {
|
||||
"location": {
|
||||
"x": 4,
|
||||
"y": 0
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"integration": {
|
||||
@@ -160,13 +104,35 @@
|
||||
}
|
||||
},
|
||||
"shape": {
|
||||
"location": {
|
||||
"x": 5,
|
||||
"y": 2
|
||||
"md": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 1
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 2
|
||||
"sm": {
|
||||
"location": {
|
||||
"x": 1,
|
||||
"y": 2
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"lg": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"size": {
|
||||
"width": 4,
|
||||
"height": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -199,50 +165,158 @@
|
||||
}
|
||||
},
|
||||
"shape": {
|
||||
"location": {
|
||||
"x": 7,
|
||||
"y": 0
|
||||
"md": {
|
||||
"location": {
|
||||
"x": 3,
|
||||
"y": 1
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
"sm": {
|
||||
"location": {
|
||||
"x": 1,
|
||||
"y": 4
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"lg": {
|
||||
"location": {
|
||||
"x": 4,
|
||||
"y": 2
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "5df743d9-5cb1-457c-85d2-64ff86855652",
|
||||
"name": "Your app",
|
||||
"url": "https://homarr.dev",
|
||||
"appearance": {
|
||||
"iconUrl": "/imgs/logo/logo.png"
|
||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990",
|
||||
"name": "Donate",
|
||||
"url": "https://ko-fi.com/ajnart",
|
||||
"behaviour": {
|
||||
"onClickUrl": "https://ko-fi.com/ajnart",
|
||||
"externalUrl": "https://ko-fi.com/ajnart",
|
||||
"isOpeningNewTab": true
|
||||
},
|
||||
"network": {
|
||||
"enabledStatusChecker": false,
|
||||
"okStatus": []
|
||||
"okStatus": [
|
||||
200
|
||||
]
|
||||
},
|
||||
"behaviour": {
|
||||
"isOpeningNewTab": true,
|
||||
"externalUrl": "https://homarr.dev"
|
||||
},
|
||||
"area": {
|
||||
"type": "wrapper",
|
||||
"properties": {
|
||||
"id": "default"
|
||||
}
|
||||
},
|
||||
"shape": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 7
|
||||
},
|
||||
"size": {
|
||||
"width": 6,
|
||||
"height": 4
|
||||
}
|
||||
"appearance": {
|
||||
"iconUrl": "https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/61e1116779fc0a9bd5bdbcc7_Frame%206.png"
|
||||
},
|
||||
"integration": {
|
||||
"type": null,
|
||||
"properties": []
|
||||
},
|
||||
"area": {
|
||||
"type": "category",
|
||||
"properties": {
|
||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f"
|
||||
}
|
||||
},
|
||||
"shape": {
|
||||
"md": {
|
||||
"location": {
|
||||
"x": 2,
|
||||
"y": 0
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"sm": {
|
||||
"location": {
|
||||
"x": 2,
|
||||
"y": 0
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"lg": {
|
||||
"location": {
|
||||
"x": 5,
|
||||
"y": 0
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a",
|
||||
"name": "Documentation",
|
||||
"url": "https://homarr.dev",
|
||||
"behaviour": {
|
||||
"onClickUrl": "https://homarr.dev",
|
||||
"externalUrl": "https://homarr.dev",
|
||||
"isOpeningNewTab": true
|
||||
},
|
||||
"network": {
|
||||
"enabledStatusChecker": false,
|
||||
"okStatus": [
|
||||
200
|
||||
]
|
||||
},
|
||||
"appearance": {
|
||||
"iconUrl": "/imgs/logo/logo.png"
|
||||
},
|
||||
"integration": {
|
||||
"type": null,
|
||||
"properties": []
|
||||
},
|
||||
"area": {
|
||||
"type": "category",
|
||||
"properties": {
|
||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f"
|
||||
}
|
||||
},
|
||||
"shape": {
|
||||
"md": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 10
|
||||
},
|
||||
"size": {
|
||||
"width": 2,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"sm": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 10
|
||||
},
|
||||
"size": {
|
||||
"width": 2,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"lg": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 10
|
||||
},
|
||||
"size": {
|
||||
"width": 2,
|
||||
"height": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -267,13 +341,93 @@
|
||||
}
|
||||
},
|
||||
"shape": {
|
||||
"location": {
|
||||
"x": 8,
|
||||
"y": 9
|
||||
"md": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 6
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
"width": 4,
|
||||
"height": 2
|
||||
"sm": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 6
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"lg": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 6
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"integration": {
|
||||
"type": null,
|
||||
"properties": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "5df743d9-5cb1-457c-85d2-64ff86855652",
|
||||
"name": "Your app",
|
||||
"url": "https://homarr.dev",
|
||||
"appearance": {
|
||||
"iconUrl": "/imgs/logo/logo.png"
|
||||
},
|
||||
"network": {
|
||||
"enabledStatusChecker": false,
|
||||
"okStatus": []
|
||||
},
|
||||
"behaviour": {
|
||||
"isOpeningNewTab": true,
|
||||
"externalUrl": "https://homarr.dev"
|
||||
},
|
||||
"area": {
|
||||
"type": "category",
|
||||
"properties": {
|
||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f"
|
||||
}
|
||||
},
|
||||
"shape": {
|
||||
"md": {
|
||||
"location": {
|
||||
"x": 3,
|
||||
"y": 0
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"sm": {
|
||||
"location": {
|
||||
"x": 2,
|
||||
"y": 3
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"lg": {
|
||||
"location": {
|
||||
"x": 6,
|
||||
"y": 0
|
||||
},
|
||||
"size": {
|
||||
"width": 1,
|
||||
"height": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"integration": {
|
||||
@@ -295,13 +449,35 @@
|
||||
}
|
||||
},
|
||||
"shape": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 0
|
||||
"md": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 1
|
||||
},
|
||||
"size": {
|
||||
"width": 3,
|
||||
"height": 5
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
"width": 3,
|
||||
"height": 5
|
||||
"sm": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"size": {
|
||||
"width": 3,
|
||||
"height": 5
|
||||
}
|
||||
},
|
||||
"lg": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"size": {
|
||||
"width": 3,
|
||||
"height": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -318,13 +494,35 @@
|
||||
}
|
||||
},
|
||||
"shape": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 0
|
||||
"md": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"size": {
|
||||
"width": 3,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
"width": 3,
|
||||
"height": 2
|
||||
"sm": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"size": {
|
||||
"width": 2,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"lg": {
|
||||
"location": {
|
||||
"x": 4,
|
||||
"y": 1
|
||||
},
|
||||
"size": {
|
||||
"width": 3,
|
||||
"height": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -340,13 +538,35 @@
|
||||
}
|
||||
},
|
||||
"shape": {
|
||||
"location": {
|
||||
"x": 9,
|
||||
"y": 0
|
||||
"sm": {
|
||||
"location": {
|
||||
"x": 0,
|
||||
"y": 1
|
||||
},
|
||||
"size": {
|
||||
"width": 3,
|
||||
"height": 1
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
"width": 3,
|
||||
"height": 2
|
||||
"md": {
|
||||
"location": {
|
||||
"x": 4,
|
||||
"y": 0
|
||||
},
|
||||
"size": {
|
||||
"width": 2,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
"lg": {
|
||||
"location": {
|
||||
"x": 5,
|
||||
"y": 2
|
||||
},
|
||||
"size": {
|
||||
"width": 2,
|
||||
"height": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -360,8 +580,8 @@
|
||||
},
|
||||
"customization": {
|
||||
"layout": {
|
||||
"enabledLeftSidebar": false,
|
||||
"enabledRightSidebar": false,
|
||||
"enabledLeftSidebar": true,
|
||||
"enabledRightSidebar": true,
|
||||
"enabledDocker": false,
|
||||
"enabledPing": false,
|
||||
"enabledSearchbar": true
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useConfigContext } from '../../../../config/provider';
|
||||
import { useConfigStore } from '../../../../config/store';
|
||||
import widgets from '../../../../widgets';
|
||||
import { WidgetChangePositionModalInnerProps } from '../../Tiles/Widgets/WidgetsMenu';
|
||||
import { useGridstackStore } from '../../Wrappers/gridstack/store';
|
||||
import { ChangePositionModal } from './ChangePositionModal';
|
||||
|
||||
export const ChangeWidgetPositionModal = ({
|
||||
@@ -13,6 +14,7 @@ export const ChangeWidgetPositionModal = ({
|
||||
}: ContextModalProps<WidgetChangePositionModalInnerProps>) => {
|
||||
const { name: configName } = useConfigContext();
|
||||
const updateConfig = useConfigStore((x) => x.updateConfig);
|
||||
const shapeSize = useGridstackStore(x => x.currentShapeSize);
|
||||
|
||||
const handleSubmit = (x: number, y: number, width: number, height: number) => {
|
||||
if (!configName) {
|
||||
@@ -23,7 +25,7 @@ export const ChangeWidgetPositionModal = ({
|
||||
configName,
|
||||
(prev) => {
|
||||
const currentWidget = prev.widgets.find((x) => x.id === innerProps.widgetId);
|
||||
currentWidget!.shape = {
|
||||
currentWidget!.shape[shapeSize] = {
|
||||
location: {
|
||||
x,
|
||||
y,
|
||||
@@ -57,10 +59,10 @@ export const ChangeWidgetPositionModal = ({
|
||||
onCancel={handleCancel}
|
||||
heightData={heightData}
|
||||
widthData={widthData}
|
||||
initialX={innerProps.widget.shape.location.x}
|
||||
initialY={innerProps.widget.shape.location.y}
|
||||
initialWidth={innerProps.widget.shape.size.width}
|
||||
initialHeight={innerProps.widget.shape.size.height}
|
||||
initialX={innerProps.widget.shape[shapeSize].location.x}
|
||||
initialY={innerProps.widget.shape[shapeSize].location.y}
|
||||
initialWidth={innerProps.widget.shape[shapeSize].size.width}
|
||||
initialHeight={innerProps.widget.shape[shapeSize].size.height}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -46,13 +46,35 @@ export const WidgetElementType = ({ id, image, disabled, widget }: WidgetElement
|
||||
},
|
||||
},
|
||||
shape: {
|
||||
location: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
sm: {
|
||||
location: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
size: {
|
||||
width: widget.gridstack.minWidth,
|
||||
height: widget.gridstack.minHeight,
|
||||
},
|
||||
},
|
||||
size: {
|
||||
width: widget.gridstack.minWidth,
|
||||
height: widget.gridstack.minHeight,
|
||||
md: {
|
||||
location: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
size: {
|
||||
width: widget.gridstack.minWidth,
|
||||
height: widget.gridstack.minHeight,
|
||||
},
|
||||
},
|
||||
lg: {
|
||||
location: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
size: {
|
||||
width: widget.gridstack.minWidth,
|
||||
height: widget.gridstack.minHeight,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Title } from '@mantine/core';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { openContextModalGeneric } from '../../../../tools/mantineModalManagerExtensions';
|
||||
import { IWidget } from '../../../../widgets/widgets';
|
||||
import { useWrapperColumnCount } from '../../Wrappers/gridstack/store';
|
||||
import { GenericTileMenu } from '../GenericTileMenu';
|
||||
import { WidgetEditModalInnerProps } from './WidgetsEditModal';
|
||||
import { WidgetsRemoveModalInnerProps } from './WidgetsRemoveModal';
|
||||
@@ -9,6 +10,7 @@ import { WidgetsRemoveModalInnerProps } from './WidgetsRemoveModal';
|
||||
export type WidgetChangePositionModalInnerProps = {
|
||||
widgetId: string;
|
||||
widget: IWidget<string, any>;
|
||||
wrapperColumnCount: number;
|
||||
};
|
||||
|
||||
interface WidgetsMenuProps {
|
||||
@@ -18,8 +20,9 @@ interface WidgetsMenuProps {
|
||||
|
||||
export const WidgetsMenu = ({ integration, widget }: WidgetsMenuProps) => {
|
||||
const { t } = useTranslation(`modules/${integration}`);
|
||||
const wrapperColumnCount = useWrapperColumnCount();
|
||||
|
||||
if (!widget) return null;
|
||||
if (!widget || !wrapperColumnCount) return null;
|
||||
|
||||
const handleDeleteClick = () => {
|
||||
openContextModalGeneric<WidgetsRemoveModalInnerProps>({
|
||||
@@ -39,6 +42,7 @@ export const WidgetsMenu = ({ integration, widget }: WidgetsMenuProps) => {
|
||||
innerProps: {
|
||||
widgetId: integration,
|
||||
widget,
|
||||
wrapperColumnCount,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { Group, Stack } from '@mantine/core';
|
||||
import { useMemo } from 'react';
|
||||
import { Center, Group, Loader, Stack } from '@mantine/core';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { useConfigContext } from '../../../config/provider';
|
||||
import { useResize } from '../../../hooks/use-resize';
|
||||
import { useScreenSmallerThan } from '../../../hooks/useScreenSmallerThan';
|
||||
import { CategoryType } from '../../../types/category';
|
||||
import { WrapperType } from '../../../types/wrapper';
|
||||
import { DashboardCategory } from '../Wrappers/Category/Category';
|
||||
import { useGridstackStore } from '../Wrappers/gridstack/store';
|
||||
import { DashboardSidebar } from '../Wrappers/Sidebar/Sidebar';
|
||||
import { DashboardWrapper } from '../Wrappers/Wrapper/Wrapper';
|
||||
|
||||
@@ -12,14 +14,26 @@ export const DashboardView = () => {
|
||||
const wrappers = useWrapperItems();
|
||||
const layoutSettings = useConfigContext()?.config?.settings.customization.layout;
|
||||
const doNotShowSidebar = useScreenSmallerThan('md');
|
||||
const notReady = typeof doNotShowSidebar === 'undefined';
|
||||
const mainAreaRef = useRef<HTMLDivElement>(null);
|
||||
const { width } = useResize(mainAreaRef, [doNotShowSidebar]);
|
||||
const setMainAreaWidth = useGridstackStore(x => x.setMainAreaWidth);
|
||||
const mainAreaWidth = useGridstackStore(x => x.mainAreaWidth);
|
||||
|
||||
useEffect(() => {
|
||||
setMainAreaWidth(width);
|
||||
}, [width]);
|
||||
|
||||
return (
|
||||
<Group align="top" h="100%">
|
||||
{layoutSettings?.enabledLeftSidebar && !doNotShowSidebar ? (
|
||||
{notReady ? <Center w="100%">
|
||||
<Loader />
|
||||
</Center> : <>
|
||||
{layoutSettings?.enabledLeftSidebar && !doNotShowSidebar && mainAreaWidth ? (
|
||||
<DashboardSidebar location="left" />
|
||||
) : null}
|
||||
<Stack mx={-10} style={{ flexGrow: 1 }}>
|
||||
{wrappers.map((item) =>
|
||||
<Stack ref={mainAreaRef} mx={-10} style={{ flexGrow: 1 }}>
|
||||
{!mainAreaWidth ? null : wrappers.map((item) =>
|
||||
item.type === 'category' ? (
|
||||
<DashboardCategory key={item.id} category={item as unknown as CategoryType} />
|
||||
) : (
|
||||
@@ -27,9 +41,11 @@ export const DashboardView = () => {
|
||||
)
|
||||
)}
|
||||
</Stack>
|
||||
{layoutSettings?.enabledRightSidebar && !doNotShowSidebar ? (
|
||||
{layoutSettings?.enabledRightSidebar && !doNotShowSidebar && mainAreaWidth ? (
|
||||
<DashboardSidebar location="right" />
|
||||
) : null}
|
||||
</>
|
||||
}
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -22,7 +22,6 @@ export const DashboardCategory = ({ category }: DashboardCategoryProps) => {
|
||||
</Group>
|
||||
<div
|
||||
className="grid-stack grid-stack-category"
|
||||
style={{ transitionDuration: '0s' }}
|
||||
data-category={category.id}
|
||||
ref={refs.wrapper}
|
||||
>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { IWidget, IWidgetDefinition } from '../../../widgets/widgets';
|
||||
import { WidgetWrapper } from '../../../widgets/WidgetWrapper';
|
||||
import { appTileDefinition } from '../Tiles/Apps/AppTile';
|
||||
import { GridstackTileWrapper } from '../Tiles/TileWrapper';
|
||||
import { useGridstackStore } from './gridstack/store';
|
||||
|
||||
interface WrapperContentProps {
|
||||
apps: AppType[];
|
||||
@@ -18,6 +19,10 @@ interface WrapperContentProps {
|
||||
}
|
||||
|
||||
export function WrapperContent({ apps, refs, widgets }: WrapperContentProps) {
|
||||
const shapeSize = useGridstackStore(x => x.currentShapeSize);
|
||||
|
||||
if (!shapeSize) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{apps?.map((app) => {
|
||||
@@ -29,8 +34,8 @@ export function WrapperContent({ apps, refs, widgets }: WrapperContentProps) {
|
||||
key={app.id}
|
||||
itemRef={refs.items.current[app.id]}
|
||||
{...tile}
|
||||
{...app.shape.location}
|
||||
{...app.shape.size}
|
||||
{...app.shape[shapeSize]?.location}
|
||||
{...app.shape[shapeSize]?.size}
|
||||
>
|
||||
<TileComponent className="grid-stack-item-content" app={app} />
|
||||
</GridstackTileWrapper>
|
||||
@@ -49,8 +54,8 @@ export function WrapperContent({ apps, refs, widgets }: WrapperContentProps) {
|
||||
itemRef={refs.items.current[widget.id]}
|
||||
id={definition.id}
|
||||
{...definition.gridstack}
|
||||
{...widget.shape.location}
|
||||
{...widget.shape.size}
|
||||
{...widget.shape[shapeSize]?.location}
|
||||
{...widget.shape[shapeSize]?.size}
|
||||
>
|
||||
<WidgetWrapper className="grid-stack-item-content" widget={widget} widgetId={widget.id}>
|
||||
<definition.component className="grid-stack-item-content" widget={widget} />
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
import { GridStackNode } from 'fily-publish-gridstack';
|
||||
|
||||
type GridstackColumnSortingFn = (
|
||||
column: number,
|
||||
prevColumn: number,
|
||||
newNodes: GridStackNode[],
|
||||
nodes: GridStackNode[]
|
||||
) => void;
|
||||
|
||||
const getGridstackAttribute = (node: GridStackNode, path: 'x' | 'y' | 'w' | 'h'): number =>
|
||||
parseInt(node.el!.getAttribute(`data-gridstack-${path}`)!, 10);
|
||||
|
||||
const getGridstackAttributes = (node: GridStackNode) => ({
|
||||
width: getGridstackAttribute(node, 'w'),
|
||||
height: getGridstackAttribute(node, 'h'),
|
||||
x: getGridstackAttribute(node, 'x'),
|
||||
y: getGridstackAttribute(node, 'y'),
|
||||
});
|
||||
|
||||
type Type = ReturnType<typeof getGridstackAttributes> & { node: GridStackNode };
|
||||
|
||||
const nextItem = (start: number, end: number, nodes: Type[]): number => {
|
||||
const next = nodes
|
||||
.filter((x) => x.y <= end && x.y + x.height - 1 > end)
|
||||
.sort((a, b) => a.y + a.height - (b.y + b.height))
|
||||
.at(0);
|
||||
if (!next) return end;
|
||||
return nextItem(start, next.height - 1 + next.y, nodes);
|
||||
};
|
||||
|
||||
const nextRowHeight = (
|
||||
nodes: Type[],
|
||||
values: { height: number; items: Type[] }[],
|
||||
maxHeight: number,
|
||||
current = 0
|
||||
) => {
|
||||
const item = nodes.find((x) => x.y >= current);
|
||||
if (!item) return;
|
||||
if (current < item.y) {
|
||||
values.push({ height: item.y - current, items: [] });
|
||||
}
|
||||
const next = nextItem(item.y, item.y + item.height - 1, nodes);
|
||||
values.push({
|
||||
height: next + 1 - item.y,
|
||||
items: nodes.filter((x) => x.y >= current - 2 && x.y + x.height <= current + next + 1 - item.y),
|
||||
});
|
||||
nextRowHeight(nodes, values, maxHeight, next + 1);
|
||||
};
|
||||
|
||||
const getRowHeights = (nodes: Type[]) => {
|
||||
const maxHeightElement = nodes.sort((a, b) => a.y + a.height - (b.y + b.height)).at(-1);
|
||||
if (!maxHeightElement) return [];
|
||||
const maxHeight = maxHeightElement.height + maxHeightElement.y;
|
||||
const rowHeights: { height: number; items: Type[] }[] = [];
|
||||
nextRowHeight(nodes, rowHeights, maxHeight);
|
||||
return rowHeights;
|
||||
};
|
||||
|
||||
const sortNodesByYAndX = (a: GridStackNode, b: GridStackNode) => {
|
||||
const aAttributes = getGridstackAttributes(a);
|
||||
const bAttributes = getGridstackAttributes(b);
|
||||
|
||||
const differenceY = aAttributes.y - bAttributes.y;
|
||||
|
||||
return differenceY !== 0 ? differenceY : aAttributes.x - bAttributes.x;
|
||||
};
|
||||
|
||||
export const commonColumnSorting: GridstackColumnSortingFn = (
|
||||
column,
|
||||
prevColumn,
|
||||
newNodes,
|
||||
nodes
|
||||
) => {
|
||||
if (column === prevColumn) {
|
||||
newNodes.concat(nodes);
|
||||
return;
|
||||
}
|
||||
|
||||
let nextRow = 0;
|
||||
let available = column;
|
||||
|
||||
const sortedNodes = nodes.sort(sortNodesByYAndX);
|
||||
const mappedNodes = sortedNodes.map((node) => ({
|
||||
...getGridstackAttributes(node),
|
||||
node,
|
||||
}));
|
||||
const rowHeights = getRowHeights(mappedNodes);
|
||||
|
||||
const rowItems: Type[][] = [];
|
||||
|
||||
// TODO: fix issue with spaces between.
|
||||
let rowTotal = 0;
|
||||
rowHeights.forEach(({ height }) => {
|
||||
rowItems.push(mappedNodes.filter((node) => node.y >= rowTotal && node.y < rowTotal + height));
|
||||
rowTotal += height;
|
||||
});
|
||||
|
||||
console.log(rowHeights);
|
||||
};
|
||||
@@ -20,7 +20,7 @@ export const initializeGridstack = (
|
||||
) => {
|
||||
if (!wrapperRef.current) return;
|
||||
// calculates the currently available count of columns
|
||||
const columnCount = areaType === 'sidebar' ? 4 : wrapperColumnCount;
|
||||
const columnCount = areaType === 'sidebar' ? 2 : wrapperColumnCount;
|
||||
const minRow = areaType !== 'sidebar' ? 1 : Math.floor(wrapperRef.current.offsetHeight / 64);
|
||||
// initialize gridstack
|
||||
const newGrid = gridRef;
|
||||
@@ -35,11 +35,14 @@ export const initializeGridstack = (
|
||||
disableOneColumnMode: true,
|
||||
staticGrid: !isEditMode,
|
||||
minRow,
|
||||
animate: false,
|
||||
},
|
||||
// selector of the gridstack item (it's eather category or wrapper)
|
||||
`.grid-stack-${areaType}[data-${areaType}='${areaId}']`
|
||||
);
|
||||
const grid = newGrid.current;
|
||||
// Must be used to update the column count after the initialization
|
||||
grid.column(columnCount);
|
||||
|
||||
// Add listener for moving items around in a wrapper
|
||||
grid.on('change', (_, el) => {
|
||||
@@ -60,12 +63,16 @@ export const initializeGridstack = (
|
||||
grid.batchUpdate();
|
||||
grid.removeAll(false);
|
||||
items.forEach(
|
||||
({ id }) =>
|
||||
itemRefs.current[id] && grid.makeWidget(itemRefs.current[id].current as HTMLDivElement)
|
||||
({ id }) => {
|
||||
const item = itemRefs.current[id]?.current;
|
||||
item && grid.makeWidget(item as HTMLDivElement);
|
||||
}
|
||||
);
|
||||
widgets.forEach(
|
||||
({ id }) =>
|
||||
itemRefs.current[id] && grid.makeWidget(itemRefs.current[id].current as HTMLDivElement)
|
||||
({ id }) => {
|
||||
const item = itemRefs.current[id]?.current;
|
||||
item && grid.makeWidget(item as HTMLDivElement);
|
||||
}
|
||||
);
|
||||
grid.batchUpdate(false);
|
||||
};
|
||||
|
||||
31
src/components/Dashboard/Wrappers/gridstack/store.tsx
Normal file
31
src/components/Dashboard/Wrappers/gridstack/store.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { useMantineTheme } from '@mantine/core';
|
||||
import create from 'zustand';
|
||||
|
||||
export const useGridstackStore = create<GridstackStoreType>((set, get) => ({
|
||||
mainAreaWidth: null,
|
||||
currentShapeSize: null,
|
||||
setMainAreaWidth: (w: number) =>
|
||||
set((v) => ({ ...v, mainAreaWidth: w, currentShapeSize: getCurrentShapeSize(w) })),
|
||||
}));
|
||||
|
||||
interface GridstackStoreType {
|
||||
mainAreaWidth: null | number;
|
||||
currentShapeSize: null | 'sm' | 'md' | 'lg';
|
||||
setMainAreaWidth: (width: number) => void;
|
||||
}
|
||||
|
||||
export const useWrapperColumnCount = () => {
|
||||
const mainAreaWidth = useGridstackStore((x) => x.mainAreaWidth);
|
||||
const { sm, xl } = useMantineTheme().breakpoints;
|
||||
if (!mainAreaWidth) return null;
|
||||
|
||||
if (mainAreaWidth >= xl) return 12;
|
||||
|
||||
if (mainAreaWidth >= sm) return 6;
|
||||
|
||||
return 3;
|
||||
};
|
||||
|
||||
function getCurrentShapeSize(size: number) {
|
||||
return size >= 1400 ? 'lg' : size >= 768 ? 'md' : 'sm';
|
||||
}
|
||||
@@ -3,58 +3,17 @@ import {
|
||||
createRef,
|
||||
MutableRefObject,
|
||||
RefObject,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useEffect, useMemo,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { useConfigContext } from '../../../../config/provider';
|
||||
import { useConfigStore } from '../../../../config/store';
|
||||
import { useResize } from '../../../../hooks/use-resize';
|
||||
import { useScreenLargerThan } from '../../../../hooks/useScreenLargerThan';
|
||||
import { AppType } from '../../../../types/app';
|
||||
import { AreaType } from '../../../../types/area';
|
||||
import { IWidget } from '../../../../widgets/widgets';
|
||||
import { useEditModeStore } from '../../Views/useEditModeStore';
|
||||
import { commonColumnSorting } from './column-sorting';
|
||||
import { initializeGridstack } from './init-gridstack';
|
||||
|
||||
const getGridstackAttribute = (node: GridStackNode, path: 'x' | 'y' | 'w' | 'h'): number => parseInt(node.el!.getAttribute(`data-gridstack-${path}`)!, 10);
|
||||
|
||||
const getGridstackAttributes = (node: GridStackNode) => ({
|
||||
width: getGridstackAttribute(node, 'w'),
|
||||
height: getGridstackAttribute(node, 'h'),
|
||||
x: getGridstackAttribute(node, 'x'),
|
||||
y: getGridstackAttribute(node, 'y'),
|
||||
});
|
||||
|
||||
type Type = (ReturnType<typeof getGridstackAttributes> & { node: GridStackNode });
|
||||
|
||||
const nextItem = (start: number, end: number, nodes: Type[]): number => {
|
||||
const next = nodes
|
||||
.filter(x => x.y <= end && x.y + x.height - 1 > end)
|
||||
.sort((a, b) => (a.y + a.height) - (b.y + b.height))
|
||||
.at(0);
|
||||
if (!next) return end;
|
||||
return nextItem(start, next.height - 1 + next.y, nodes);
|
||||
};
|
||||
|
||||
const nextRowHeight = (nodes: Type[], values: number[], current = 0) => {
|
||||
const item = nodes.find(x => x.y >= current);
|
||||
if (!item) return;
|
||||
const next = nextItem(item.y, item.y + item.height - 1, nodes);
|
||||
values.push(next + 1 - item.y);
|
||||
nextRowHeight(nodes, values, next);
|
||||
};
|
||||
|
||||
const getRowHeights = (nodes: GridStackNode[]) => {
|
||||
const rowHeights: number[] = [];
|
||||
nextRowHeight(nodes.map((node) => ({
|
||||
...getGridstackAttributes(node),
|
||||
node,
|
||||
})), rowHeights);
|
||||
return rowHeights;
|
||||
};
|
||||
import { useGridstackStore, useWrapperColumnCount } from './store';
|
||||
|
||||
interface UseGristackReturnType {
|
||||
apps: AppType[];
|
||||
@@ -66,18 +25,10 @@ interface UseGristackReturnType {
|
||||
};
|
||||
}
|
||||
|
||||
const useWrapperColumnCount = () => {
|
||||
const isLargerThanSm = useScreenLargerThan('sm');
|
||||
const isLargerThanXl = useScreenLargerThan('xl');
|
||||
|
||||
return typeof isLargerThanXl === 'undefined' || isLargerThanXl ? 12 : isLargerThanSm ? 6 : 3;
|
||||
};
|
||||
|
||||
export const useGridstack = (
|
||||
areaType: 'wrapper' | 'category' | 'sidebar',
|
||||
areaId: string
|
||||
): UseGristackReturnType => {
|
||||
const wrapperColumnCount = useWrapperColumnCount();
|
||||
const isEditMode = useEditModeStore((x) => x.enabled);
|
||||
const { config, configVersion, name: configName } = useConfigContext();
|
||||
const updateConfig = useConfigStore((x) => x.updateConfig);
|
||||
@@ -87,10 +38,14 @@ export const useGridstack = (
|
||||
const itemRefs = useRef<Record<string, RefObject<HTMLDivElement>>>({});
|
||||
// reference of the gridstack object for modifications after initialization
|
||||
const gridRef = useRef<GridStack>();
|
||||
const wrapperColumnCount = useWrapperColumnCount();
|
||||
const shapeSize = useGridstackStore(x => x.currentShapeSize);
|
||||
const mainAreaWidth = useGridstackStore(x => x.mainAreaWidth);
|
||||
// width of the wrapper (updating on page resize)
|
||||
const { width } = useResize(wrapperRef);
|
||||
const root: HTMLHtmlElement = useMemo(() => document.querySelector(':root')!, []);
|
||||
|
||||
if (!mainAreaWidth || !shapeSize || !wrapperColumnCount) throw new Error('UseGridstack should not be executed before mainAreaWidth has been set!');
|
||||
|
||||
const items = useMemo(
|
||||
() =>
|
||||
config?.apps.filter(
|
||||
@@ -123,99 +78,14 @@ export const useGridstack = (
|
||||
});
|
||||
}
|
||||
|
||||
// change column count depending on the width and the gridRef
|
||||
useEffect(() => {
|
||||
if (areaType === 'sidebar') return;
|
||||
gridRef.current?.column(
|
||||
wrapperColumnCount,
|
||||
/*(column, prevColumn, newNodes, nodes) => {
|
||||
let nextRow = 0;
|
||||
let available = column;
|
||||
let maxHeightInRow = 1;
|
||||
|
||||
if (column === prevColumn) {
|
||||
newNodes.concat(nodes);
|
||||
return;
|
||||
}
|
||||
|
||||
const sortNodes = (a: GridStackNode, b: GridStackNode) => {
|
||||
const aAttributes = getGridstackAttributes(a);
|
||||
const bAttributes = getGridstackAttributes(b);
|
||||
|
||||
const differenceY = aAttributes.y - bAttributes.y;
|
||||
|
||||
return differenceY !== 0 ? differenceY : aAttributes.x - bAttributes.x;
|
||||
};
|
||||
|
||||
const sortedNodes = nodes.sort(sortNodes);
|
||||
const rowHeights = getRowHeights(sortedNodes);
|
||||
|
||||
sortedNodes.forEach((node) => {
|
||||
const newnode = node;
|
||||
const width = parseInt(newnode.el!.getAttribute('data-gridstack-w')!, 10);
|
||||
const height = parseInt(newnode.el!.getAttribute('data-gridstack-h')!, 10);
|
||||
const x = parseInt(newnode.el!.getAttribute('data-gridstack-x')!, 10);
|
||||
const y = parseInt(newnode.el!.getAttribute('data-gridstack-y')!, 10);
|
||||
maxHeightInRow = height > maxHeightInRow ? height : maxHeightInRow;
|
||||
|
||||
const continueInNextRow = () => {
|
||||
nextRow += maxHeightInRow;
|
||||
maxHeightInRow = 1;
|
||||
available = column;
|
||||
return nextRow;
|
||||
};
|
||||
|
||||
if (column === 3) {
|
||||
newnode.x = available >= width ? 3 - available : 0;
|
||||
newnode.y = available === 3 || available >= width ? nextRow : continueInNextRow();
|
||||
|
||||
if (width > 3) {
|
||||
newnode.w = 3;
|
||||
continueInNextRow();
|
||||
} else if (available >= width) {
|
||||
available -= width;
|
||||
if (available === 0) {
|
||||
continueInNextRow();
|
||||
}
|
||||
} else if (available < width) {
|
||||
newnode.y = continueInNextRow();
|
||||
available = 3 - width;
|
||||
}
|
||||
} else if (column === 6) {
|
||||
newnode.x = available >= width ? 6 - available : 0;
|
||||
newnode.y = nextRow;
|
||||
|
||||
if (width > 6) {
|
||||
newnode.w = 6;
|
||||
continueInNextRow();
|
||||
} else if (available >= width) {
|
||||
available -= width;
|
||||
if (available === 0) {
|
||||
continueInNextRow();
|
||||
}
|
||||
} else if (available < width) {
|
||||
newnode.y = continueInNextRow();
|
||||
available = 6 - width;
|
||||
}
|
||||
} else {
|
||||
newnode.x = y % 2 === 1 ? x + 6 : x;
|
||||
newnode.y = Math.floor(y / 2);
|
||||
}
|
||||
|
||||
newNodes.push(newnode);
|
||||
});
|
||||
}*/
|
||||
commonColumnSorting
|
||||
);
|
||||
}, [wrapperColumnCount]);
|
||||
|
||||
useEffect(() => {
|
||||
if (width === 0) return;
|
||||
const widgetWidth = width / wrapperColumnCount;
|
||||
const widgetWidth = mainAreaWidth / wrapperColumnCount;
|
||||
// widget width is used to define sizes of gridstack items within global.scss
|
||||
// TODO: improve
|
||||
root.style.setProperty('--gridstack-widget-width', widgetWidth.toString());
|
||||
root.style.setProperty('--gridstack-column-count', wrapperColumnCount.toString());
|
||||
gridRef.current?.cellHeight(widgetWidth);
|
||||
}, [width, wrapperColumnCount]);
|
||||
}, [mainAreaWidth, wrapperColumnCount]);
|
||||
|
||||
const onChange = isEditMode
|
||||
? (changedNode: GridStackNode) => {
|
||||
@@ -233,14 +103,14 @@ export const useGridstack = (
|
||||
: previous.widgets.find((x) => x.id === itemId);
|
||||
if (!currentItem) return previous;
|
||||
|
||||
currentItem.shape = {
|
||||
currentItem.shape[shapeSize] = {
|
||||
location: {
|
||||
x: changedNode.x ?? currentItem.shape.location.x,
|
||||
y: changedNode.y ?? currentItem.shape.location.y,
|
||||
x: changedNode.x ?? currentItem.shape[shapeSize].location.x,
|
||||
y: changedNode.y ?? currentItem.shape[shapeSize].location.y,
|
||||
},
|
||||
size: {
|
||||
width: changedNode.w ?? currentItem.shape.size.width,
|
||||
height: changedNode.h ?? currentItem.shape.size.height,
|
||||
width: changedNode.w ?? currentItem.shape[shapeSize].size.width,
|
||||
height: changedNode.h ?? currentItem.shape[shapeSize].size.height,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -299,14 +169,14 @@ export const useGridstack = (
|
||||
};
|
||||
}
|
||||
|
||||
currentItem.shape = {
|
||||
currentItem.shape[shapeSize] = {
|
||||
location: {
|
||||
x: addedNode.x ?? currentItem.shape.location.x,
|
||||
y: addedNode.y ?? currentItem.shape.location.y,
|
||||
x: addedNode.x ?? currentItem.shape[shapeSize].location.x,
|
||||
y: addedNode.y ?? currentItem.shape[shapeSize].location.y,
|
||||
},
|
||||
size: {
|
||||
width: addedNode.w ?? currentItem.shape.size.width,
|
||||
height: addedNode.h ?? currentItem.shape.size.height,
|
||||
width: addedNode.w ?? currentItem.shape[shapeSize].size.width,
|
||||
height: addedNode.h ?? currentItem.shape[shapeSize].size.height,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -362,7 +232,7 @@ export const useGridstack = (
|
||||
: () => {};
|
||||
|
||||
// initialize the gridstack
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
initializeGridstack(
|
||||
areaType,
|
||||
wrapperRef,
|
||||
@@ -378,7 +248,7 @@ export const useGridstack = (
|
||||
onAdd,
|
||||
}
|
||||
);
|
||||
}, [items, wrapperRef.current, widgets]);
|
||||
}, [items, wrapperRef.current, widgets, wrapperColumnCount]);
|
||||
|
||||
return {
|
||||
apps: items,
|
||||
|
||||
@@ -184,13 +184,35 @@ export default function ContainerActionBar({ selected, reload }: ContainerAction
|
||||
},
|
||||
},
|
||||
shape: {
|
||||
location: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
lg: {
|
||||
location: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
size: {
|
||||
height: 1,
|
||||
width: 1,
|
||||
},
|
||||
},
|
||||
size: {
|
||||
height: 1,
|
||||
width: 1,
|
||||
md: {
|
||||
location: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
size: {
|
||||
height: 1,
|
||||
width: 1,
|
||||
},
|
||||
},
|
||||
sm: {
|
||||
location: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
size: {
|
||||
height: 1,
|
||||
width: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
integration: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useState, MutableRefObject } from 'react';
|
||||
|
||||
export const useResize = (myRef: MutableRefObject<HTMLDivElement | null>) => {
|
||||
export const useResize = (myRef: MutableRefObject<HTMLDivElement | null>, dependencies: any[]) => {
|
||||
const [width, setWidth] = useState(0);
|
||||
const [height, setHeight] = useState(0);
|
||||
|
||||
@@ -21,7 +21,7 @@ export const useResize = (myRef: MutableRefObject<HTMLDivElement | null>) => {
|
||||
|
||||
useEffect(() => {
|
||||
handleResize();
|
||||
}, [myRef]);
|
||||
}, [myRef, dependencies]);
|
||||
|
||||
return { width, height };
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
:root {
|
||||
--gridstack-widget-width: 64;
|
||||
--gridstack-column-count: 12;
|
||||
}
|
||||
|
||||
.grid-stack-placeholder > .placeholder-content {
|
||||
@@ -17,24 +18,24 @@
|
||||
}
|
||||
|
||||
@for $i from 1 to 13 {
|
||||
.grid-stack>.grid-stack-item[gs-w="#{$i}"] { width: ($i / 12) * 100 + "%" }
|
||||
.grid-stack>.grid-stack-item[gs-min-w="#{$i}"] { min-width: ($i / 12) * 100 + "%" }
|
||||
.grid-stack>.grid-stack-item[gs-max-w="#{$i}"] { max-width: ($i / 12) * 100 + "%" }
|
||||
.grid-stack>.grid-stack-item[gs-w="#{$i}"] { width: calc(100% / #{var(--gridstack-column-count)} * #{$i}) }
|
||||
.grid-stack>.grid-stack-item[gs-min-w="#{$i}"] { min-width: calc(100% / #{var(--gridstack-column-count)} * #{$i}) }
|
||||
.grid-stack>.grid-stack-item[gs-max-w="#{$i}"] { max-width: calc(100% / #{var(--gridstack-column-count)} * #{$i}) }
|
||||
}
|
||||
|
||||
@for $i from 1 to 96 {
|
||||
.grid-stack>.grid-stack-item[gs-h="#{$i}"] { height: calc($i * #{var(--gridstack-widget-width)}) }
|
||||
.grid-stack>.grid-stack-item[gs-min-h="#{$i}"] { min-height: calc($i * #{var(--gridstack-widget-width)}) }
|
||||
.grid-stack>.grid-stack-item[gs-max-h="#{$i}"] { max-height: calc($i * #{var(--gridstack-widget-width)}) }
|
||||
.grid-stack>.grid-stack-item[gs-h="#{$i}"] { height: calc(#{$i} * #{var(--gridstack-widget-width)}) }
|
||||
.grid-stack>.grid-stack-item[gs-min-h="#{$i}"] { min-height: calc(#{$i} * #{var(--gridstack-widget-width)}) }
|
||||
.grid-stack>.grid-stack-item[gs-max-h="#{$i}"] { max-height: calc(#{$i} * #{var(--gridstack-widget-width)}) }
|
||||
}
|
||||
|
||||
@for $i from 1 to 13 {
|
||||
.grid-stack>.grid-stack-item[gs-x="#{$i}"] { left: ($i / 12) * 100 + "%" }
|
||||
.grid-stack>.grid-stack-item[gs-x="#{$i}"] { left: calc(100% / #{var(--gridstack-column-count)} * #{$i}) }
|
||||
}
|
||||
|
||||
|
||||
@for $i from 1 to 96 {
|
||||
.grid-stack>.grid-stack-item[gs-y="#{$i}"] { top: calc($i * #{var(--gridstack-widget-width)}) }
|
||||
.grid-stack>.grid-stack-item[gs-y="#{$i}"] { top: calc(#{$i} * #{var(--gridstack-widget-width)}) }
|
||||
}
|
||||
|
||||
.grid-stack>.grid-stack-item>.grid-stack-item-content,
|
||||
@@ -48,7 +49,7 @@
|
||||
}
|
||||
|
||||
.grid-stack>.grid-stack-item {
|
||||
min-width: (1/12)+'%';
|
||||
min-width: calc(percentage(1) * #{var(--gridstack-widget-width)});
|
||||
}
|
||||
|
||||
.grid-stack > .grid-stack-item > .grid-stack-item-content {
|
||||
@@ -58,35 +59,3 @@
|
||||
.grid-stack.grid-stack-animate {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1400px) {
|
||||
@for $i from 1 to 7 {
|
||||
.grid-stack>.grid-stack-item[gs-w="#{$i}"] { width: percentage(($i / 6)) !important }
|
||||
.grid-stack>.grid-stack-item[gs-min-w="#{$i}"] { min-width: percentage(($i / 6)) !important }
|
||||
.grid-stack>.grid-stack-item[gs-max-w="#{$i}"] { max-width: percentage(($i / 6)) !important }
|
||||
}
|
||||
|
||||
@for $i from 1 to 7 {
|
||||
.grid-stack>.grid-stack-item[gs-x="#{$i}"] { left: percentage(($i / 6)) }
|
||||
}
|
||||
|
||||
.grid-stack>.grid-stack-item {
|
||||
min-width: percentage(1/6) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
@for $i from 1 to 4 {
|
||||
.grid-stack>.grid-stack-item[gs-w="#{$i}"] { width: percentage(($i / 3)) !important }
|
||||
.grid-stack>.grid-stack-item[gs-min-w="#{$i}"] { min-width: percentage(($i / 3)) !important }
|
||||
.grid-stack>.grid-stack-item[gs-max-w="#{$i}"] { max-width: percentage(($i / 3)) !important }
|
||||
}
|
||||
|
||||
@for $i from 1 to 4 {
|
||||
.grid-stack>.grid-stack-item[gs-x="#{$i}"] { left: percentage(($i / 3)) }
|
||||
}
|
||||
|
||||
.grid-stack>.grid-stack-item {
|
||||
min-width: percentage(1/3) !important;
|
||||
}
|
||||
}
|
||||
@@ -92,6 +92,17 @@ const getConfigAndCreateIfNotExsists = (
|
||||
return category;
|
||||
};
|
||||
|
||||
const getShapeForColumnCount = (index: number, columnCount: number) => ({
|
||||
location: {
|
||||
x: index % columnCount,
|
||||
y: Math.floor(index / columnCount),
|
||||
},
|
||||
size: {
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const migrateService = (
|
||||
oldService: serviceItem,
|
||||
serviceIndex: number,
|
||||
@@ -117,13 +128,8 @@ const migrateService = (
|
||||
},
|
||||
area: areaType,
|
||||
shape: {
|
||||
location: {
|
||||
x: (serviceIndex * 3) % 18,
|
||||
y: Math.floor(serviceIndex / 6) * 3,
|
||||
},
|
||||
size: {
|
||||
width: 3,
|
||||
height: 3,
|
||||
},
|
||||
lg: getShapeForColumnCount(serviceIndex, 12),
|
||||
md: getShapeForColumnCount(serviceIndex, 6),
|
||||
sm: getShapeForColumnCount(serviceIndex, 3),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
export interface ShapeType {
|
||||
lg?: SizedShapeType;
|
||||
md?: SizedShapeType;
|
||||
sm?: SizedShapeType;
|
||||
}
|
||||
|
||||
export interface SizedShapeType {
|
||||
location: {
|
||||
x: number;
|
||||
y: number;
|
||||
|
||||
Reference in New Issue
Block a user