/ src / elements / div.ts
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();