fix sidebar speed dial buttons
This commit is contained in:
parent
080d9ff1b4
commit
a01f72bbc1
@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { ref, nextTick } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useModalStore } from "@/stores/modal";
|
||||
import { useNotificationStore } from "@/stores/notifications-primevue"
|
||||
@ -19,17 +19,27 @@ import {
|
||||
NavArrowLeft,
|
||||
NavArrowRight,
|
||||
} from "@iconoir/vue";
|
||||
import SpeedDial from "primevue/speeddial";
|
||||
import SidebarSpeedDial from "./SidebarSpeedDial.vue";
|
||||
|
||||
const router = useRouter();
|
||||
const modalStore = useModalStore();
|
||||
const notifications = useNotificationStore();
|
||||
const isCollapsed = ref(false);
|
||||
const pendingOpen = ref(null);
|
||||
|
||||
const toggleSidebar = () => {
|
||||
isCollapsed.value = !isCollapsed.value;
|
||||
};
|
||||
|
||||
const openSidebarAndDial = (category) => {
|
||||
isCollapsed.value = false;
|
||||
pendingOpen.value = category.name;
|
||||
// ensure re-render picks up forceOpen
|
||||
nextTick(() => {
|
||||
pendingOpen.value = category.name;
|
||||
});
|
||||
};
|
||||
|
||||
const clientButtons = ref([
|
||||
{
|
||||
label: "Customer Lookup",
|
||||
@ -89,7 +99,7 @@ const createButtons = ref([
|
||||
},
|
||||
},
|
||||
{
|
||||
lable: "Note",
|
||||
label: "Note",
|
||||
command: () => {
|
||||
notifications.addWarning("Sending Notes coming soon!");
|
||||
}
|
||||
@ -100,7 +110,7 @@ const categories = ref([
|
||||
{ name: "Home", icon: Home, url: "/" },
|
||||
{ name: "Calendar", icon: Calendar, url: "/calendar" },
|
||||
{
|
||||
name: "Clients",
|
||||
name: "CRM",
|
||||
icon: Community,
|
||||
buttons: clientButtons,
|
||||
},
|
||||
@ -150,30 +160,22 @@ const handleCategoryClick = (category) => {
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<SpeedDial
|
||||
:model="category.buttons"
|
||||
direction="down"
|
||||
type="linear"
|
||||
radius="50"
|
||||
<SidebarSpeedDial
|
||||
v-if="!isCollapsed"
|
||||
>
|
||||
<template #button="{ toggleCallback }">
|
||||
<button
|
||||
class="sidebar-button"
|
||||
@click="toggleCallback"
|
||||
:key="category.name"
|
||||
>
|
||||
<component :is="category.icon" class="button-icon" />
|
||||
<span class="button-text">{{ category.name }}</span>
|
||||
</button>
|
||||
</template>
|
||||
<template #item="{ item, toggleCallback }">
|
||||
<button class="create-item" @click="toggleCallback" :key="item.label">
|
||||
<span class="p-menuitem-text">{{ item.label }}</span>
|
||||
</button>
|
||||
</template>
|
||||
</SpeedDial>
|
||||
<button v-else class="sidebar-button" :key="category.name" :title="category.name">
|
||||
:key="category.name"
|
||||
:icon="category.icon"
|
||||
:label="category.name"
|
||||
:items="category.buttons"
|
||||
:force-open="pendingOpen === category.name"
|
||||
@opened="pendingOpen = null"
|
||||
/>
|
||||
<button
|
||||
v-else
|
||||
class="sidebar-button"
|
||||
:key="category.name"
|
||||
:title="category.name"
|
||||
@click="openSidebarAndDial(category)"
|
||||
>
|
||||
<component :is="category.icon" class="button-icon" />
|
||||
</button>
|
||||
</template>
|
||||
@ -217,22 +219,6 @@ const handleCategoryClick = (category) => {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.create-item {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-color);
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.create-item:hover {
|
||||
background-color: var(--surface-hover);
|
||||
}
|
||||
|
||||
.button-text {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
@ -263,6 +249,13 @@ const handleCategoryClick = (category) => {
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.speeddial-caret {
|
||||
margin-right: 10px;
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.sidebar-button:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
@ -338,21 +331,63 @@ const handleCategoryClick = (category) => {
|
||||
}
|
||||
|
||||
/* SpeedDial customization */
|
||||
:deep(.p-speeddial) {
|
||||
position: relative;
|
||||
.sidebar-speeddial {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.p-speeddial-list) {
|
||||
.sidebar-submenu {
|
||||
background-color: var(--surface-card);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid var(--surface-border);
|
||||
padding: 4px;
|
||||
margin-top: 2px;
|
||||
border-radius: 6px;
|
||||
margin-top: 6px;
|
||||
padding: 6px 4px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
:deep(.p-speeddial-item) {
|
||||
margin: 2px 0;
|
||||
.sidebar-sub-button {
|
||||
border: none;
|
||||
background: transparent;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 8px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-color);
|
||||
transition: background-color 0.15s ease, transform 0.1s ease;
|
||||
}
|
||||
|
||||
.sidebar-sub-button:hover {
|
||||
background-color: var(--surface-hover);
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
.sub-button-text {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.sidebar-accordion-enter-active,
|
||||
.sidebar-accordion-leave-active {
|
||||
transition: max-height 0.25s ease, opacity 0.2s ease, margin 0.2s ease;
|
||||
}
|
||||
|
||||
.sidebar-accordion-enter-from,
|
||||
.sidebar-accordion-leave-to {
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.sidebar-accordion-enter-to,
|
||||
.sidebar-accordion-leave-from {
|
||||
max-height: 600px;
|
||||
opacity: 1;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
/* Responsive adjustments for smaller screens */
|
||||
|
||||
79
frontend/src/components/SidebarSpeedDial.vue
Normal file
79
frontend/src/components/SidebarSpeedDial.vue
Normal file
@ -0,0 +1,79 @@
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
icon: {
|
||||
type: [Object, Function],
|
||||
required: true,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
initiallyOpen: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
forceOpen: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["opened", "closed"]);
|
||||
|
||||
const isOpen = ref(props.initiallyOpen);
|
||||
|
||||
const toggle = () => {
|
||||
isOpen.value = !isOpen.value;
|
||||
};
|
||||
|
||||
const handleItem = (item) => {
|
||||
if (typeof item?.command === "function") {
|
||||
item.command();
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.forceOpen,
|
||||
(value) => {
|
||||
if (value) {
|
||||
isOpen.value = true;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
watch(isOpen, (value) => {
|
||||
if (value) {
|
||||
emit("opened", props.label);
|
||||
} else {
|
||||
emit("closed", props.label);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-speeddial" :class="{ open: isOpen }">
|
||||
<button class="sidebar-button" @click="toggle" :aria-expanded="isOpen">
|
||||
<component :is="icon" class="button-icon" />
|
||||
<span class="button-text">{{ label }}</span>
|
||||
<span class="speeddial-caret" aria-hidden="true">{{ isOpen ? "-" : "+" }}</span>
|
||||
</button>
|
||||
<transition name="sidebar-accordion">
|
||||
<div v-show="isOpen" class="sidebar-submenu">
|
||||
<button
|
||||
v-for="item in items"
|
||||
:key="item.label"
|
||||
class="sidebar-sub-button"
|
||||
@click="handleItem(item)"
|
||||
>
|
||||
<span class="sub-button-text">{{ item.label }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
Loading…
x
Reference in New Issue
Block a user