UNPKG

verstak

Version:
553 lines (496 loc) 21.6 kB
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; } } `;