// This file was moved from the `server` package to here and `server` has `strictNullChecks` disabled. // This code was originally written with the assumption that `widget.children` is never undefined, despite the type saying otherwise. // So we are using `!` to assert that `widget.children` is never undefined. // Note that `!` does not change the runtime behavior of the code so the code will work the same way as before. /* eslint-disable @typescript-eslint/no-explicit-any */ import type { PageDSL4and5 } from '../../types/index.js'; const V4_COLUMN_COUNT = 16; const ROW_DENSITY_RATIO = 3; const ROW_HEIGHT = 12; // This migration code uses strings instead of variables like CANVAS_WIDGET for two reasons: // 1. These strings will never change, even if we change the types in the future // 2. We do want to make these types much stricter in the future, but for now export const migrate4to5 = (widget: PageDSL4and5, maxColumns = 96): PageDSL4and5 => { // MAIN_CONTAINER_WIDGET_ID if (widget.widgetId === '0') { widget.snapColumns = maxColumns; widget.bottomRow = Math.floor(widget.bottomRow / ROW_HEIGHT); widget.snapRows = widget.bottomRow; } if (widget.type === 'CANVAS_WIDGET') { delete widget.parentColumnSpace; delete widget.parentRowSpace; delete widget.leftColumn; delete widget.rightColumn; } if (widget.type !== 'CANVAS_WIDGET') { delete widget.parentColumnSpace; delete widget.parentRowSpace; // If we are inside a contained element, maxColumns is scaled by the actual space usage // This usually scales up, but can also scale down widget.leftColumn = Math.floor(widget.leftColumn! * (maxColumns / V4_COLUMN_COUNT)); widget.rightColumn = Math.floor(widget.rightColumn! * (maxColumns / V4_COLUMN_COUNT)); widget.snapColumns = widget.rightColumn - widget.leftColumn; maxColumns = widget.rightColumn - widget.leftColumn; } widget.children?.forEach((child: PageDSL4and5) => { if (!child) return; if (child.type === 'CANVAS_WIDGET') { if (child.bottomRow) { if (!widget.shouldScrollContents) { // for unscrollable container, we should just use container's height // using the max result above will cause unscrollabe tab cutoff on header child.bottomRow = widget.bottomRow - widget.topRow; } else { // Divide by row height child.bottomRow = Math.max(Math.ceil(child.bottomRow / ROW_HEIGHT), widget.bottomRow - widget.topRow); } child.snapRows = child.bottomRow; } else { child.bottomRow = widget.bottomRow; child.snapRows = child.bottomRow; } } else { child.topRow *= ROW_DENSITY_RATIO; child.bottomRow *= ROW_DENSITY_RATIO; } // Based on some trial and error, inputs look better when they are shrunk by 1 row // due to the reduced label size. Inputs are not migrated if: // a. They are horizontal // b. They use a dynamic binding to determine vertical/horizontal (very rare) if ( (child.type === 'INPUT_WIDGET' || child.type === 'DROP_DOWN_WIDGET') && child.isVertical === true && String(child.label).trim().length ) { child.topRow += 1; } if (widget.type === 'CANVAS_WIDGET') { if (widget.noPad) child.noContainerOffset = true; } else { child.shouldScrollContents = false; child.canExtend = widget.shouldScrollContents; } // Adjust based on parent widget if ( widget.type === 'CONTAINER_WIDGET' || widget.type === 'FORM_WIDGET' || widget.type === 'GRID_WIDGET' || widget.type === 'TABS_WIDGET' ) { child.snapColumns = Math.floor((widget.rightColumn! - widget.leftColumn!) / ((widget as any).columnCount ?? 1)); maxColumns = child.snapColumns; } else if (widget.type === 'SLIDEOUT_WIDGET') { child.containerStyle = 'none'; child.minHeight = 800; // Seems like some slideouts did not set the bottomRow correctly const maxInnerRow = (child.children?.reduce((prev, current) => Math.max(current.bottomRow, prev), 0) ?? 0) * 3; child.bottomRow = Math.max(maxInnerRow, Math.ceil(child.minHeight / ROW_HEIGHT)); child.snapRows = child.bottomRow + 2; child.snapColumns = 80; // This produces ~12px wide columns maxColumns = child.snapColumns; } if (widget.type === 'TABS_WIDGET') { child.bottomRow -= 3; // the tab header take 3 more rows now. child.snapRows! -= 3; delete child.minHeight; } migrate4to5(child, maxColumns); }); return widget; };