mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-20 15:23:50 +02:00
224 lines
6.5 KiB
JavaScript
224 lines
6.5 KiB
JavaScript
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
|
import { storeToRefs } from 'pinia';
|
|
|
|
import { useAppearanceSettingsStore } from '../stores';
|
|
|
|
export function useAuthenticatedLayoutResizable() {
|
|
const navCollapsedPx = 64;
|
|
const navDefaultPx = 240;
|
|
const navMinPx = 180;
|
|
const navMaxPx = 300;
|
|
const asideMaxPx = 500;
|
|
|
|
const appearanceStore = useAppearanceSettingsStore();
|
|
const { setAsideWidth, setNavWidth } = appearanceStore;
|
|
const { asideWidth, isNavCollapsed, isSideBarTabShow, navWidth } =
|
|
storeToRefs(appearanceStore);
|
|
|
|
const fallbackWidth =
|
|
typeof window !== 'undefined' && window.innerWidth
|
|
? window.innerWidth
|
|
: 1200;
|
|
|
|
const panelGroupRef = ref(null);
|
|
const navPanelRef = ref(null);
|
|
const asidePanelRef = ref(null);
|
|
const navExpandedSize = ref(null);
|
|
const groupWidth = ref(fallbackWidth);
|
|
const draggingCount = ref(0);
|
|
let resizeObserver = null;
|
|
|
|
const getGroupWidthRaw = () => {
|
|
const element = panelGroupRef.value?.$el ?? panelGroupRef.value;
|
|
const width = element?.getBoundingClientRect?.().width;
|
|
return Number.isFinite(width) ? width : null;
|
|
};
|
|
|
|
const getGroupWidth = () => {
|
|
const width = getGroupWidthRaw();
|
|
return Number.isFinite(width) && width > 0 ? width : fallbackWidth;
|
|
};
|
|
|
|
const resolveDraggingPayload = (payload) => {
|
|
if (typeof payload === 'boolean') {
|
|
return payload;
|
|
}
|
|
if (payload && typeof payload === 'object') {
|
|
if (typeof payload.detail === 'boolean') {
|
|
return payload.detail;
|
|
}
|
|
if (typeof payload.dragging === 'boolean') {
|
|
return payload.dragging;
|
|
}
|
|
}
|
|
return Boolean(payload);
|
|
};
|
|
|
|
const setIsDragging = (payload) => {
|
|
const isDragging = resolveDraggingPayload(payload);
|
|
const next = draggingCount.value + (isDragging ? 1 : -1);
|
|
draggingCount.value = Math.max(0, next);
|
|
};
|
|
|
|
const pxToPercent = (px, groupWidth, min = 1) => {
|
|
const w = groupWidth ?? getGroupWidth();
|
|
return Math.min(100, Math.max(min, (px / w) * 100));
|
|
};
|
|
|
|
const percentToPx = (percent, groupWidth) => (percent / 100) * groupWidth;
|
|
|
|
const isAsideCollapsed = (layout) =>
|
|
Array.isArray(layout) &&
|
|
layout.length >= 3 &&
|
|
layout[layout.length - 1] <= 1;
|
|
|
|
const navCollapsedSize = computed(() =>
|
|
pxToPercent(navCollapsedPx, groupWidth.value)
|
|
);
|
|
const navExpandedPx = computed(() => navWidth.value || navDefaultPx);
|
|
|
|
const navDefaultSize = computed(() =>
|
|
isNavCollapsed.value
|
|
? navCollapsedSize.value
|
|
: pxToPercent(navExpandedPx.value)
|
|
);
|
|
|
|
const navMinSize = computed(() =>
|
|
isNavCollapsed.value ? navCollapsedSize.value : pxToPercent(navMinPx)
|
|
);
|
|
const navMaxSize = computed(() =>
|
|
isNavCollapsed.value ? navCollapsedSize.value : pxToPercent(navMaxPx)
|
|
);
|
|
|
|
const asideDefaultSize = computed(() =>
|
|
pxToPercent(asideWidth.value, undefined, 0)
|
|
);
|
|
const asideMaxSize = computed(() => pxToPercent(asideMaxPx, undefined, 0));
|
|
|
|
const mainDefaultSize = computed(
|
|
() =>
|
|
100 -
|
|
navDefaultSize.value -
|
|
(isSideBarTabShow.value ? asideDefaultSize.value : 0)
|
|
);
|
|
|
|
const handleLayout = (sizes) => {
|
|
if (!Array.isArray(sizes) || sizes.length < 2) {
|
|
return;
|
|
}
|
|
|
|
if (draggingCount.value === 0) {
|
|
return;
|
|
}
|
|
|
|
const rawWidth = getGroupWidthRaw();
|
|
if (!Number.isFinite(rawWidth) || rawWidth <= 0) {
|
|
return;
|
|
}
|
|
const width = rawWidth;
|
|
|
|
const navSize = sizes[0];
|
|
if (!isNavCollapsed.value && Number.isFinite(navSize) && navSize > 0) {
|
|
navExpandedSize.value = navSize;
|
|
setNavWidth(Math.round(percentToPx(navSize, width)));
|
|
}
|
|
|
|
if (!isSideBarTabShow.value || sizes.length < 3) {
|
|
return;
|
|
}
|
|
|
|
const asideSize = sizes[sizes.length - 1];
|
|
if (!Number.isFinite(asideSize)) {
|
|
return;
|
|
}
|
|
|
|
if (asideSize <= 1) {
|
|
setAsideWidth(0);
|
|
return;
|
|
}
|
|
|
|
setAsideWidth(Math.round(percentToPx(asideSize, width)));
|
|
};
|
|
|
|
const resizeNavPanel = (targetSize) =>
|
|
navPanelRef.value?.resize?.(targetSize);
|
|
|
|
const resizeAsidePanel = (targetSize) =>
|
|
asidePanelRef.value?.resize?.(targetSize);
|
|
|
|
const updateGroupWidth = () => {
|
|
const width = getGroupWidth();
|
|
groupWidth.value = width;
|
|
|
|
if (isNavCollapsed.value) {
|
|
resizeNavPanel(navCollapsedSize.value);
|
|
} else {
|
|
const targetSize = pxToPercent(navExpandedPx.value, width);
|
|
navExpandedSize.value = targetSize;
|
|
resizeNavPanel(targetSize);
|
|
}
|
|
|
|
if (!isSideBarTabShow.value) {
|
|
return;
|
|
}
|
|
|
|
const storedAsidePx = asideWidth.value;
|
|
const asideTargetSize =
|
|
storedAsidePx > 0 ? pxToPercent(storedAsidePx, width, 0) : 0;
|
|
resizeAsidePanel(asideTargetSize);
|
|
};
|
|
|
|
watch(isNavCollapsed, async (collapsed) => {
|
|
await nextTick();
|
|
if (collapsed) {
|
|
resizeNavPanel(navCollapsedSize.value);
|
|
return;
|
|
}
|
|
const targetSize =
|
|
navExpandedSize.value ?? pxToPercent(navExpandedPx.value);
|
|
resizeNavPanel(targetSize);
|
|
});
|
|
|
|
onMounted(async () => {
|
|
await nextTick();
|
|
updateGroupWidth();
|
|
let panelSize = null;
|
|
panelSize = navPanelRef.value?.getSize?.() ?? null;
|
|
|
|
navExpandedSize.value = panelSize ?? navDefaultSize.value;
|
|
if (isNavCollapsed.value) {
|
|
resizeNavPanel(navCollapsedSize.value);
|
|
}
|
|
|
|
const element = panelGroupRef.value?.$el ?? panelGroupRef.value;
|
|
if (element && typeof ResizeObserver !== 'undefined') {
|
|
resizeObserver = new ResizeObserver(updateGroupWidth);
|
|
resizeObserver.observe(element);
|
|
}
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
if (resizeObserver) {
|
|
resizeObserver.disconnect();
|
|
resizeObserver = null;
|
|
}
|
|
});
|
|
|
|
return {
|
|
panelGroupRef,
|
|
navPanelRef,
|
|
asidePanelRef,
|
|
navDefaultSize,
|
|
navMinSize,
|
|
navMaxSize,
|
|
asideDefaultSize,
|
|
asideMaxSize,
|
|
mainDefaultSize,
|
|
handleLayout,
|
|
setIsDragging,
|
|
isAsideCollapsed,
|
|
isNavCollapsed,
|
|
isSideBarTabShow
|
|
};
|
|
}
|