simplify main layout resizing

This commit is contained in:
pa
2026-02-01 00:24:52 +09:00
parent 858fb076da
commit 91692229ae
2 changed files with 54 additions and 97 deletions
+52 -93
View File
@@ -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);
let asideSyncPromise = null;
const syncAsidePanelWidth = async (width) => {
if (asideSyncPromise) {
return asideSyncPromise;
}
asideSyncPromise = (async () => {
const storedAsidePx = await configRepository.getInt(
'VRCX_sidePanelWidth',
asideWidth.value
);
if (
Number.isFinite(storedAsidePx) &&
storedAsidePx !== asideWidth.value
) {
setAsideWidth(storedAsidePx);
} }
const asideTargetSize = const asideTargetSize =
storedAsidePx > 0 ? pxToPercent(storedAsidePx, width, 0) : 0; clampedAsidePx > 0 ? pxToPercent(clampedAsidePx, width, 0) : 0;
resizeAsidePanel(asideTargetSize); resizeAsidePanel(asideTargetSize);
})(); };
try {
return await asideSyncPromise; // window resize: updateGroupWidth, resizeObserver
} finally { const updateGroupWidth = () => {
asideSyncPromise = null; const width = getGroupWidth();
} groupWidth.value = width;
syncAsidePanelSize(width);
}; };
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
+1 -3
View File
@@ -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();