mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-07 14:56:06 +02:00
simplify main layout resizing
This commit is contained in:
@@ -1,12 +1,11 @@
|
|||||||
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue';
|
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
import { useAppearanceSettingsStore } from '../stores';
|
import { useAppearanceSettingsStore } from '../stores';
|
||||||
|
|
||||||
import configRepository from '../service/config';
|
|
||||||
|
|
||||||
export function useMainLayoutResizable() {
|
export function useMainLayoutResizable() {
|
||||||
const asideMaxPx = 700;
|
const asideMaxPx = 700;
|
||||||
|
const mainMinPx = 320;
|
||||||
|
|
||||||
const appearanceStore = useAppearanceSettingsStore();
|
const appearanceStore = useAppearanceSettingsStore();
|
||||||
const { setAsideWidth } = appearanceStore;
|
const { setAsideWidth } = appearanceStore;
|
||||||
@@ -21,10 +20,9 @@ export function useMainLayoutResizable() {
|
|||||||
const panelGroupRef = ref(null);
|
const panelGroupRef = ref(null);
|
||||||
const asidePanelRef = ref(null);
|
const asidePanelRef = ref(null);
|
||||||
const groupWidth = ref(fallbackWidth);
|
const groupWidth = ref(fallbackWidth);
|
||||||
const draggingCount = ref(0);
|
|
||||||
const lastLayoutSizes = ref(null);
|
|
||||||
let resizeObserver = null;
|
let resizeObserver = null;
|
||||||
|
|
||||||
|
// size helpers: panelGroupRef, groupWidth, fallbackWidth
|
||||||
const getGroupWidthRaw = () => {
|
const getGroupWidthRaw = () => {
|
||||||
const element = panelGroupRef.value?.$el ?? panelGroupRef.value;
|
const element = panelGroupRef.value?.$el ?? panelGroupRef.value;
|
||||||
const width = element?.getBoundingClientRect?.().width;
|
const width = element?.getBoundingClientRect?.().width;
|
||||||
@@ -36,77 +34,47 @@ export function useMainLayoutResizable() {
|
|||||||
return Number.isFinite(width) && width > 0 ? width : fallbackWidth;
|
return Number.isFinite(width) && width > 0 ? width : fallbackWidth;
|
||||||
};
|
};
|
||||||
|
|
||||||
const resolveDraggingPayload = (payload) => {
|
const pxToPercent = (px, width, min = 1) => {
|
||||||
if (typeof payload === 'boolean') {
|
const w = Number.isFinite(width) && width > 0 ? width : getGroupWidth();
|
||||||
return payload;
|
|
||||||
}
|
|
||||||
if (payload && typeof payload === 'object') {
|
|
||||||
if (typeof payload.detail === 'boolean') {
|
|
||||||
return payload.detail;
|
|
||||||
}
|
|
||||||
if (typeof payload.dragging === 'boolean') {
|
|
||||||
return payload.dragging;
|
|
||||||
}
|
|
||||||
if (payload.detail && typeof payload.detail === 'object') {
|
|
||||||
if (typeof payload.detail.dragging === 'boolean') {
|
|
||||||
return payload.detail.dragging;
|
|
||||||
}
|
|
||||||
if (typeof payload.detail.isDragging === 'boolean') {
|
|
||||||
return payload.detail.isDragging;
|
|
||||||
}
|
|
||||||
if (typeof payload.detail.value === 'boolean') {
|
|
||||||
return payload.detail.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const setIsDragging = (payload) => {
|
|
||||||
const isDragging = resolveDraggingPayload(payload);
|
|
||||||
if (typeof isDragging !== 'boolean') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const wasDragging = draggingCount.value > 0;
|
|
||||||
const next = draggingCount.value + (isDragging ? 1 : -1);
|
|
||||||
draggingCount.value = Math.max(0, next);
|
|
||||||
|
|
||||||
if (wasDragging && draggingCount.value === 0 && lastLayoutSizes.value) {
|
|
||||||
handleLayout(lastLayoutSizes.value, { force: true });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const pxToPercent = (px, groupWidth, min = 1) => {
|
|
||||||
const w = groupWidth ?? getGroupWidth();
|
|
||||||
return Math.min(100, Math.max(min, (px / w) * 100));
|
return Math.min(100, Math.max(min, (px / w) * 100));
|
||||||
};
|
};
|
||||||
|
|
||||||
const percentToPx = (percent, groupWidth) => (percent / 100) * groupWidth;
|
const percentToPx = (percent, groupWidth) => (percent / 100) * groupWidth;
|
||||||
|
|
||||||
|
const getMaxAsidePx = (width) =>
|
||||||
|
Math.min(asideMaxPx, Math.max(0, width - mainMinPx));
|
||||||
|
|
||||||
|
const clampAsidePx = (px, width) =>
|
||||||
|
Math.min(getMaxAsidePx(width), Math.max(0, px));
|
||||||
|
|
||||||
|
// layout state: isAsideCollapsed, asideMaxSize, asideDefaultSize, mainDefaultSize
|
||||||
const isAsideCollapsed = (layout) =>
|
const isAsideCollapsed = (layout) =>
|
||||||
Array.isArray(layout) &&
|
Array.isArray(layout) &&
|
||||||
layout.length >= 2 &&
|
layout.length >= 2 &&
|
||||||
layout[layout.length - 1] <= 1;
|
layout[layout.length - 1] <= 1;
|
||||||
|
|
||||||
const asideDefaultSize = computed(() =>
|
const asideMaxSize = computed(() =>
|
||||||
pxToPercent(asideWidth.value, undefined, 0)
|
pxToPercent(getMaxAsidePx(groupWidth.value), groupWidth.value, 0)
|
||||||
);
|
);
|
||||||
const asideMaxSize = computed(() => pxToPercent(asideMaxPx, undefined, 0));
|
|
||||||
|
const asideDefaultSize = computed(() => {
|
||||||
|
if (!isSideBarTabShow.value) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const percent = pxToPercent(asideWidth.value, groupWidth.value, 0);
|
||||||
|
return Math.min(asideMaxSize.value, percent);
|
||||||
|
});
|
||||||
|
|
||||||
const mainDefaultSize = computed(
|
const mainDefaultSize = computed(
|
||||||
() => 100 - (isSideBarTabShow.value ? asideDefaultSize.value : 0)
|
() => 100 - (isSideBarTabShow.value ? asideDefaultSize.value : 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleLayout = (sizes, { force = false } = {}) => {
|
// drag -> store: handleLayout, asideWidth
|
||||||
lastLayoutSizes.value = Array.isArray(sizes) ? [...sizes] : sizes;
|
const handleLayout = (sizes) => {
|
||||||
if (!Array.isArray(sizes) || sizes.length < 1) {
|
if (!Array.isArray(sizes) || sizes.length < 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!force && draggingCount.value === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isSideBarTabShow.value || sizes.length < 2) {
|
if (!isSideBarTabShow.value || sizes.length < 2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -122,56 +90,41 @@ export function useMainLayoutResizable() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asideSize <= 1) {
|
const nextAsidePx =
|
||||||
setAsideWidth(0);
|
asideSize <= 1
|
||||||
configRepository.setInt('VRCX_sidePanelWidth', 0);
|
? 0
|
||||||
|
: clampAsidePx(
|
||||||
|
Math.round(percentToPx(asideSize, width)),
|
||||||
|
width
|
||||||
|
);
|
||||||
|
if (nextAsidePx === asideWidth.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextAsidePx = Math.round(percentToPx(asideSize, width));
|
|
||||||
setAsideWidth(nextAsidePx);
|
setAsideWidth(nextAsidePx);
|
||||||
configRepository.setInt('VRCX_sidePanelWidth', nextAsidePx);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// sync store -> panel: resizeAsidePanel, syncAsidePanelSize
|
||||||
const resizeAsidePanel = (targetSize) =>
|
const resizeAsidePanel = (targetSize) =>
|
||||||
asidePanelRef.value?.resize?.(targetSize);
|
asidePanelRef.value?.resize?.(targetSize);
|
||||||
|
|
||||||
const updateGroupWidth = () => {
|
const syncAsidePanelSize = (width) => {
|
||||||
const width = getGroupWidth();
|
|
||||||
groupWidth.value = width;
|
|
||||||
|
|
||||||
if (!isSideBarTabShow.value) {
|
if (!isSideBarTabShow.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const clampedAsidePx = clampAsidePx(asideWidth.value, width);
|
||||||
void syncAsidePanelWidth(width);
|
if (clampedAsidePx !== asideWidth.value) {
|
||||||
|
setAsideWidth(clampedAsidePx);
|
||||||
|
}
|
||||||
|
const asideTargetSize =
|
||||||
|
clampedAsidePx > 0 ? pxToPercent(clampedAsidePx, width, 0) : 0;
|
||||||
|
resizeAsidePanel(asideTargetSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
let asideSyncPromise = null;
|
// window resize: updateGroupWidth, resizeObserver
|
||||||
const syncAsidePanelWidth = async (width) => {
|
const updateGroupWidth = () => {
|
||||||
if (asideSyncPromise) {
|
const width = getGroupWidth();
|
||||||
return asideSyncPromise;
|
groupWidth.value = width;
|
||||||
}
|
syncAsidePanelSize(width);
|
||||||
asideSyncPromise = (async () => {
|
|
||||||
const storedAsidePx = await configRepository.getInt(
|
|
||||||
'VRCX_sidePanelWidth',
|
|
||||||
asideWidth.value
|
|
||||||
);
|
|
||||||
if (
|
|
||||||
Number.isFinite(storedAsidePx) &&
|
|
||||||
storedAsidePx !== asideWidth.value
|
|
||||||
) {
|
|
||||||
setAsideWidth(storedAsidePx);
|
|
||||||
}
|
|
||||||
const asideTargetSize =
|
|
||||||
storedAsidePx > 0 ? pxToPercent(storedAsidePx, width, 0) : 0;
|
|
||||||
resizeAsidePanel(asideTargetSize);
|
|
||||||
})();
|
|
||||||
try {
|
|
||||||
return await asideSyncPromise;
|
|
||||||
} finally {
|
|
||||||
asideSyncPromise = null;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@@ -185,6 +138,13 @@ export function useMainLayoutResizable() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [isSideBarTabShow.value, asideWidth.value],
|
||||||
|
() => {
|
||||||
|
syncAsidePanelSize(groupWidth.value);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (resizeObserver) {
|
if (resizeObserver) {
|
||||||
resizeObserver.disconnect();
|
resizeObserver.disconnect();
|
||||||
@@ -200,7 +160,6 @@ export function useMainLayoutResizable() {
|
|||||||
asideMaxPx,
|
asideMaxPx,
|
||||||
mainDefaultSize,
|
mainDefaultSize,
|
||||||
handleLayout,
|
handleLayout,
|
||||||
setIsDragging,
|
|
||||||
isAsideCollapsed,
|
isAsideCollapsed,
|
||||||
isNavCollapsed,
|
isNavCollapsed,
|
||||||
isSideBarTabShow
|
isSideBarTabShow
|
||||||
|
|||||||
@@ -35,8 +35,7 @@
|
|||||||
:class="[
|
:class="[
|
||||||
isAsideCollapsed(layout) ? 'opacity-100' : 'opacity-0',
|
isAsideCollapsed(layout) ? 'opacity-100' : 'opacity-0',
|
||||||
'z-20 [&>div]:-translate-x-1/2'
|
'z-20 [&>div]:-translate-x-1/2'
|
||||||
]"
|
]"></ResizableHandle>
|
||||||
@dragging="setIsDragging"></ResizableHandle>
|
|
||||||
<ResizablePanel
|
<ResizablePanel
|
||||||
ref="asidePanelRef"
|
ref="asidePanelRef"
|
||||||
:default-size="asideDefaultSize"
|
:default-size="asideDefaultSize"
|
||||||
@@ -170,7 +169,6 @@
|
|||||||
asideMaxPx,
|
asideMaxPx,
|
||||||
mainDefaultSize,
|
mainDefaultSize,
|
||||||
handleLayout,
|
handleLayout,
|
||||||
setIsDragging,
|
|
||||||
isAsideCollapsed,
|
isAsideCollapsed,
|
||||||
isSideBarTabShow
|
isSideBarTabShow
|
||||||
} = useMainLayoutResizable();
|
} = useMainLayoutResizable();
|
||||||
|
|||||||
Reference in New Issue
Block a user