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);