Add sidebar to gallery for filtering and bulk actions

This commit is contained in:
Julian Stirling 2026-06-25 17:00:03 +01:00
parent 2dcd386661
commit 8d201dd1a9
5 changed files with 154 additions and 66 deletions

View file

@ -198,6 +198,63 @@
} }
} }
.ofm-tab {
--ofm-navbar-height: 50px;
}
.ofm-tab-navbar {
position: sticky;
top: 0;
z-index: 1000;
display: flex;
justify-content: flex-end;
height: var(--ofm-navbar-height);
border-width: 0 0 1px;
border-style: solid;
border-color: rgba(180, 180, 180, 0.25);
padding-right: 20px;
background: #fff;
}
.hook-inverse() {
.ofm-tab-navbar {
background: #252525;
}
}
/*
* Sidebar
*/
.ofm-container-with-sidebar
{
display: grid;
grid-template-columns: 1fr 0;
height: 100%;
}
.ofm-container-with-sidebar.sidebar-open {
grid-template-columns: 1fr 250px;
}
.ofm-sidebar {
padding: 10px;
overflow: hidden;
border-left: none;
display: none;
background:#f5f5f5;
}
.ofm-sidebar.open {
border-left: 1px solid rgba(180, 180, 180, 0.25);
display: block;
}
.hook-inverse() {
.ofm-sidebar{
background: #282626;
}
}
/* /*
* Links * Links
*/ */

View file

