diff --git a/client/package-lock.json b/client/package-lock.json
index 9e4e22b..1a43ce1 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -9,7 +9,6 @@
"version": "0.0.0",
"dependencies": {
"-": "^0.0.1",
- "@dnd-kit/core": "^6.3.1",
"@fluentui/react-components": "^9.69.0",
"@fluentui/react-datepicker-compat": "^0.6.14",
"@fluentui/react-icons": "^2.0.309",
@@ -1937,42 +1936,6 @@
"node": ">=10"
}
},
- "node_modules/@dnd-kit/accessibility": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
- "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
- "dependencies": {
- "tslib": "^2.0.0"
- },
- "peerDependencies": {
- "react": ">=16.8.0"
- }
- },
- "node_modules/@dnd-kit/core": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
- "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
- "dependencies": {
- "@dnd-kit/accessibility": "^3.1.1",
- "@dnd-kit/utilities": "^3.2.2",
- "tslib": "^2.0.0"
- },
- "peerDependencies": {
- "react": ">=16.8.0",
- "react-dom": ">=16.8.0"
- }
- },
- "node_modules/@dnd-kit/utilities": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
- "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
- "dependencies": {
- "tslib": "^2.0.0"
- },
- "peerDependencies": {
- "react": ">=16.8.0"
- }
- },
"node_modules/@emotion/hash": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
diff --git a/client/package.json b/client/package.json
index 8d8d6b1..10f838c 100644
--- a/client/package.json
+++ b/client/package.json
@@ -12,7 +12,6 @@
},
"dependencies": {
"-": "^0.0.1",
- "@dnd-kit/core": "^6.3.1",
"@fluentui/react-components": "^9.69.0",
"@fluentui/react-datepicker-compat": "^0.6.14",
"@fluentui/react-icons": "^2.0.309",
diff --git a/client/src/pages/MapTest.tsx b/client/src/pages/MapTest.tsx
index 948d1bf..8f454fc 100644
--- a/client/src/pages/MapTest.tsx
+++ b/client/src/pages/MapTest.tsx
@@ -1,11 +1,13 @@
import { Tab, TabList } from "@fluentui/react-tabs";
import MapComponent from "../components/map/MapComponent";
+import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd";
import {
useAppStore,
setCurrentTab,
deleteMapTab,
addMapTab,
+ reorderTabs,
} from "../store/app";
import { initializeMapState, useMapStore } from "../store/map";
import { initializeObjectsState } from "../store/objects";
@@ -13,9 +15,86 @@ import { Button } from "@fluentui/react-components";
import { Add12Filled, Dismiss12Filled, Map16Regular } from "@fluentui/react-icons";
function MapTest() {
- const { mapTab, currentTab } = useAppStore()
+ const { mapTab, currentTab, tabOrder } = useAppStore()
const { id } = useMapStore()
+ const handleDragEnd = (result: any) => {
+ if (!result.destination) return
+ reorderTabs(result.source.index, result.destination.index)
+ }
+
+ return (
+
+
+
+
+ {(provided) => (
+ setCurrentTab(data.value as string)}
+ style={{ borderBottom: '1px solid var(--colorNeutralShadowKey)' }}
+ onDragStart={(e) => {
+ e.stopPropagation(); // stop TabList from also handling it
+ }}
+ onDrag={(e) => e.stopPropagation()}
+ >
+ {tabOrder.map((key, index) => (
+
+ {(dragProvided) => (
+
+ }>
+ {id[key]?.mapLabel ?? `Tab ${key}`}
+ }
+ appearance="subtle"
+ onClick={(e) => {
+ e.stopPropagation()
+ deleteMapTab(key)
+ }}
+ />
+
+
+ )}
+
+ ))}
+
+ {provided.placeholder}
+
+ }
+ title="Открыть новую вкладку"
+ appearance="subtle"
+ onClick={() => {
+ const newId = addMapTab();
+ initializeObjectsState(newId, null, null, null, null);
+ initializeMapState(newId);
+ }}
+ />
+
+ )}
+
+
+
+
+ {tabOrder.map((key) => (
+
+
+
+ ))}
+
+
+
+ )
+
return (
diff --git a/client/src/store/app.ts b/client/src/store/app.ts
index 5560f40..529dc19 100644
--- a/client/src/store/app.ts
+++ b/client/src/store/app.ts
@@ -4,7 +4,6 @@ import { initializeObjectsState } from './objects'
import { initializeMapState } from './map'
export type Mode = 'edit' | 'view'
-
export type ColorScheme = 'light' | 'dark' | 'auto'
export interface MapTabState {
@@ -14,15 +13,16 @@ export interface MapTabState {
}
export interface AppState {
- colorScheme: ColorScheme,
- mapTab: Record
,
- currentTab: string | null;
+ colorScheme: ColorScheme
+ mapTab: Record
+ tabOrder: string[] // 👈 defines tab order
+ currentTab: string | null
}
export const useAppStore = create(() => {
const firstId = uuidv4()
- initializeObjectsState(firstId, null, null, null, null);
+ initializeObjectsState(firstId, null, null, null, null)
initializeMapState(firstId)
return {
@@ -31,15 +31,15 @@ export const useAppStore = create(() => {
mapTab: {
[firstId]: { year: null, region: null, district: null },
},
+ tabOrder: [firstId],
}
})
-export const getColorScheme = () => {
- useAppStore.getState().colorScheme
-}
+// getters/setters
+export const getColorScheme = () => useAppStore.getState().colorScheme
export const setColorScheme = (colorScheme: ColorScheme) => {
- useAppStore.setState(() => ({ colorScheme: colorScheme }))
+ useAppStore.setState(() => ({ colorScheme }))
localStorage.setItem('colorScheme', colorScheme.toString())
}
@@ -47,40 +47,46 @@ export const getCurrentTab = () => useAppStore.getState().currentTab
export const setCurrentTab = (id: string | null) => useAppStore.setState(() => ({ currentTab: id }))
export const setMapTabYear = (id: string, year: number | null) =>
- useAppStore.setState((state) => {
- return {
- mapTab: {
- ...state.mapTab,
- [id]: { ...state.mapTab[id], year: year }
- }
- }
- })
+ useAppStore.setState((state) => ({
+ mapTab: {
+ ...state.mapTab,
+ [id]: { ...state.mapTab[id], year },
+ },
+ }))
export const deleteMapTab = (id: string) =>
useAppStore.setState((state) => {
- const { [id]: _, ...remainingTabs } = state.mapTab;
- const keys = Object.keys(remainingTabs);
+ const { [id]: _, ...remainingTabs } = state.mapTab
+ const newOrder = state.tabOrder.filter((tid) => tid !== id)
return {
mapTab: remainingTabs,
- currentTab: keys.length > 0 ? keys[keys.length - 1] : null,
- };
+ tabOrder: newOrder,
+ currentTab: newOrder.length > 0 ? newOrder[newOrder.length - 1] : null,
+ }
})
export const addMapTab = () => {
- const id = uuidv4();
+ const id = uuidv4()
+ initializeObjectsState(id, null, null, null, null)
+ initializeMapState(id)
useAppStore.setState((state) => ({
mapTab: {
...state.mapTab,
- [id]: {
- year: null,
- region: null,
- district: null,
- },
+ [id]: { year: null, region: null, district: null },
},
- currentTab: id, // optionally switch to this new tab
- }));
+ tabOrder: [...state.tabOrder, id],
+ currentTab: id,
+ }))
- return id; // so you can use the new id in your components
-}
\ No newline at end of file
+ return id
+}
+
+export const reorderTabs = (from: number, to: number) =>
+ useAppStore.setState((state) => {
+ const newOrder = Array.from(state.tabOrder)
+ const [moved] = newOrder.splice(from, 1)
+ newOrder.splice(to, 0, moved)
+ return { tabOrder: newOrder }
+ })
diff --git a/client/yarn.lock b/client/yarn.lock
index c264158..b9a05e9 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -987,29 +987,6 @@
resolved "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz"
integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==
-"@dnd-kit/accessibility@^3.1.1":
- version "3.1.1"
- resolved "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz"
- integrity sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==
- dependencies:
- tslib "^2.0.0"
-
-"@dnd-kit/core@^6.3.1":
- version "6.3.1"
- resolved "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz"
- integrity sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==
- dependencies:
- "@dnd-kit/accessibility" "^3.1.1"
- "@dnd-kit/utilities" "^3.2.2"
- tslib "^2.0.0"
-
-"@dnd-kit/utilities@^3.2.2":
- version "3.2.2"
- resolved "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz"
- integrity sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==
- dependencies:
- tslib "^2.0.0"
-
"@emotion/hash@^0.9.0":
version "0.9.2"
resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz"
@@ -5584,7 +5561,7 @@ rc@^1.0.1, rc@^1.1.6:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
-"react-dom@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", react-dom@^18.0.0, react-dom@^18.2.0, "react-dom@>=16.14.0 <19.0.0", "react-dom@>=16.14.0 <20.0.0", react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0, "react-dom@>=16.8.0 <19.0.0", "react-dom@>=16.8.0 <20.0.0", react-dom@>=18.0.0:
+"react-dom@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", react-dom@^18.0.0, react-dom@^18.2.0, "react-dom@>=16.14.0 <19.0.0", "react-dom@>=16.14.0 <20.0.0", react-dom@>=16.6.0, react-dom@>=16.8, "react-dom@>=16.8.0 <19.0.0", "react-dom@>=16.8.0 <20.0.0", react-dom@>=18.0.0:
version "18.3.1"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz"
integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==
@@ -5640,7 +5617,7 @@ react-transition-group@^4.4.1:
loose-envify "^1.4.0"
prop-types "^15.6.2"
-"react@^16.11.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17 || ^18 || ^19", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^18.0 || ^19", react@^18.0.0, react@^18.2.0, react@^18.3.1, "react@>= 16", "react@>=16.14.0 <19.0.0", "react@>=16.14.0 <20.0.0", react@>=16.6.0, react@>=16.8, react@>=16.8.0, "react@>=16.8.0 <19.0.0", "react@>=16.8.0 <20.0.0", react@>=18.0.0:
+"react@^16.11.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17 || ^18 || ^19", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^18.0 || ^19", react@^18.0.0, react@^18.2.0, react@^18.3.1, "react@>= 16", "react@>=16.14.0 <19.0.0", "react@>=16.14.0 <20.0.0", react@>=16.6.0, react@>=16.8, "react@>=16.8.0 <19.0.0", "react@>=16.8.0 <20.0.0", react@>=18.0.0:
version "18.3.1"
resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz"
integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
@@ -6529,7 +6506,7 @@ ts-interface-checker@^0.1.9:
resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz"
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
-tslib@^2.0.0, tslib@^2.1.0, tslib@^2.8.0, tslib@^2.8.1:
+tslib@^2.1.0, tslib@^2.8.0, tslib@^2.8.1:
version "2.8.1"
resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz"
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==