Skip to content

Commit

Permalink
Added multiple selection of gallery items.
Browse files Browse the repository at this point in the history
  • Loading branch information
ashleydavis committed Jul 7, 2024
1 parent b3568e6 commit 575a2e8
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 20 deletions.
74 changes: 57 additions & 17 deletions packages/user-interface/src/components/gallery-image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function GalleryImage({ item, onClick, x, y, width, height }: IGalleryIma
const [source, setSource] = useState<string>();
const [objectURL, setObjectURL] = useState<string>("");

const { loadAsset, unloadAsset } = useGallery();
const { loadAsset, unloadAsset, addToMultipleSelection, removeFromMultipleSelection } = useGallery();

const gutter = 1;

Expand Down Expand Up @@ -134,25 +134,65 @@ export function GalleryImage({ item, onClick, x, y, width, height }: IGalleryIma
</svg>
</div>
}

{/* Selection tick mark. */}

<div
style={{
position: "absolute",
left: "8px",
top: "8px",
width: "24px",
height: "24px",
borderRadius: "50%",
backgroundColor: item.selected ? "rgba(0, 0, 255, 1)" : "rgba(0, 0, 0, 0.25)",
display: "flex",
justifyContent: "center",
alignItems: "center",
cursor: "pointer",
}}
onClick={async event => {
event.preventDefault();
event.stopPropagation();
if (item.selected) {
await removeFromMultipleSelection(item);
}
else {
await addToMultipleSelection(item);
}
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="white"
width="16px"
height="16px"
>
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" />
</svg>
</div>

{/* Image number. */}

<div
style={{
position: "absolute",
left: `8px`,
bottom: `8px`,
padding: "2px",
color: "white",
backgroundColor: "rgba(0, 0, 0, 0.5)",
pointerEvents: "none",
fontSize: "14px",
lineHeight: "14px",
}}
>
#{item.searchIndex!+1}
</div>
</div>
}

<div
style={{
position: "absolute",
left: `${x}px`,
top: `${y}px`,
padding: "2px",
color: "white",
backgroundColor: "black",
pointerEvents: "none",
fontSize: "12px",
lineHeight: "14px",
}}
>
#{item.searchIndex!+1}
</div>

{/* Renders a debug panel for each image showing it's position and dimensions. */}
{/* <div
style={{
Expand Down
11 changes: 11 additions & 0 deletions packages/user-interface/src/context/asset-database-source.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,16 @@ export function AssetDatabaseProvider({ children }: IAssetDatabaseProviderProps)
});
}

//
// Update multiple assets with non persisted changes.
//
function updateAssets(assetUpdates: { assetIndex: number, partialAsset: Partial<IGalleryItem>}[]): void {
let _assets = [...assets];
assetUpdates.forEach(({ assetIndex, partialAsset }) => {
_assets[assetIndex] = { ..._assets[assetIndex], ...partialAsset };
});
setAssets(_assets);
}
//
// Adds an array value to the asset.
//
Expand Down Expand Up @@ -446,6 +456,7 @@ export function AssetDatabaseProvider({ children }: IAssetDatabaseProviderProps)
assets,
addAsset,
updateAsset,
updateAssets,
addArrayValue,
removeArrayValue,
checkAssetHash,
Expand Down
76 changes: 75 additions & 1 deletion packages/user-interface/src/context/gallery-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { createContext, ReactNode, useContext, useEffect, useRef, useStat
import { IGalleryItem } from "../lib/gallery-item";
import flexsearch from "flexsearch";
import { useGallerySource } from "./gallery-source";
import { update } from "lodash";

//
// Gets the sorting value from the gallery item.
Expand Down Expand Up @@ -102,6 +103,26 @@ export interface IGalleryContext {
//
clearSelectedItem(): void;

//
// Multiple selected gallery items.
//
selectedItems: IGalleryItem[]; //fio:

//
// Add the item to the multiple selection.
//
addToMultipleSelection(item: IGalleryItem): Promise<void>;

//
// Remove the item from the multiple selection.
//
removeFromMultipleSelection(item: IGalleryItem): Promise<void>;

//
// Clears the multiple selection.
//
clearMultiSelection(): Promise<void>;

//
// The current search text.
//
Expand Down Expand Up @@ -155,6 +176,11 @@ export function GalleryContextProvider({ sortFn, children }: IGalleryContextProv
//
const [selectedItem, setSelectedItem] = useState<IGalleryItem | undefined>(undefined);

//
// Multiple selected gallery items.
//
const [selectedItems, setSelectedItems] = useState<IGalleryItem[]>([]);

//
// References the search index.
//
Expand Down Expand Up @@ -395,6 +421,48 @@ export function GalleryContextProvider({ sortFn, children }: IGalleryContextProv
setSelectedItem(undefined);
}

//
// Add the item to the multiple selection.
//
async function addToMultipleSelection(item: IGalleryItem): Promise<void> {
if (!item.setIndex) {
throw new Error(`Asset index is not set`);
}
if (item.selected) {
// Already selected.
return;
}
setSelectedItems([...selectedItems, item]);
updateAssets([{ assetIndex: item.setIndex, partialAsset: { selected: true } }]);
}

//
// Remove the item from the multiple selection.
//
async function removeFromMultipleSelection(item: IGalleryItem): Promise<void> {
if (!item.setIndex) {
throw new Error(`Asset index is not set`);
}
if (!item.selected) {
// Already not selected.
return;
}

setSelectedItems(selectedItems.filter(selectedItem => selectedItem._id !== item._id));
updateAssets([{ assetIndex: item.setIndex, partialAsset: { selected: false } }]);
}

//
// Clears the multiple selection.
//
async function clearMultiSelection(): Promise<void> {
updateAssets(selectedItems.map(item => ({
assetIndex: item.setIndex!,
partialAsset: { selected: false },
})));
setSelectedItems([]);
}

//
// Sets the search text for finding assets.
// Passing in empty string or undefined gets all assets.
Expand All @@ -411,7 +479,9 @@ export function GalleryContextProvider({ sortFn, children }: IGalleryContextProv
return;
}

setItems(applySort(removeDeletedAssets(searchAssets(newSearchText))));
const items = applySort(removeDeletedAssets(searchAssets(newSearchText)));
setItems(items);
setSelectedItems(items.filter(item => item.selected));
setSearchText(newSearchText);
}

Expand Down Expand Up @@ -515,6 +585,10 @@ export function GalleryContextProvider({ sortFn, children }: IGalleryContextProv
selectedItem,
setSelectedItem,
clearSelectedItem,
selectedItems,
addToMultipleSelection,
removeFromMultipleSelection,
clearMultiSelection,
search,
clearSearch,
};
Expand Down
4 changes: 4 additions & 0 deletions packages/user-interface/src/context/gallery-source.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export interface IGallerySource {
// Updates an existing asset.
//
updateAsset(assetIndex: number, partialAsset: Partial<IGalleryItem>): Promise<void>;
//
// Update multiple assets with non persisted changes.
//
updateAssets(assetUpdates: { assetIndex: number, partialAsset: Partial<IGalleryItem>}[]): void;

//
// Adds an array value to the asset.
Expand Down
5 changes: 5 additions & 0 deletions packages/user-interface/src/lib/gallery-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ export interface IGalleryItem {
// The user that uploaded the asset.
//
userId: string;

//
// Set to true when selected.
//
selected?: boolean;
}

//
Expand Down
18 changes: 16 additions & 2 deletions packages/user-interface/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export function Main({ computerPage }: IMainProps) {
isLoading: isGalleryLoading,
items,
selectedItem,
selectedItems,
clearMultiSelection,
search,
clearSearch,
} = useGallery();
Expand Down Expand Up @@ -254,9 +256,21 @@ export function Main({ computerPage }: IMainProps) {
}

<div
className="mr-2 text-xs sm:text-sm"
className="flex flex-row items-center mr-2 text-xs sm:text-sm"
>
{items.length} photos
{selectedItems.length > 0
&& <div className="flex flex-row items-center">
<button
className="w-6 text-sm"
onClick={clearMultiSelection}
>
<i className="fa-solid fa-close"></i>
</button>
{selectedItems.length} selected
</div>
|| <div>{items.length} photos</div>
}

</div>

{!isAuthenticated && (
Expand Down

0 comments on commit 575a2e8

Please sign in to comment.