1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00
joplin/packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.ts
2020-11-13 17:09:28 +00:00

96 lines
3.1 KiB
TypeScript

import { useMemo } from 'react';
import { LayoutItem, Size } from './types';
const dragBarThickness = 5;
export interface LayoutItemSizes {
[key: string]: Size;
}
// Container always take the full space while the items within it need to
// accomodate for the resize handle.
export function itemSize(item: LayoutItem, parent: LayoutItem | null, sizes: LayoutItemSizes, isContainer: boolean): Size {
const parentResizableRight = !!parent && parent.resizableRight;
const parentResizableBottom = !!parent && parent.resizableBottom;
const rightGap = !isContainer && (item.resizableRight || parentResizableRight) ? dragBarThickness : 0;
const bottomGap = !isContainer && (item.resizableBottom || parentResizableBottom) ? dragBarThickness : 0;
return {
width: ('width' in item ? item.width : sizes[item.key].width) - rightGap,
height: ('height' in item ? item.height : sizes[item.key].height) - bottomGap,
};
}
// This calculate the size of each item within the layout. However
// the final size, as rendered by the component is determined by
// `itemSize()`, as it takes into account the resizer handle
function calculateChildrenSizes(item: LayoutItem, parent: LayoutItem | null, sizes: LayoutItemSizes, makeAllVisible: boolean): LayoutItemSizes {
if (!item.children) return sizes;
const parentSize = itemSize(item, parent, sizes, true);
const remainingSize: Size = {
width: parentSize.width,
height: parentSize.height,
};
const noWidthChildren: any[] = [];
const noHeightChildren: any[] = [];
for (const child of item.children) {
let w = 'width' in child ? child.width : null;
let h = 'height' in child ? child.height : null;
if (!makeAllVisible && child.visible === false) {
w = 0;
h = 0;
}
sizes[child.key] = { width: w, height: h };
if (w !== null) remainingSize.width -= w;
if (h !== null) remainingSize.height -= h;
if (w === null) noWidthChildren.push({ item: child, parent: item });
if (h === null) noHeightChildren.push({ item: child, parent: item });
}
if (noWidthChildren.length) {
const w = item.direction === 'row' ? Math.floor(remainingSize.width / noWidthChildren.length) : parentSize.width;
for (const child of noWidthChildren) {
const finalWidth = w;
sizes[child.item.key].width = finalWidth;
}
}
if (noHeightChildren.length) {
const h = item.direction === 'column' ? Math.floor(remainingSize.height / noHeightChildren.length) : parentSize.height;
for (const child of noHeightChildren) {
const finalHeight = h;
sizes[child.item.key].height = finalHeight;
}
}
for (const child of item.children) {
const childrenSizes = calculateChildrenSizes(child, parent, sizes, makeAllVisible);
sizes = { ...sizes, ...childrenSizes };
}
return sizes;
}
export default function useLayoutItemSizes(layout: LayoutItem, makeAllVisible: boolean = false) {
return useMemo(() => {
let sizes: LayoutItemSizes = {};
if (!('width' in layout) || !('height' in layout)) throw new Error('width and height are required on layout root');
sizes[layout.key] = {
width: layout.width,
height: layout.height,
};
sizes = calculateChildrenSizes(layout, null, sizes, makeAllVisible);
return sizes;
}, [layout, makeAllVisible]);
}