fix(Map): Add style of corners for windows. Add ability to reset widgets. A lot of refactoring

This commit is contained in:
achichenkov
2025-01-14 14:40:06 +03:00
parent 2c00bd426e
commit ef44881f06
11 changed files with 239 additions and 106 deletions

View File

@@ -27,5 +27,5 @@
--text-color: #ffffff;
--tooltip-bg: #202020;
--window-corner: #72716f;
}

View File

@@ -48,4 +48,6 @@
--rf-tag-color: #fbbf24;
--rf-has-user-characters: #5cb85c;
--window-corner: #72716f;
}

View File

@@ -1,63 +1,25 @@
import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css';
import { useMemo, useState } from 'react';
import { SESSION_KEY } from '@/hooks/Mapper/constants.ts';
import { useMemo } from 'react';
import { WindowManager } from '@/hooks/Mapper/components/ui-kit/WindowManager';
import { WindowProps } from '@/hooks/Mapper/components/ui-kit/WindowManager/types.ts';
import { CURRENT_WINDOWS_VERSION, DEFAULT_WIDGETS } from '@/hooks/Mapper/components/mapInterface/constants.tsx';
import { DEFAULT_WIDGETS } from '@/hooks/Mapper/components/mapInterface/constants.tsx';
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
type WindowsLS = {
windows: WindowProps[];
version: number;
};
const saveWindowsToLS = (toSaveItems: WindowProps[]) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const out = toSaveItems.map(({ content, ...rest }) => rest);
localStorage.setItem(SESSION_KEY.windows, JSON.stringify({ version: CURRENT_WINDOWS_VERSION, windows: out }));
};
const restoreWindowsFromLS = (): WindowProps[] => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const raw = localStorage.getItem(SESSION_KEY.windows);
if (!raw) {
console.warn('No windows found in local storage!!');
return DEFAULT_WIDGETS;
}
const { version, windows } = JSON.parse(raw) as WindowsLS;
if (!version || CURRENT_WINDOWS_VERSION > version) {
return DEFAULT_WIDGETS;
}
// eslint-disable-next-line no-debugger
const out = (windows as Omit<WindowProps, 'content'>[])
.filter(x => DEFAULT_WIDGETS.find(def => def.id === x.id))
.map(x => {
const content = DEFAULT_WIDGETS.find(def => def.id === x.id)?.content;
return { ...x, content: content! };
});
return out;
};
export const MapInterface = () => {
const [items, setItems] = useState<WindowProps[]>(restoreWindowsFromLS);
const { windowsVisible } = useMapRootState();
// const [items, setItems] = useState<WindowProps[]>(restoreWindowsFromLS);
const { windowsSettings, updateWidgetSettings } = useMapRootState();
const itemsFiltered = useMemo(() => {
return items.filter(x => windowsVisible.some(j => x.id === j));
}, [items, windowsVisible]);
const items = useMemo(() => {
return windowsSettings.windows
.map(x => {
const content = DEFAULT_WIDGETS.find(y => y.id === x.id)?.content;
return {
...x,
content: content!,
};
})
.filter(x => windowsSettings.visible.some(j => x.id === j));
}, [windowsSettings]);
return (
<WindowManager
windows={itemsFiltered}
dragSelector=".react-grid-dragHandleExample"
onChange={x => {
saveWindowsToLS(x);
setItems(x);
}}
/>
);
return <WindowManager windows={items} dragSelector=".react-grid-dragHandleExample" onChange={updateWidgetSettings} />;
};

View File

