div.ts
1 import type { PDFFont, PDFPage, RGB } from "@cantoo/pdf-lib"; 2 import type { Node } from "yoga-layout"; 3 import type { Element } from "~/elements"; 4 import type { MemoryFont } from "~/utils/fonts"; 5 import Yoga, { Align, Display, FlexDirection, Justify, Overflow, PositionType } from "yoga-layout"; 6 import { hexToPdf } from "~/utils/colors"; 7 import { absoluteLeft, absoluteTop } from "~/utils/positions"; 8 9 export class Div implements Element { 10 get children(): Array<Element> { 11 return this._elements; 12 } 13 14 private _alignItems?: Align; 15 private _backgroundColor?: RGB; 16 private _display?: Display; 17 private _elements: Array<Element> = []; 18 private _flexDirection = FlexDirection.Row; 19 private _grow?: number; 20 private _h?: "auto" | `${number}%` | number; 21 private _justifyContent?: Justify; 22 private _node?: Node; 23 private _overflow?: Overflow; 24 private _position?: PositionType; 25 private _shrink: number = 1; 26 27 private _w?: "auto" | `${number}%` | number; 28 29 absolute(): Div { 30 this._position = PositionType.Absolute; 31 return this; 32 } 33 34 bg(color: number): Div { 35 this._backgroundColor = hexToPdf(color); 36 return this; 37 } 38 39 child(element: Element): Div { 40 this._elements.push(element); 41 return this; 42 } 43 44 draw(page: PDFPage, fonts: Map<MemoryFont, PDFFont>): void { 45 const node = this.getLayoutNode(page, fonts); 46 47 page.drawRectangle({ 48 color: this._backgroundColor, 49 height: node.getComputedHeight(), 50 width: node.getComputedWidth(), 51 x: absoluteLeft(node), 52 y: page.getHeight() - absoluteTop(node) - node.getComputedHeight() 53 }); 54 55 for (const element of this._elements) { 56 element.draw(page, fonts); 57 } 58 } 59 60 flex(): Div { 61 this._display = Display.Flex; 62 return this; 63 } 64 65 flexCol(): Div { 66 this._flexDirection = FlexDirection.Column; 67 return this; 68 } 69 70 flexColReverse(): Div { 71 this._flexDirection = FlexDirection.ColumnReverse; 72 return this; 73 } 74 75 flexRow(): Div { 76 this._flexDirection = FlexDirection.Row; 77 return this; 78 } 79 80 flexRowReverse(): Div { 81 this._flexDirection = FlexDirection.RowReverse; 82 return this; 83 } 84 85 getLayoutNode(page: PDFPage, fonts: Map<MemoryFont, PDFFont>): Node { 86 if (this._node) return this._node; 87 const node = Yoga.Node.create(); 88 console.log("div: construct layout"); 89 90 node.setHeight(this._h); 91 node.setWidth(this._w); 92 93 if (this._position !== void 0) { 94 node.setPositionType(this._position); 95 } 96 97 if (this._display !== void 0) { 98 node.setDisplay(this._display); 99 } 100 101 if (this._flexDirection !== void 0) { 102 node.setFlexDirection(this._flexDirection); 103 } 104 105 if (this._alignItems !== void 0) { 106 node.setAlignItems(this._alignItems); 107 } 108 if (this._justifyContent !== void 0) { 109 node.setJustifyContent(this._justifyContent); 110 } 111 112 if (this._grow !== void 0) { 113 node.setFlexGrow(this._grow); 114 } 115 116 if (this._shrink !== void 0) { 117 node.setFlexShrink(this._shrink); 118 } 119 120 if (this._overflow !== void 0) { 121 node.setOverflow(this._overflow); 122 } 123 124 for (let i = 0; i < this.children.length; i++) { 125 const element = this.children[i]!; 126 node.insertChild(element.getLayoutNode(page, fonts), i); 127 } 128 129 this._node = node; 130 return this._node; 131 } 132 133 grow1(): Div { 134 this._grow = 1; 135 return this; 136 } 137 138 h(h: number): Div { 139 this._h = h; 140 return this; 141 } 142 143 hFull(): Div { 144 this._h = "100%"; 145 return this; 146 } 147 148 itemsCenter(): Div { 149 this._alignItems = Align.Center; 150 return this; 151 } 152 153 itemsStart(): Div { 154 this._alignItems = Align.FlexStart; 155 return this; 156 } 157 158 justifyCenter(): Div { 159 this._justifyContent = Justify.Center; 160 return this; 161 } 162 163 justifyStart(): Div { 164 this._justifyContent = Justify.FlexStart; 165 return this; 166 } 167 168 overflowHidden(): Div { 169 this._overflow = Overflow.Hidden; 170 return this; 171 } 172 173 relative(): Div { 174 this._position = PositionType.Relative; 175 return this; 176 } 177 178 shrink0(): Div { 179 this._shrink = 0; 180 return this; 181 } 182 183 shrink1(): Div { 184 this._shrink = 1; 185 return this; 186 } 187 188 w(w: number): Div { 189 this._w = w; 190 return this; 191 } 192 193 wFull(): Div { 194 this._w = "100%"; 195 return this; 196 } 197 } 198 export const div = (): Div => new Div();