verstak
Version:
Verstak - Front-End Library
553 lines (496 loc) • 21.6 kB
JavaScript
import { ReactiveTreeNode, Mode, declare, runNonReactive } from "reactronic";
import { ElKind, Direction, H, V } from "./El.js";
import { clamp } from "./ElUtils.js";
import { Constants, CursorCommandDriver, ElDriver, ElLayoutInfo, InitialElLayoutInfo, StylingClassNameByAlignmentHorizontal, StylingClassNameByAlignmentVertical, StylingClassNameByAlignmentVerticalRowWise, StylingClassNameBySelfAlignmentHorizontal, StylingClassNameBySelfAlignmentVertical, StylingClassNameByPartitionAlignmentVertical } from "./ElDriver.js";
import { getPrioritiesForEmptySpaceDistribution, getPrioritiesForSizeChanging, relayout, relayoutUsingSplitter } from "./SplitViewMath.js";
import { Axis, BodyFontSize, Dimension, toPx } from "./Sizes.js";
import { HtmlDriver } from "./WebDriver.js";
export function ApplicationWindow(bodyOrDeclaration, bodyTask, key, mode, unmounted, preparation, preparationTask, mounting, finalization, signalArgs, basis) {
acquireVerstakStyleSheet();
const body = (document !== null && document !== void 0 ? document : global.document).body;
const driver = new StaticBlockDriver(body, "Page", false, el => el.kind = ElKind.block);
body.classList.add("v5k-body");
body.classList.add("v5k-block");
return declare(driver, bodyOrDeclaration, bodyTask, key, mode, unmounted, preparation, preparationTask, mounting, finalization, signalArgs, basis);
}
function acquireVerstakStyleSheet() {
const existing = findStyleByTitle("v5k");
if (existing === undefined) {
const style = document.createElement("style");
style.title = "v5k";
style.innerHTML = VerstakStyleSheetCode;
const head = document.getElementsByTagName("head")[0];
head.appendChild(style);
}
}
function findStyleByTitle(title) {
const styles = document.styleSheets;
for (let i = 0, l = styles.length; i < l; i++) {
const s = styles[i];
if (s.disabled)
continue;
if (s.title === title)
return s;
}
return undefined;
}
export function Block(bodyOrDeclaration, bodyTask, key, mode, unmounted, preparation, preparationTask, mounting, finalization, signalArgs, basis) {
return declare(Drivers.block, bodyOrDeclaration, bodyTask, key, mode, unmounted, preparation, preparationTask, mounting, finalization, signalArgs, basis);
}
export function Table(bodyOrDeclaration, bodyTask, key, mode, unmounted, preparation, preparationTask, mounting, finalization, signalArgs, basis) {
return declare(Drivers.table, bodyOrDeclaration, bodyTask, key, mode, unmounted, preparation, preparationTask, mounting, finalization, signalArgs, basis);
}
export function row(builder, shiftCursorDown) {
rowBreak(shiftCursorDown);
builder === null || builder === void 0 ? void 0 : builder();
}
export function Splitter(bodyOrDeclaration, bodyTask, key, mode, unmounted, preparation, preparationTask, mounting, finalization, signalArgs, basis) {
return declare(Drivers.splitter, bodyOrDeclaration, bodyTask, key, mode, unmounted, preparation, preparationTask, mounting, finalization, signalArgs, basis);
}
export function rowBreak(shiftCursorDown) {
declare(Drivers.partition);
}
export function declareSplitter(index, splitViewNode) {
const key = `splitter-${index}`;
return (Splitter({
key,
preparation() {
this.native.className = `splitter ${key}`;
},
body() {
const e = this.native;
const model = this.model;
const dataForSensor = e.dataForSensor;
dataForSensor.draggable = key;
dataForSensor.drag = key;
Fragment(() => {
var _a, _b, _c, _d;
const pointer = e.sensors.pointer;
if (pointer.dragSource === key) {
if (pointer.dragStarted) {
if (pointer.draggingOver) {
pointer.dropAllowed = true;
const initialSizesPx = pointer.getData();
const deltaPx = Math.floor(splitViewNode.element.splitView === Direction.horizontal
? pointer.positionX - pointer.startX : pointer.positionY - pointer.startY);
const clonedSizesPx = [];
for (const item of initialSizesPx) {
clonedSizesPx.push({ node: item.node, sizePx: item.sizePx });
}
runNonReactive(() => relayoutUsingSplitter(splitViewNode, deltaPx, index, clonedSizesPx));
if (pointer.dropped) {
(_a = model === null || model === void 0 ? void 0 : model.droppedAction) === null || _a === void 0 ? void 0 : _a.call(model, pointer);
}
}
else {
e.setAttribute("rx-dragging", "true");
const initialSizesPx = [];
for (const child of splitViewNode.children.items()) {
if (isSplitViewPartition(child.driver)) {
const sizePx = (_c = (_b = child.element.layoutInfo) === null || _b === void 0 ? void 0 : _b.effectiveSizePx) !== null && _c !== void 0 ? _c : 0;
initialSizesPx.push({ node: child, sizePx });
}
}
pointer.setData(initialSizesPx);
}
if (pointer.dragFinished) {
(_d = model === null || model === void 0 ? void 0 : model.dragFinishedAction) === null || _d === void 0 ? void 0 : _d.call(model, pointer);
e.setAttribute("rx-dragging", "false");
}
}
}
});
},
}));
}
export function cursor(place) {
declare(Drivers.cursor, {
body() {
this.place = place;
},
});
}
export function Group(bodyOrDeclaration, bodyTask, key, mode, unmounted, preparation, preparationTask, mounting, finalization, signalArgs, basis) {
return declare(Drivers.group, bodyOrDeclaration, bodyTask, key, mode, unmounted, preparation, preparationTask, mounting, finalization, signalArgs, basis);
}
export function Fragment(bodyOrDeclaration, bodyTask, key, mode, unmounted, preparation, preparationTask, mounting, finalization, signalArgs, basis) {
mode = mode !== undefined ? mode | Mode.fragment : Mode.fragment;
return declare(Drivers.fragment, bodyOrDeclaration, bodyTask, key, mode, unmounted, preparation, preparationTask, mounting, finalization, signalArgs, basis);
}
export class BlockDriver extends HtmlDriver {
rebuildBody(node) {
const el = node.element;
const result = super.rebuildBody(node);
if (el.splitView !== undefined) {
if (el.layoutInfo === undefined)
el.layoutInfo = new ElLayoutInfo(InitialElLayoutInfo);
const layoutInfo = el.layoutInfo;
layoutInfo.isUpdateFinished = false;
Fragment(h => {
const native = el.native;
const resize = native.sensors.resize;
for (const x of resize.resizedElements) {
const borderBoxPx = x.borderBoxSize[0];
const contentBoxPx = x.contentBoxSize[0];
layoutInfo.borderSizeXpx = borderBoxPx.inlineSize;
layoutInfo.borderSizeYpx = borderBoxPx.blockSize;
layoutInfo.contentSizeXpx = contentBoxPx.inlineSize;
layoutInfo.contentSizeYpx = contentBoxPx.blockSize;
}
});
const relayoutEl = Fragment({
body() {
const native = el.native;
const isHorizontal = el.splitView === Direction.horizontal;
if (layoutInfo.isUpdateFinished) {
const surroundingXpx = layoutInfo.borderSizeXpx - layoutInfo.contentSizeXpx;
const surroundingYpx = layoutInfo.borderSizeYpx - layoutInfo.contentSizeYpx;
let i = 0;
const preferred = [];
const sizesPx = [];
for (const child of node.children.items()) {
const partEl = child.element;
if (isSplitViewPartition(child.driver) && partEl !== undefined) {
const size = isHorizontal ? partEl.width : partEl.height;
const options = {
axis: isHorizontal ? Axis.X : Axis.Y, lineSizePx: BodyFontSize, fontSizePx: BodyFontSize,
containerSizeXpx: native.scrollWidth - surroundingXpx, containerSizeYpx: native.scrollHeight - surroundingYpx,
};
const minPx = size.min ? toPx(Dimension.parse(size.min), options) : 0;
let maxPx = size.max ? toPx(Dimension.parse(size.max), options) : Number.POSITIVE_INFINITY;
maxPx = Math.max(minPx, maxPx);
if (partEl.layoutInfo === undefined)
partEl.layoutInfo = new ElLayoutInfo(InitialElLayoutInfo);
if (isHorizontal)
partEl.widthPx = { minPx, maxPx };
else
partEl.heightPx = { minPx, maxPx };
const preferredPx = size.preferred ? toPx(Dimension.parse(size.preferred), options) : 0;
if (preferredPx > 0) {
partEl.layoutInfo.effectiveSizePx = preferredPx;
size.preferred = undefined;
preferred.push(i);
}
const sizePx = partEl.layoutInfo.effectiveSizePx = clamp(partEl.layoutInfo.effectiveSizePx, minPx, maxPx);
sizesPx.push({ node: child, sizePx });
i++;
}
}
const priorities = preferred.length > 0
? getPrioritiesForSizeChanging(isHorizontal, node.children, preferred)
: getPrioritiesForEmptySpaceDistribution(isHorizontal, node.children);
runNonReactive(() => relayout(node, priorities.resizable, priorities.manuallyResizable, sizesPx));
}
},
});
ReactiveTreeNode.launchNestedNodesThenDo(() => {
layoutInfo.isUpdateFinished = true;
ReactiveTreeNode.rebuildBody(relayoutEl, { stamp: node.stamp });
});
}
return result;
}
declareChild(ownerNode, childDriver, childDeclaration, childBasis) {
let result = undefined;
const el = ownerNode.element;
if (el.splitView !== undefined) {
if (isSplitViewPartition(childDriver)) {
let partCount = 0;
for (const child of ownerNode.children.items()) {
if (isSplitViewPartition(child.driver))
partCount++;
}
const isHorizontal = el.splitView === Direction.horizontal;
if (childDeclaration !== undefined) {
if (childDeclaration.signalArgs === undefined)
childDeclaration.signalArgs = {};
Object.defineProperty(childDeclaration.signalArgs, "index", { value: partCount });
overrideMethod(childDeclaration, "body", el => {
if (isHorizontal)
el.style.gridColumn = `${partCount + 1}`;
else
el.style.gridRow = `${partCount + 1}`;
});
}
if (partCount > 0)
declareSplitter(partCount - 1, ownerNode);
}
}
else {
const last = ownerNode.children.lastItem;
if (childDriver.isPartition) {
if ((last === null || last === void 0 ? void 0 : last.driver) === childDriver)
result = last;
}
else if (last === undefined) {
declare(Drivers.partition);
}
}
return result;
}
}
export function isSplitViewPartition(childDriver) {
return !childDriver.isPartition && childDriver !== Drivers.splitter && childDriver !== Drivers.fragment;
}
function overrideMethod(declaration, method, func) {
const baseScript = declaration[method];
declaration[method] = baseScript !== undefined
? (el, base) => { baseScript.call(el, el, base); func.call(el, el); }
: (el, base) => { base(); func.call(el, el); };
}
export class StaticBlockDriver extends BlockDriver {
constructor(native, name, isPartition, initialize) {
super(name, isPartition, initialize);
this.native = native;
}
assignNativeElement(node) {
node.element.native = this.native;
}
}
export class PartitionDriver extends HtmlDriver {
rebuildBody(node) {
const result = super.rebuildBody(node);
const ownerEl = node.owner.element;
if (ownerEl.sealed !== undefined) {
node.element.style.flexGrow = "1";
declare(Drivers.wrapper, {
body() {
const ownerEl = this.node.owner.owner.element;
if (ownerEl.splitView !== undefined) {
this.style.display = "grid";
this.style.flexDirection = "";
}
else {
if (ownerEl.isTable) {
this.style.display = "contents";
this.style.flexDirection = "";
}
else {
this.style.display = "flex";
this.style.flexDirection = "row";
}
}
this.style.position = "absolute";
this.style.inset = "0";
this.style.overflow = "auto";
this.style.gap = "inherit";
},
});
}
return result;
}
provideHost(node) {
let host;
const ownerEl = node.owner.element;
if (ownerEl.sealed !== undefined)
host = node.children.firstItem;
else
host = node;
return host;
}
}
export const Drivers = {
block: new BlockDriver(Constants.element, false, el => el.kind = ElKind.block),
table: new HtmlDriver(Constants.element, false, el => el.kind = ElKind.table),
group: new HtmlDriver(Constants.group, false, el => el.kind = ElKind.group),
partition: new PartitionDriver(Constants.partition, true, el => el.kind = ElKind.partition),
wrapper: new HtmlDriver(Constants.wrapper, false, el => el.kind = ElKind.native),
splitter: new HtmlDriver(Constants.splitter, false, el => el.kind = ElKind.splitter),
cursor: new CursorCommandDriver(),
fragment: new ElDriver("fragment", false, el => el.kind = ElKind.group),
};
const VerstakStyleSheetCode = `
.v5k-body {
width: 100vw;
max-width: 100vw;
min-height: 100vh;
margin: 0;
padding: 0;
border: none;
box-sizing: border-box;
}
.v5k-block {
display: flex;
flex-direction: column;
flex-shrink: 1;
justify-content: center;
text-align: center;
}
.v5k-block > .v5k-part {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
/* Children Alignment */
.v5k-block.${StylingClassNameByAlignmentVertical[V.center]} {
justify-content: center;
}
.v5k-block.${StylingClassNameByAlignmentVertical[V.top]} {
justify-content: start;
}
.v5k-block.${StylingClassNameByAlignmentVertical[V.bottom]} {
justify-content: end;
}
.v5k-block.${StylingClassNameByAlignmentVertical[V.stretch]} > .v5k-part,
.v5k-block.${StylingClassNameByAlignmentVertical[V.stretchAndFix]} > .v5k-part {
flex-grow: 1;
align-items: stretch;
}
.v5k-block.${StylingClassNameByAlignmentVerticalRowWise[V.center]} > .v5k-part {
align-items: center;
}
.v5k-block.${StylingClassNameByAlignmentVerticalRowWise[V.top]} > .v5k-part {
align-items: start;
}
.v5k-block.${StylingClassNameByAlignmentVerticalRowWise[V.bottom]} > .v5k-part {
align-items: end;
}
.v5k-block.${StylingClassNameByAlignmentVerticalRowWise[V.stretch]} > .v5k-part,
.v5k-block.${StylingClassNameByAlignmentVerticalRowWise[V.stretchAndFix]} > .v5k-part {
align-items: stretch;
}
.v5k-block.${StylingClassNameByAlignmentHorizontal[H.center]} {
text-align: center;
}
.v5k-block.${StylingClassNameByAlignmentHorizontal[H.center]} > .v5k-part {
justify-content: center;
}
.v5k-block.${StylingClassNameByAlignmentHorizontal[H.left]} {
text-align: left;
}
.v5k-block.${StylingClassNameByAlignmentHorizontal[H.left]} > .v5k-part {
justify-content: start;
}
.v5k-block.${StylingClassNameByAlignmentHorizontal[H.right]} {
text-align: right;
}
.v5k-block.${StylingClassNameByAlignmentHorizontal[H.right]} > .v5k-part {
justify-content: end;
}
.v5k-block.${StylingClassNameByAlignmentHorizontal[H.stretch]},
.v5k-block.${StylingClassNameByAlignmentHorizontal[H.stretchAndFix]} {
text-align: justify;
}
.v5k-block.${StylingClassNameByAlignmentHorizontal[H.stretch]} > .v5k-part > *,
.v5k-block.${StylingClassNameByAlignmentHorizontal[H.stretchAndFix]} > .v5k-part > * {
flex-grow: 1;
}
/* Partition Alignment */
.v5k-part.${StylingClassNameByPartitionAlignmentVertical[V.center]} {
justify-self: center;
}
.v5k-part.${StylingClassNameByPartitionAlignmentVertical[V.top]} {
justify-self: start;
}
.v5k-part.${StylingClassNameByPartitionAlignmentVertical[V.bottom]} {
justify-self: end;
}
.v5k-part.${StylingClassNameByPartitionAlignmentVertical[V.stretch]},
.v5k-part.${StylingClassNameByPartitionAlignmentVertical[V.stretchAndFix]} {
flex-grow: 1;
justify-self: stretch;
}
/* Self Alignment */
.v5k-block.${StylingClassNameBySelfAlignmentVertical[V.center]} {
align-self: center;
}
.v5k-block.${StylingClassNameBySelfAlignmentVertical[V.top]} {
align-self: start;
}
.v5k-block.${StylingClassNameBySelfAlignmentVertical[V.bottom]} {
align-self: end;
}
.v5k-block.${StylingClassNameBySelfAlignmentVertical[V.stretch]},
.v5k-block.${StylingClassNameBySelfAlignmentVertical[V.stretchAndFix]} {
align-self: stretch;
}
.v5k-block.${StylingClassNameBySelfAlignmentHorizontal[H.center]} {
justify-self: center;
}
.v5k-block.${StylingClassNameBySelfAlignmentHorizontal[H.left]} {
justify-self: start;
}
.v5k-block.${StylingClassNameBySelfAlignmentHorizontal[H.right]} {
justify-self: end;
}
.v5k-block.${StylingClassNameBySelfAlignmentHorizontal[H.stretch]},
.v5k-block.${StylingClassNameBySelfAlignmentHorizontal[H.stretchAndFix]} {
flex-grow: 1;
justify-self: stretch;
}
/* Table */
.v5k-table {
display: grid;
flex-basis: 0;
grid-auto-rows: minmax(min-content, 1fr);
grid-auto-columns: minmax(min-content, 1fr);
text-align: initial;
}
.v5k-table > .v5k-part {
display: contents;
flex-direction: row;
justify-content: center;
align-items: center;
}
/* Verstak Blinking Effect */
.v5k-00 {
animation: v5k-blink-ex-0-1 0.75s ease-in 1 !important;
}
.v5k-01 {
animation: v5k-blink-ex-0-2 0.75s ease-in 1 !important;
}
.v5k-10 {
animation: v5k-blink-ex-1-1 0.75s ease-in 1 !important;
}
.v5k-11 {
animation: v5k-blink-ex-1-2 0.75s ease-in 1 !important;
}
.v5k-20 {
animation: v5k-blink-ex-2-1 0.75s ease-in 1 !important;
}
.v5k-21 {
animation: v5k-blink-ex-2-2 0.75s ease-in 1 !important;
}
/* Verstak Blinking Animation */
@keyframes v5k-blink-0-2 {
from { box-shadow: 0 0 0 2px red inset; }
}
@keyframes v5k-blink-1-1 {
from { box-shadow: 0 0 0 2px #0000BB inset; }
}
@keyframes v5k-blink-1-2 {
from { box-shadow: 0 0 0 2px #0000BB inset; }
}
@keyframes v5k-blink-2-1 {
from { box-shadow: 0 0 0 2px #00BB00 inset; }
}
@keyframes v5k-blink-2-2 {
from { box-shadow: 0 0 0 2px #00BB00 inset; }
}
/* Verstak Blinking Animation */
@keyframes v5k-blink-ex-0-1 {
from { outline-offset: -1px; outline: 2px solid red; }
to { outline-offset: -1px; outline: 2px solid transparent; }
}
@keyframes v5k-blink-ex-0-2 {
from { outline-offset: -1px; outline: 2px solid red; }
to { outline-offset: -1px; outline: 2px solid transparent; }
}
@keyframes v5k-blink-ex-1-1 {
from { outline-offset: -1px; outline: 2px solid #0000BB; }
to { outline-offset: -1px; outline: 2px solid transparent; }
}
@keyframes v5k-blink-ex-1-2 {
from { outline-offset: -1px; outline: 2px solid #0000BB; }
to { outline-offset: -1px; outline: 2px solid transparent; }
}
@keyframes v5k-blink-ex-2-1 {
from { outline-offset: -1px; outline: 2px solid #00BB00; }
to { outline-offset: -1px; outline: 2px solid transparent; }
}
@keyframes v5k-blink-ex-2-2 {
from { outline-offset: -1px; outline: 2px solid #00BB00; }
to { outline-offset: -1px; outline: 2px solid transparent; }
}
`;