|
@@ -1,5 +1,5 @@
|
|
|
import { chunk } from "lodash";
|
|
|
-import { useCallback, useState } from "react";
|
|
|
+import React, { useCallback, useState } from "react";
|
|
|
import { importLibraryFromJSON, saveLibraryAsJSON } from "../data/json";
|
|
|
import Library from "../data/library";
|
|
|
import { ExcalidrawElement, NonDeleted } from "../element/types";
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
LibraryItem,
|
|
|
LibraryItems,
|
|
|
} from "../types";
|
|
|
-import { muteFSAbortError } from "../utils";
|
|
|
+import { arrayToMap, muteFSAbortError } from "../utils";
|
|
|
import { useDeviceType } from "./App";
|
|
|
import ConfirmDialog from "./ConfirmDialog";
|
|
|
import { exportToFileIcon, load, publishIcon, trash } from "./icons";
|
|
@@ -38,7 +38,7 @@ const LibraryMenuItems = ({
|
|
|
files,
|
|
|
id,
|
|
|
selectedItems,
|
|
|
- onToggle,
|
|
|
+ onSelectItems,
|
|
|
onPublish,
|
|
|
resetLibrary,
|
|
|
}: {
|
|
@@ -55,7 +55,7 @@ const LibraryMenuItems = ({
|
|
|
library: Library;
|
|
|
id: string;
|
|
|
selectedItems: LibraryItem["id"][];
|
|
|
- onToggle: (id: LibraryItem["id"], event: React.MouseEvent) => void;
|
|
|
+ onSelectItems: (id: LibraryItem["id"][]) => void;
|
|
|
onPublish: () => void;
|
|
|
resetLibrary: () => void;
|
|
|
}) => {
|
|
@@ -192,6 +192,55 @@ const LibraryMenuItems = ({
|
|
|
(id) => libraryItems.find((item) => item.id === id)?.status === "published",
|
|
|
);
|
|
|
|
|
|
+ const [lastSelectedItem, setLastSelectedItem] = useState<
|
|
|
+ LibraryItem["id"] | null
|
|
|
+ >(null);
|
|
|
+
|
|
|
+ const onItemSelectToggle = (
|
|
|
+ id: LibraryItem["id"],
|
|
|
+ event: React.MouseEvent,
|
|
|
+ ) => {
|
|
|
+ const shouldSelect = !selectedItems.includes(id);
|
|
|
+
|
|
|
+ const orderedItems = [...unpublishedItems, ...publishedItems];
|
|
|
+
|
|
|
+ if (shouldSelect) {
|
|
|
+ if (event.shiftKey && lastSelectedItem) {
|
|
|
+ const rangeStart = orderedItems.findIndex(
|
|
|
+ (item) => item.id === lastSelectedItem,
|
|
|
+ );
|
|
|
+ const rangeEnd = orderedItems.findIndex((item) => item.id === id);
|
|
|
+
|
|
|
+ if (rangeStart === -1 || rangeEnd === -1) {
|
|
|
+ onSelectItems([...selectedItems, id]);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const selectedItemsMap = arrayToMap(selectedItems);
|
|
|
+ const nextSelectedIds = orderedItems.reduce(
|
|
|
+ (acc: LibraryItem["id"][], item, idx) => {
|
|
|
+ if (
|
|
|
+ (idx >= rangeStart && idx <= rangeEnd) ||
|
|
|
+ selectedItemsMap.has(item.id)
|
|
|
+ ) {
|
|
|
+ acc.push(item.id);
|
|
|
+ }
|
|
|
+ return acc;
|
|
|
+ },
|
|
|
+ [],
|
|
|
+ );
|
|
|
+
|
|
|
+ onSelectItems(nextSelectedIds);
|
|
|
+ } else {
|
|
|
+ onSelectItems([...selectedItems, id]);
|
|
|
+ }
|
|
|
+ setLastSelectedItem(id);
|
|
|
+ } else {
|
|
|
+ setLastSelectedItem(null);
|
|
|
+ onSelectItems(selectedItems.filter((_id) => _id !== id));
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
const createLibraryItemCompo = (params: {
|
|
|
item:
|
|
|
| LibraryItem
|
|
@@ -212,9 +261,7 @@ const LibraryMenuItems = ({
|
|
|
onClick={params.onClick || (() => {})}
|
|
|
id={params.item?.id || null}
|
|
|
selected={!!params.item?.id && selectedItems.includes(params.item.id)}
|
|
|
- onToggle={(id, event) => {
|
|
|
- onToggle(id, event);
|
|
|
- }}
|
|
|
+ onToggle={onItemSelectToggle}
|
|
|
/>
|
|
|
</Stack.Col>
|
|
|
);
|
|
@@ -272,16 +319,12 @@ const LibraryMenuItems = ({
|
|
|
});
|
|
|
};
|
|
|
|
|
|
+ const unpublishedItems = libraryItems.filter(
|
|
|
+ (item) => item.status !== "published",
|
|
|
+ );
|
|
|
const publishedItems = libraryItems.filter(
|
|
|
(item) => item.status === "published",
|
|
|
);
|
|
|
- const unpublishedItems = [
|
|
|
- // append pending library item
|
|
|
- ...(pendingElements.length
|
|
|
- ? [{ id: null, elements: pendingElements }]
|
|
|
- : []),
|
|
|
- ...libraryItems.filter((item) => item.status !== "published"),
|
|
|
- ];
|
|
|
|
|
|
return (
|
|
|
<div className="library-menu-items-container">
|
|
@@ -310,7 +353,13 @@ const LibraryMenuItems = ({
|
|
|
>
|
|
|
<>
|
|
|
<div className="separator">{t("labels.personalLib")}</div>
|
|
|
- {renderLibrarySection(unpublishedItems)}
|
|
|
+ {renderLibrarySection([
|
|
|
+ // append pending library item
|
|
|
+ ...(pendingElements.length
|
|
|
+ ? [{ id: null, elements: pendingElements }]
|
|
|
+ : []),
|
|
|
+ ...unpublishedItems,
|
|
|
+ ])}
|
|
|
</>
|
|
|
|
|
|
<>
|