diff --git a/app/Http/Resources/Search/InitResource.php b/app/Http/Resources/Search/InitResource.php
index 685165efee9..767d76e088e 100644
--- a/app/Http/Resources/Search/InitResource.php
+++ b/app/Http/Resources/Search/InitResource.php
@@ -2,6 +2,7 @@
namespace App\Http\Resources\Search;
+use App\Enum\PhotoLayoutType;
use App\Models\Configs;
use Spatie\LaravelData\Data;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;
@@ -13,9 +14,11 @@
class InitResource extends Data
{
public int $search_minimum_length = 3;
+ public PhotoLayoutType $photo_layout;
public function __construct()
{
$this->search_minimum_length = Configs::getValueAsInt('search_minimum_length_required');
+ $this->photo_layout = Configs::getValueAsEnum('search_photos_layout', PhotoLayoutType::class);
}
}
\ No newline at end of file
diff --git a/resources/js/components/forms/album/AlbumProperties.vue b/resources/js/components/forms/album/AlbumProperties.vue
index daf2ad4f4ca..05150b4b617 100644
--- a/resources/js/components/forms/album/AlbumProperties.vue
+++ b/resources/js/components/forms/album/AlbumProperties.vue
@@ -321,8 +321,8 @@ function load(editable: App.Http.Resources.Editable.EditableBaseAlbumResource, p
albumSortingColumn.value = SelectBuilders.buildAlbumSorting(editable.album_sorting?.column);
albumSortingOrder.value = SelectBuilders.buildSortingOrder(editable.album_sorting?.order);
photoLayout.value = SelectBuilders.buildPhotoLayout(editable.photo_layout ?? undefined);
- license.value = SelectBuilders.buildLicense(editable.license);
- aspectRatio.value = SelectBuilders.buildAspectRatio(editable.aspect_ratio);
+ license.value = SelectBuilders.buildLicense(editable.license ?? undefined);
+ aspectRatio.value = SelectBuilders.buildAspectRatio(editable.aspect_ratio ?? undefined);
header_id.value = buildHeaderId(editable.header_id, photos);
tags.value = editable.tags;
}
diff --git a/resources/js/components/gallery/AlbumThumbPanel.vue b/resources/js/components/gallery/AlbumThumbPanel.vue
index 2d8395a4f60..269d8ee1a28 100644
--- a/resources/js/components/gallery/AlbumThumbPanel.vue
+++ b/resources/js/components/gallery/AlbumThumbPanel.vue
@@ -1,29 +1,24 @@
diff --git a/resources/js/components/gallery/PhotoThumbPanel.vue b/resources/js/components/gallery/PhotoThumbPanel.vue
index 86e9f0334dd..4b18fecad7b 100644
--- a/resources/js/components/gallery/PhotoThumbPanel.vue
+++ b/resources/js/components/gallery/PhotoThumbPanel.vue
@@ -1,39 +1,25 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
diff --git a/resources/js/components/gallery/PhotoThumbPanelControl.vue b/resources/js/components/gallery/PhotoThumbPanelControl.vue
new file mode 100644
index 00000000000..99ea1e8e92b
--- /dev/null
+++ b/resources/js/components/gallery/PhotoThumbPanelControl.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/js/components/gallery/PhotoThumbPanelList.vue b/resources/js/components/gallery/PhotoThumbPanelList.vue
new file mode 100644
index 00000000000..729014939cd
--- /dev/null
+++ b/resources/js/components/gallery/PhotoThumbPanelList.vue
@@ -0,0 +1,46 @@
+
+
+
+
diff --git a/resources/js/components/settings/AllSettings.vue b/resources/js/components/settings/AllSettings.vue
index a29f5b3a0b9..91288f17cda 100644
--- a/resources/js/components/settings/AllSettings.vue
+++ b/resources/js/components/settings/AllSettings.vue
@@ -89,9 +89,8 @@
@filled="update"
@reset="reset"
/>
-
, auth: AuthStore, isLogin
const tagAlbum = ref(undefined as undefined | App.Http.Resources.Models.TagAlbumResource);
const smartAlbum = ref(undefined as undefined | App.Http.Resources.Models.SmartAlbumResource);
const album = computed(() => modelAlbum.value || tagAlbum.value || smartAlbum.value);
- const layout = ref(null) as Ref;
const isAlbumConsented = ref(false);
const photos = ref([]) as Ref;
@@ -54,12 +53,6 @@ export function useAlbumRefresher(albumId: Ref, auth: AuthStore, isLogin
});
}
- function loadLayout() {
- AlbumService.getLayout().then((data) => {
- layout.value = data.data;
- });
- }
-
function refresh() {
loadUser();
}
@@ -74,12 +67,10 @@ export function useAlbumRefresher(albumId: Ref, auth: AuthStore, isLogin
smartAlbum,
album,
rights,
- layout,
photos,
config,
loadUser,
loadAlbum,
- loadLayout,
refresh,
};
}
diff --git a/resources/js/composables/album/searchRefresher.ts b/resources/js/composables/album/searchRefresher.ts
index 27af286fb29..cc106a02a97 100644
--- a/resources/js/composables/album/searchRefresher.ts
+++ b/resources/js/composables/album/searchRefresher.ts
@@ -12,6 +12,7 @@ export function useSearch(albumid: Ref, lycheeStore: LycheeStateStore, s
const from = ref(0);
const per_page = ref(0);
const total = ref(0);
+ const layout = ref("square" as App.Enum.PhotoLayoutType);
const photoHeader = computed(() => {
return trans("lychee.PHOTOS") + " (" + total.value + ")";
@@ -24,6 +25,7 @@ export function useSearch(albumid: Ref, lycheeStore: LycheeStateStore, s
function searchInit() {
SearchService.init(albumid.value).then((response) => {
searchMinimumLengh.value = response.data.search_minimum_length;
+ layout.value = response.data.photo_layout;
});
}
@@ -68,6 +70,7 @@ export function useSearch(albumid: Ref, lycheeStore: LycheeStateStore, s
}
return {
+ layout,
albums,
photos,
noData,
diff --git a/resources/js/composables/contextMenus/contextMenu.ts b/resources/js/composables/contextMenus/contextMenu.ts
index 745ddd3bdfe..acebe9c4679 100644
--- a/resources/js/composables/contextMenus/contextMenu.ts
+++ b/resources/js/composables/contextMenus/contextMenu.ts
@@ -1,19 +1,19 @@
import { computed, Ref, ref } from "vue";
type Selectors = {
- config: Ref | null;
- album: Ref<
+ config?: Ref;
+ album?: Ref<
| App.Http.Resources.Models.AlbumResource
| App.Http.Resources.Models.TagAlbumResource
| App.Http.Resources.Models.SmartAlbumResource
| undefined
- > | null;
- selectedPhotosIdx: Ref | undefined;
- selectedPhoto: Ref | undefined;
- selectedPhotos: Ref | undefined;
- selectedAlbumIdx: Ref;
- selectedAlbum: Ref;
- selectedAlbums: Ref;
+ >;
+ selectedPhotosIdx?: Ref;
+ selectedPhoto?: Ref;
+ selectedPhotos?: Ref;
+ selectedAlbumIdx?: Ref;
+ selectedAlbum?: Ref;
+ selectedAlbums?: Ref;
};
type PhotoCallbacks = {
@@ -73,10 +73,10 @@ export function useContextMenu(selectors: Selectors, photoCallbacks: PhotoCallba
if (selectors.selectedPhotos !== undefined && selectors.selectedPhotos.value.length > 1) {
menu = photosMenu();
}
- if (selectors.selectedAlbum.value !== undefined) {
+ if (selectors.selectedAlbum !== undefined && selectors.selectedAlbum.value !== undefined) {
menu = albumMenu();
}
- if (selectors.selectedAlbums.value.length > 1) {
+ if (selectors.selectedAlbums !== undefined && selectors.selectedAlbums.value.length > 1) {
menu = albumsMenu();
}
return menu.filter((item) => item.access !== false);
diff --git a/resources/js/config/constants.ts b/resources/js/config/constants.ts
index 3786525a288..132103493bf 100644
--- a/resources/js/config/constants.ts
+++ b/resources/js/config/constants.ts
@@ -117,11 +117,11 @@ export const SelectBuilders = {
return albumSortingColumnsOptions.find((option) => option.value === value) || undefined;
},
- buildAspectRatio(value: string | App.Enum.AspectRatioType | null): SelectOption | undefined {
+ buildAspectRatio(value: string | App.Enum.AspectRatioType | undefined): SelectOption | undefined {
return aspectRationOptions.find((option) => option.value === value) || undefined;
},
- buildLicense(value: string | App.Enum.LicenseType | null): SelectOption | undefined {
+ buildLicense(value: string | App.Enum.LicenseType | undefined): SelectOption | undefined {
return licenseOptions.find((option) => option.value === value) || undefined;
},
diff --git a/resources/js/layouts/PhotoLayout.ts b/resources/js/layouts/PhotoLayout.ts
index c4eb5f6cf76..655d1e931ed 100644
--- a/resources/js/layouts/PhotoLayout.ts
+++ b/resources/js/layouts/PhotoLayout.ts
@@ -1,20 +1,20 @@
-import { computed, ref } from "vue";
+import { computed, Ref, ref } from "vue";
import { useSquare } from "./useSquare";
import { useJustify } from "./useJustify";
import { useMasonry } from "./useMasonry";
import { useGrid } from "./useGrid";
+import AlbumService from "@/services/album-service";
-export function useLayouts(config: App.Http.Resources.GalleryConfigs.PhotoLayoutConfig, photo_layout: App.Enum.PhotoLayoutType) {
+export function useLayouts(
+ config: App.Http.Resources.GalleryConfigs.PhotoLayoutConfig,
+ layout: Ref,
+ elemId: string = "photoListing",
+) {
const configRef = ref(config);
- const layout = ref(photo_layout);
- const BASE = "my-0 w-5 h-5 mr-0 ml-0 transition-all duration-300 group-hover:scale-150 group-hover:stroke-black dark:group-hover:stroke-white ";
- const squareClass = computed(() => BASE + (layout.value === "square" ? "stroke-primary-400" : "stroke-neutral-400"));
- const justifiedClass = computed(() => BASE + (layout.value === "justified" ? "fill-primary-400" : "fill-neutral-400"));
- const masonryClass = computed(() => BASE + (layout.value === "masonry" ? "stroke-primary-400" : "stroke-neutral-400"));
- const gridClass = computed(() => BASE + (layout.value === "grid" ? "stroke-primary-400" : "stroke-neutral-400"));
+ const elementId = elemId;
function activateLayout() {
- const photoListing = document.getElementById("photoListing");
+ const photoListing = document.getElementById(elementId);
if (photoListing === null) {
return; // Nothing to do
}
@@ -33,11 +33,38 @@ export function useLayouts(config: App.Http.Resources.GalleryConfigs.PhotoLayout
}
return {
- layout,
+ activateLayout,
+ };
+}
+
+export function useLayoutClass(layout: Ref) {
+ const BASE = "my-0 w-5 h-5 mr-0 ml-0 transition-all duration-300 group-hover:scale-150 group-hover:stroke-black dark:group-hover:stroke-white ";
+ const squareClass = computed(() => BASE + (layout.value === "square" ? "stroke-primary-400" : "stroke-neutral-400"));
+ const justifiedClass = computed(() => BASE + (layout.value === "justified" ? "fill-primary-400" : "fill-neutral-400"));
+ const masonryClass = computed(() => BASE + (layout.value === "masonry" ? "stroke-primary-400" : "stroke-neutral-400"));
+ const gridClass = computed(() => BASE + (layout.value === "grid" ? "stroke-primary-400" : "stroke-neutral-400"));
+
+ return {
squareClass,
justifiedClass,
masonryClass,
gridClass,
- activateLayout,
+ };
+}
+
+export function useGetLayoutConfig() {
+ const layoutConfig = ref(null) as Ref;
+ const layout = ref("square") as Ref;
+
+ function loadLayoutConfig() {
+ AlbumService.getLayout().then((data) => {
+ layoutConfig.value = data.data;
+ });
+ }
+
+ return {
+ layout,
+ layoutConfig,
+ loadLayoutConfig,
};
}
diff --git a/resources/js/lychee.d.ts b/resources/js/lychee.d.ts
index a9c1076ed91..39aacddbb49 100644
--- a/resources/js/lychee.d.ts
+++ b/resources/js/lychee.d.ts
@@ -564,6 +564,7 @@ declare namespace App.Http.Resources.Root {
declare namespace App.Http.Resources.Search {
export type InitResource = {
search_minimum_length: number;
+ photo_layout: App.Enum.PhotoLayoutType;
};
export type ResultsResource = {
albums: App.Http.Resources.Models.ThumbAlbumResource[] | Array;
diff --git a/resources/js/views/gallery-panels/Album.vue b/resources/js/views/gallery-panels/Album.vue
index 445cc7bc38e..dc261eff055 100644
--- a/resources/js/views/gallery-panels/Album.vue
+++ b/resources/js/views/gallery-panels/Album.vue
@@ -55,11 +55,11 @@
:selected-albums="selectedAlbumsIds"
/>
(() => {
if (config.value !== undefined) {
@@ -212,7 +218,7 @@ const configForMenu = computed(()
photo_layout: "justified",
};
});
-const albumForMenu = albumid.value !== "" ? album : null;
+const albumForMenu = albumid.value !== "" ? album : undefined;
const title = computed(() => {
if (album.value === undefined) {
@@ -315,7 +321,7 @@ if (albumid.value !== "") {
}
searchInit();
-loadLayout();
+loadLayoutConfig();
if (lycheeStore.isSearchActive) {
search(lycheeStore.search_term);