@ -267,7 +267,6 @@ export default {
#container-left { #container-left {
overflow: auto; overflow: auto;
scrollbar-gutter: stable;
background-color: rgba(180, 180, 180, 0.025); background-color: rgba(180, 180, 180, 0.025);
width: 100%; width: 100%;
height: 100%; height: 100%;

View file

@ -104,7 +104,8 @@ export default {
.multi-select { .multi-select {
position: relative; position: relative;
width: 250px; width: 100%;
max-width: 250px;
} }
.dropdown-top { .dropdown-top {

View file

@ -2,7 +2,7 @@
<div <div
v-if="!(requireConnection && !ready)" v-if="!(requireConnection && !ready)"
:hidden="currentTab != tabID" :hidden="currentTab != tabID"
class="uk-width-expand uk-height-1-1" class="ofm-tab uk-width-expand uk-height-1-1"
> >
<slot></slot> <slot></slot>
</div> </div>

View file

@ -1,47 +1,10 @@
<template> <template>
<div ref="galleryDisplay" class="gallery-display uk-padding uk-padding-remove-top"> <div ref="galleryDisplay" class="gallery-display">
<!-- Gallery nav bar --> <!-- Gallery nav bar -->
<nav class="gallery-navbar uk-navbar-container uk-navbar-transparent" uk-navbar="mode: click"> <nav class="ofm-tab-navbar">
<!-- Right side buttons --> <!-- Right side buttons -->
<div class="uk-navbar-right"> <div class="uk-navbar-right">
<div class="uk-grid"> <div class="uk-grid">
<div class="gallery-button">
<multi-select-dropdown
v-model="selectedCardTypes"
:options="allCardTypes"
title="Filter Gallery"
/>
</div>
<div class="gallery-button">
<action-button
class="uk-width-1-1"
thing="smart_scan"
action="stitch_all_scans"
submit-label="Stitch All Remaining"
:can-terminate="true"
:button-primary="false"
:modal-progress="true"
:requires-confirmation="true"
:confirmation-message="'<h3>Stitch all unstitched scans?</h3><br>Depending on the number and size of scans, this may be slow, and your microscope should not be used during the stitching.'"
@error="modalError"
/>
</div>
<div class="gallery-button">
<action-button
class="uk-width-1-1"
thing="gallery"
action="delete_all_data"
submit-label="Delete All"
:submit-data="{ card_types: selectedCardTypes }"
:is-disabled="totalPages == 0"
:can-terminate="true"
:button-primary="false"
:modal-progress="true"
:requires-confirmation="true"
:confirmation-message="deleteAllConfirmationMessage"
@error="modalError"
/>
</div>
<div> <div>
<button <button
class="uk-button uk-button-default uk-width-1-1 gallery-button" class="uk-button uk-button-default uk-width-1-1 gallery-button"
@ -51,6 +14,15 @@
Refresh Refresh
</button> </button>
</div> </div>
<div>
<button
class="sidebar-toggle uk-button uk-button-default uk-width-1-1 gallery-button"
type="button"
@click="sidebarOpen = !sidebarOpen"
>
<div class="sidebar-toggle-text" :class="{ open: sidebarOpen }"></div>
</button>
</div>
</div> </div>
</div> </div>
</nav> </nav>
@ -58,34 +30,74 @@
<gallery-modal ref="viewerModal" :selected-item="selectedItem" :base-uri="baseUri" /> <gallery-modal ref="viewerModal" :selected-item="selectedItem" :base-uri="baseUri" />
<!-- Gallery --> <!-- Gallery -->
<div v-if="ready" class="uk-padding-remove-top" uk-lightbox="toggle: .lightbox-link"> <div
v-if="ready"
class="ofm-container-with-sidebar"
:class="{ 'sidebar-open': sidebarOpen }"
uk-lightbox="toggle: .lightbox-link"
>
<!-- Gallery capture cards --> <!-- Gallery capture cards -->
<div class="gallery-grid uk-grid-match" uk-grid> <main class="gallery-main">
<div v-if="noItems"> <div class="gallery-grid uk-grid-match" uk-grid>
<h2>Nothing to show</h2> <div v-if="noItems">
<p>There is no captured data to show.</p> <h2>Nothing to show</h2>
<p>There is no captured data to show.</p>
</div>
<div v-for="itemData in paginatedItems" :key="itemData.id">
<gallery-card
:item-data="itemData"
@viewer-requested="showItem"
@update-requested="refreshGallery"
/>
</div>
</div> </div>
<div v-for="itemData in paginatedItems" :key="itemData.id"> <PaginateLinks
<gallery-card :total-pages="totalPages"
:item-data="itemData" :current-page="currentPage"
@viewer-requested="showItem" @change-page="changePage"
@update-requested="refreshGallery" />
</main>
<aside id="sidebar" class="ofm-sidebar" :class="{ open: sidebarOpen }">
<div class="gallery-button">
<multi-select-dropdown
v-model="selectedCardTypes"
:options="allCardTypes"
title="Filter Gallery"
/> />
</div> </div>
</div> <div
v-for="bulkAction in bulkActions"
:key="'action' + bulkAction.thing + bulkAction.action"
class="gallery-button"
>
<server-specified-action-button :action-data="bulkAction" @error="modalError" />
</div>
<div class="gallery-button">
<action-button
class="uk-width-1-1"
thing="gallery"
action="delete_all_data"
submit-label="Delete All"
:submit-data="{ card_types: selectedCardTypes }"
:is-disabled="totalPages == 0"
:can-terminate="true"
:button-primary="false"
:modal-progress="true"
:requires-confirmation="true"
:confirmation-message="deleteAllConfirmationMessage"
@error="modalError"
/>
</div>
</aside>
</div> </div>
<PaginateLinks
:total-pages="totalPages"
:current-page="currentPage"
@change-page="changePage"
/>
</div> </div>
</template> </template>
<script> <script>
import PaginateLinks from "@/components/genericComponents/paginateLinks.vue"; import PaginateLinks from "@/components/genericComponents/paginateLinks.vue";
import MultiSelectDropdown from "@/components/genericComponents/multiSelectDropdown.vue"; import MultiSelectDropdown from "@/components/genericComponents/multiSelectDropdown.vue";
import actionButton from "../labThingsComponents/actionButton.vue"; import actionButton from "@/components/labThingsComponents/actionButton.vue";
import ServerSpecifiedActionButton from "@/components/labThingsComponents/serverSpecifiedActionButton.vue";
import galleryCard from "./galleryComponents/galleryCard.vue"; import galleryCard from "./galleryComponents/galleryCard.vue";
import galleryModal from "./galleryComponents/galleryViewer.vue"; import galleryModal from "./galleryComponents/galleryViewer.vue";
import { eventBus } from "../../eventBus.js"; import { eventBus } from "../../eventBus.js";
@ -102,6 +114,7 @@ export default {
galleryModal, galleryModal,
PaginateLinks, PaginateLinks,
MultiSelectDropdown, MultiSelectDropdown,
ServerSpecifiedActionButton,
}, },
emits: ["scrollTop"], emits: ["scrollTop"],
@ -115,6 +128,8 @@ export default {
itemsPerPage: 18, itemsPerPage: 18,
selectedCardTypes: [], selectedCardTypes: [],
allCardTypes: [], allCardTypes: [],
bulkActions: [],
sidebarOpen: false,
}; };
}, },
@ -167,6 +182,7 @@ export default {
); );
this.allCardTypes = await this.readThingProperty("gallery", "card_types"); this.allCardTypes = await this.readThingProperty("gallery", "card_types");
this.selectedCardTypes = this.allCardTypes; this.selectedCardTypes = this.allCardTypes;
this.bulkActions = await this.readThingProperty("gallery", "bulk_actions");
// Update on mount (does nothing if not connected) // Update on mount (does nothing if not connected)
await this.refreshGallery(); await this.refreshGallery();
// A global signal listener to perform a gallery refresh // A global signal listener to perform a gallery refresh
@ -239,19 +255,34 @@ export default {
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.gallery-navbar { .gallery-display {
border-width: 0 0 1px; height: 100%;
border-style: solid; overflow-y: hidden;
border-color: rgba(180, 180, 180, 0.25);
} }
.gallery-navbar, .gallery-main {
.gallery-folder-heading { height: calc(100% - var(--ofm-navbar-height));
margin-bottom: 30px; box-sizing: border-box;
overflow-y: auto;
padding-left: 20px;
padding-top: 20px;
} }
.gallery-button { .gallery-button {
margin-top: 5px; margin-top: 5px;
margin-bottom: 2px; margin-bottom: 2px;
} }
.sidebar-toggle {
width: 40px;
}
.sidebar-toggle-text {
font-size: 24px;
transition: transform 0.25s ease;
}
.sidebar-toggle-text.open {
transform: rotate(180deg);
}
</style> </style>