@@ -6,7 +6,8 @@ import {
SystemSignatures,
} from '@/hooks/Mapper/components/mapInterface/widgets';
export const CURRENT_WINDOWS_VERSION = 2;
export const CURRENT_WINDOWS_VERSION = 7;
export const WINDOWS_LOCAL_STORE_KEY = 'windows:settings:v2';
export enum WidgetsIds {
info = 'info',
@@ -15,6 +16,13 @@ export enum WidgetsIds {
routes = 'routes',
}
export const STORED_VISIBLE_WIDGETS_DEFAULT = [
WidgetsIds.info,
WidgetsIds.local,
WidgetsIds.routes,
WidgetsIds.signatures,
];
export const DEFAULT_WIDGETS: WindowProps[] = [
{
id: WidgetsIds.info,

View File

@@ -13,6 +13,7 @@
.p-tabview-panels {
padding: 6px 1rem !important;
flex-grow: 1;
height: 100%;
}
.p-tabview-nav-container {

View File

@@ -221,11 +221,7 @@ export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
<div className="flex flex-col gap-3">
<div className="flex flex-col gap-2">
<div className={styles.verticalTabsContainer}>
<TabView
activeIndex={activeIndex}
onTabChange={e => setActiveIndex(e.index)}
className={styles.verticalTabView}
>
<TabView activeIndex={activeIndex} onTabChange={e => setActiveIndex(e.index)}>
<TabPanel header="Common" headerClassName={styles.verticalTabHeader}>
<div className="w-full h-full flex flex-col gap-1">{renderSettingsList(COMMON_CHECKBOXES_PROPS)}</div>
</TabPanel>
@@ -246,7 +242,7 @@ export const MapSettings = ({ show, onHide }: MapSettingsProps) => {
{renderSettingsList(UI_CHECKBOXES_PROPS)}
</TabPanel>
<TabPanel header="Widgets" headerClassName={styles.verticalTabHeader}>
<TabPanel header="Widgets" className="h-full" headerClassName={styles.verticalTabHeader}>
<WidgetsSettings />
</TabPanel>

View File

@@ -3,35 +3,35 @@ import { WIDGETS_CHECKBOXES_PROPS, WidgetsIds } from '@/hooks/Mapper/components/
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
import { useCallback } from 'react';
import { Button } from 'primereact/button';
export interface WidgetsSettingsProps {}
// eslint-disable-next-line no-empty-pattern
export const WidgetsSettings = ({}: WidgetsSettingsProps) => {
const { windowsVisible, setWindowsVisible } = useMapRootState();
const { windowsSettings, toggleWidgetVisibility, resetWidgets } = useMapRootState();
const handleWidgetSettingsChange = useCallback(
(widget: WidgetsIds, checked: boolean) => {
setWindowsVisible(prev => {
if (checked) {
return [...prev, widget];
}
return prev.filter(x => x !== widget);
});
},
[setWindowsVisible],
(widget: WidgetsIds) => toggleWidgetVisibility(widget),
[toggleWidgetVisibility],
);
return (
<div className="">
<div className="flex flex-col h-full gap-2">
<div>
{WIDGETS_CHECKBOXES_PROPS.map(widget => (
<PrettySwitchbox
key={widget.id}
label={widget.label}
checked={windowsVisible.some(x => x === widget.id)}
setChecked={checked => handleWidgetSettingsChange(widget.id, checked)}
checked={windowsSettings.visible.some(x => x === widget.id)}
setChecked={() => handleWidgetSettingsChange(widget.id)}
/>
))}
</div>
<div className="grid grid-cols-[1fr_auto]">
<div />
<Button className="py-[4px]" onClick={resetWidgets} outlined size="small" label="Reset Widgets"></Button>
</div>
</div>
);
};

View File

@@ -35,21 +35,73 @@
.topLeft {
top: -7.5px;
left: -7.5px;
&::after {
position: relative;
top: 7.5px;
left: 7.5px;
display: block;
content: " ";
width: 5px;
height: 5px;
border-left: 1px solid var(--window-corner);
border-top: 1px solid var(--window-corner);
pointer-events: none;
}
}
.topRight {
top: -7.5px;
right: -7.5px;
&::after {
position: relative;
top: 7.5px;
right: -2.5px;
display: block;
content: " ";
width: 5px;
height: 5px;
border-right: 1px solid var(--window-corner);
border-top: 1px solid var(--window-corner);
pointer-events: none;
}
}
.bottomLeft {
bottom: -7.5px;
left: -7.5px;
&::after {
position: relative;
top: 2.5px;
left: 7.5px;
display: block;
content: " ";
width: 5px;
height: 5px;
border-left: 1px solid var(--window-corner);
border-bottom: 1px solid var(--window-corner);
pointer-events: none;
}
}
.bottomRight {
bottom: -7.5px;
right: -7.5px;
&::after {
position: relative;
top: 2.5px;
right: -2.5px;
display: block;
content: " ";
width: 5px;
height: 5px;
border-right: 1px solid var(--window-corner);
border-bottom: 1px solid var(--window-corner);
pointer-events: none;
}
}
.top {

View File

@@ -5,7 +5,7 @@ import { WindowProps } from '@/hooks/Mapper/components/ui-kit/WindowManager/type
const MIN_WINDOW_SIZE = 100;
const SNAP_THRESHOLD = 10;
const SNAP_GAP = 10;
export const SNAP_GAP = 10;
export enum ActionType {
Drag = 'drag',
@@ -92,12 +92,7 @@ export const WindowManager: React.FC<WindowManagerProps> = ({ windows: initialWi
);
useEffect(() => {
setWindows(
initialWindows.map((window, index) => ({
...window,
zIndex: index + 1,
})),
);
setWindows(initialWindows.slice(0));
}, [initialWindows]);
const containerRef = useRef<HTMLDivElement | null>(null);

View File

@@ -4,7 +4,12 @@ import { MapUnionTypes, OutCommandHandler, SolarSystemConnection } from '@/hooks
import { useMapRootHandlers } from '@/hooks/Mapper/mapRootProvider/hooks';
import { WithChildren } from '@/hooks/Mapper/types/common.ts';
import useLocalStorageState from 'use-local-storage-state';
import { WidgetsIds } from '@/hooks/Mapper/components/mapInterface/constants.tsx';
import {
ToggleWidgetVisibility,
UpdateWidgetSettingsFunc,
useStoreWidgets,
WindowStoreInfo,
} from '@/hooks/Mapper/mapRootProvider/hooks/useStoreWidgets.ts';
export type MapRootData = MapUnionTypes & {
selectedSystems: string[];
@@ -64,21 +69,16 @@ export const STORED_INTERFACE_DEFAULT_VALUES: InterfaceStoredSettings = {
theme: 'default',
};
export const STORED_VISIBLE_WIDGETS_DEFAULT = [
WidgetsIds.info,
WidgetsIds.local,
WidgetsIds.routes,
WidgetsIds.signatures,
];
export interface MapRootContextProps {
update: ContextStoreDataUpdate<MapRootData>;
data: MapRootData;
outCommand: OutCommandHandler;
interfaceSettings: InterfaceStoredSettings;
setInterfaceSettings: Dispatch<SetStateAction<InterfaceStoredSettings>>;
windowsVisible: WidgetsIds[];
setWindowsVisible: Dispatch<SetStateAction<WidgetsIds[]>>;
windowsSettings: WindowStoreInfo;
toggleWidgetVisibility: ToggleWidgetVisibility;
updateWidgetSettings: UpdateWidgetSettingsFunc;
resetWidgets: () => void;
}
const MapRootContext = createContext<MapRootContextProps>({
@@ -112,10 +112,7 @@ export const MapRootProvider = ({ children, fwdRef, outCommand }: MapRootProvide
defaultValue: STORED_INTERFACE_DEFAULT_VALUES,
},
);
const [windowsVisible, setWindowsVisible] = useLocalStorageState<WidgetsIds[]>('windows:visible', {
defaultValue: STORED_VISIBLE_WIDGETS_DEFAULT,
});
const { windowsSettings, toggleWidgetVisibility, updateWidgetSettings, resetWidgets } = useStoreWidgets();
useEffect(() => {
let foundNew = false;
@@ -143,8 +140,10 @@ export const MapRootProvider = ({ children, fwdRef, outCommand }: MapRootProvide
outCommand: outCommand,
setInterfaceSettings,
interfaceSettings,
windowsVisible,
setWindowsVisible,
windowsSettings,
updateWidgetSettings,
toggleWidgetVisibility,
resetWidgets,
}}
>
<MapRootHandlers ref={fwdRef}>{children}</MapRootHandlers>

View File

@@ -0,0 +1,118 @@
import useLocalStorageState from 'use-local-storage-state';
import {
CURRENT_WINDOWS_VERSION,
DEFAULT_WIDGETS,
STORED_VISIBLE_WIDGETS_DEFAULT,
WidgetsIds,
WINDOWS_LOCAL_STORE_KEY,
} from '@/hooks/Mapper/components/mapInterface/constants.tsx';
import { WindowProps } from '@/hooks/Mapper/components/ui-kit/WindowManager/types.ts';
import { useCallback, useEffect, useRef } from 'react';
import { SNAP_GAP } from '@/hooks/Mapper/components/ui-kit/WindowManager';
export type StoredWindowProps = Omit<WindowProps, 'content'>;
export type WindowStoreInfo = {
version: number;
windows: StoredWindowProps[];
visible: WidgetsIds[];
};
export type UpdateWidgetSettingsFunc = (widgets: WindowProps[]) => void;
export type ToggleWidgetVisibility = (widgetId: WidgetsIds) => void;
export const getDefaultWidgetProps = () => ({
version: CURRENT_WINDOWS_VERSION,
visible: STORED_VISIBLE_WIDGETS_DEFAULT,
windows: DEFAULT_WIDGETS,
});
export const useStoreWidgets = () => {
const [windowsSettings, setWindowsSettings] = useLocalStorageState<WindowStoreInfo>(WINDOWS_LOCAL_STORE_KEY, {
defaultValue: getDefaultWidgetProps(),
});
const ref = useRef({ windowsSettings, setWindowsSettings });
ref.current = { windowsSettings, setWindowsSettings };
const updateWidgetSettings: UpdateWidgetSettingsFunc = useCallback(newWindows => {
const { setWindowsSettings } = ref.current;
setWindowsSettings(({ version, visible /*, windows*/ }: WindowStoreInfo) => {
return {
version,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
windows: DEFAULT_WIDGETS.map(({ content, ...x }) => {
const windowProp = newWindows.find(j => j.id === x.id);
if (windowProp) {
return windowProp;
}
return x;
}),
visible,
};
});
}, []);
const toggleWidgetVisibility: ToggleWidgetVisibility = useCallback(widgetId => {
const { setWindowsSettings } = ref.current;
setWindowsSettings(({ visible, windows, ...x }) => {
const isCheckedPrev = visible.includes(widgetId);
if (!isCheckedPrev) {
const maxZIndex = Math.max(...windows.map(w => w.zIndex));
return {
...x,
windows: windows.map(wnd => {
if (wnd.id === widgetId) {
return { ...wnd, position: { x: SNAP_GAP, y: SNAP_GAP }, zIndex: maxZIndex + 1 };
}
return wnd;
}),
visible: [...visible, widgetId],
};
}
return {
...x,
windows,
visible: visible.filter(x => x !== widgetId),
};
});
}, []);
useEffect(() => {
const { setWindowsSettings } = ref.current;
const raw = localStorage.getItem(WINDOWS_LOCAL_STORE_KEY);
if (!raw) {
console.warn('No windows found in local storage!!');
setWindowsSettings(getDefaultWidgetProps());
return;
}
const { version, windows, visible } = JSON.parse(raw) as WindowStoreInfo;
if (!version || CURRENT_WINDOWS_VERSION > version) {
setWindowsSettings(getDefaultWidgetProps());
}
// eslint-disable-next-line no-debugger
const out = windows.filter(x => DEFAULT_WIDGETS.find(def => def.id === x.id));
setWindowsSettings({
version: CURRENT_WINDOWS_VERSION,
windows: out as WindowProps[],
visible,
});
}, []);
const resetWidgets = useCallback(() => ref.current.setWindowsSettings(getDefaultWidgetProps()), []);
return {
windowsSettings,
updateWidgetSettings,
toggleWidgetVisibility,
resetWidgets,
};
};