1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-18 09:35:20 +02:00
joplin/packages/app-desktop/gui/ResizableLayout/utils/validateLayout.ts

117 lines
3.6 KiB
TypeScript

import produce from 'immer';
import iterateItems from './iterateItems';
import { LayoutItem, LayoutItemDirection } from './types';
function isLastVisible(itemIndex: number, item: LayoutItem, parent: LayoutItem) {
if (item.visible === false) return false;
for (let i = parent.children.length - 1; i >= 0; i--) {
const child = parent.children[i];
if (child && child.visible !== false) return i === itemIndex;
}
return false;
}
function updateItemSize(itemIndex: number, itemDraft: LayoutItem, parent: LayoutItem) {
if (!parent) return;
// If a container has only one child, this child should not
// have a width and height, and simply fill up the container
if (parent.children.length === 1) {
delete itemDraft.width;
delete itemDraft.height;
}
// If all children of a container have a fixed width, the
// latest visible child should have a flexible width (i.e. no "width"
// property), so that it fills up the remaining space
if (isLastVisible(itemIndex, itemDraft, parent)) {
let allChildrenAreSized = true;
for (const child of parent.children) {
if (child.visible === false) continue;
if (parent.direction === LayoutItemDirection.Row) {
if (!child.width) {
allChildrenAreSized = false;
break;
}
} else {
if (!child.height) {
allChildrenAreSized = false;
break;
}
}
}
if (allChildrenAreSized) {
if (parent.direction === LayoutItemDirection.Row) {
delete itemDraft.width;
} else {
delete itemDraft.height;
}
}
}
}
// All items should be resizable, except for the root and the latest visible child
// of a container.
function updateResizeRules(itemIndex: number, itemDraft: LayoutItem, parent: LayoutItem) {
if (!parent) return;
const isLastVisibleChild = isLastVisible(itemIndex, itemDraft, parent);
itemDraft.resizableRight = parent.direction === LayoutItemDirection.Row && !isLastVisibleChild;
itemDraft.resizableBottom = parent.direction === LayoutItemDirection.Column && !isLastVisibleChild;
}
// Container direction should alternate between row (for the root) and
// columns, then rows again.
function updateDirection(_itemIndex: number, itemDraft: LayoutItem, parent: LayoutItem) {
if (!parent) {
itemDraft.direction = LayoutItemDirection.Row;
} else {
itemDraft.direction = parent.direction === LayoutItemDirection.Row ? LayoutItemDirection.Column : LayoutItemDirection.Row;
}
}
function itemShouldBeVisible(item: LayoutItem): boolean {
if (!item.children) return item.visible !== false;
let oneIsVisible = false;
for (const child of item.children) {
if (itemShouldBeVisible(child)) {
oneIsVisible = true;
break;
}
}
return oneIsVisible;
}
// If all children of a container are hidden, the container should be
// hidden too. A container visiblity cannot be changed by the user.
function updateContainerVisibility(_itemIndex: number, itemDraft: LayoutItem, _parent: LayoutItem) {
if (itemDraft.children) {
itemDraft.visible = itemShouldBeVisible(itemDraft);
} else {
itemDraft.visible = itemDraft.visible !== false;
}
}
export default function validateLayout(layout: LayoutItem): LayoutItem {
if (!layout) throw new Error('Layout is null');
if (!layout.children || !layout.children.length) throw new Error('Root does not have children');
return produce(layout, (draft: LayoutItem) => {
draft.isRoot = true;
iterateItems(draft, (itemIndex: number, itemDraft: LayoutItem, parent: LayoutItem) => {
updateItemSize(itemIndex, itemDraft, parent);
updateResizeRules(itemIndex, itemDraft, parent);
updateDirection(itemIndex, itemDraft, parent);
updateContainerVisibility(itemIndex, itemDraft, parent);
return true;
});
});
}