{"version":3,"file":"index.mjs","names":[],"sources":["../src/multiline-text.ts"],"sourcesContent":["import Two from 'two.js'\nimport wrap from 'word-wrapper'\n\nimport type {\n  AlignmentProperties,\n  StyleProperties,\n  DecorationProperties,\n  DirectionProperties,\n  BaselineProperties\n} from 'two.js/src/text'\n\nimport { Shape } from 'two.js/src/shape'\n\ntype OptionallyOffscreenCanvasRenderingContext2D = (\n  CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D\n)\n\ntype Text = Pick<InstanceType<typeof Two.Text>, (\n  'value' |\n  'family' |\n  'size' |\n  'leading' |\n  'alignment' |\n  'fill' |\n  'stroke' |\n  'linewidth' |\n  'style' |\n  'weight' |\n  'decoration' |\n  'baseline' |\n  'opacity' |\n  'visible' |\n  'rotation' |\n  'scale' |\n  'translation' |\n  'clone' |\n  'getBoundingClientRect'\n)>\n\nconst proto: Record<string, PropertyDescriptor> = {\n  width: {\n    enumerable: true,\n    get(this: MultilineText) { return this._width },\n    set(this: MultilineText, v: number) { this._width = v; this._flagWrapping = true }\n  },\n  measure: {\n    enumerable: true,\n    get(this: MultilineText) { return this._measure },\n    set(this: MultilineText, v: 'font' | 'monospace' | 'length') { this._measure = v; this._flagWrapping = true }\n  },\n  mode: {\n    enumerable: true,\n    get(this: MultilineText) { return this._mode },\n    set(this: MultilineText, v: 'normal' | 'pre' | 'nowrap') { this._mode = v; this._flagWrapping = true }\n  },\n  value: {\n    enumerable: true,\n    get(this: MultilineText) { return this._value },\n    set(this: MultilineText, v: string) { this._value = v; this._flagWrapping = true }\n  },\n  family: {\n    enumerable: true,\n    get(this: MultilineText) { return this._family },\n    set(this: MultilineText, v: string) { this._family = v; this._flagWrapping = true }\n  },\n  size: {\n    enumerable: true,\n    get(this: MultilineText) { return this._size },\n    set(this: MultilineText, v: number) { this._size = v; this._flagWrapping = true }\n  },\n  weight: {\n    enumerable: true,\n    get(this: MultilineText) { return this._weight },\n    set(this: MultilineText, v: number) { this._weight = v; this._flagWrapping = true }\n  },\n  style: {\n    enumerable: true,\n    get(this: MultilineText) { return this._style },\n    set(this: MultilineText, v: StyleProperties) { this._style = v; this._flagStyle = true }\n  },\n  leading: {\n    enumerable: true,\n    get(this: MultilineText) { return this._leading },\n    set(this: MultilineText, v: number) { this._leading = v; this._flagStyle = true }\n  },\n  absoluteLeading: {\n    enumerable: true,\n    get(this: MultilineText) { return this._absoluteLeading },\n    set(this: MultilineText, v: boolean) { this._absoluteLeading = v; this._flagStyle = true }\n  },\n  alignment: {\n    enumerable: true,\n    get(this: MultilineText) { return this._alignment },\n    set(this: MultilineText, v: AlignmentProperties) { this._alignment = v; this._flagStyle = true }\n  },\n  direction: {\n    enumerable: true,\n    get(this: MultilineText) { return this._direction },\n    set(this: MultilineText, v: DirectionProperties) { this._direction = v; this._flagStyle = true }\n  },\n  decoration: {\n    enumerable: true,\n    get(this: MultilineText) { return this._decoration },\n    set(this: MultilineText, v: DecorationProperties) { this._decoration = v; this._flagStyle = true }\n  },\n  baseline: {\n    enumerable: true,\n    get(this: MultilineText) { return this._baseline },\n    set(this: MultilineText, v: BaselineProperties) { this._baseline = v; this._flagStyle = true }\n  }\n}\n\nconst groupBackingFields: Array<[string, string]> = [\n  ['_fill', '_flagStyle'],\n  ['_stroke', '_flagStyle'],\n  ['_linewidth', '_flagStyle'],\n  ['_opacity', '_flagStyle'],\n  ['_visible', '_flagStyle']\n]\n\nexport class MultilineText extends Two.Group implements Text {\n  protected _flagWrapping: boolean = true\n  protected _flagStyle: boolean = true\n  protected _width!: number\n  protected _measure!: 'font' | 'monospace' | 'length'\n  protected _mode!: 'normal' | 'pre' | 'nowrap'\n  protected _value!: string\n  protected _family!: string\n  protected _size!: number\n  protected _weight!: number\n  protected _style!: StyleProperties\n  protected _leading!: number\n  protected _absoluteLeading!: boolean\n  protected _alignment!: AlignmentProperties\n  protected _direction!: DirectionProperties\n  protected _decoration!: DecorationProperties\n  protected _baseline!: BaselineProperties\n  protected declare _fill: string\n  protected declare _stroke: string\n  protected declare _linewidth: number\n  protected declare _opacity: number\n  protected declare _visible: boolean\n\n  public declare width: number\n  public declare measure: 'font' | 'monospace' | 'length'\n  public declare mode: 'normal' | 'pre' | 'nowrap'\n  public declare value: string\n  public declare family: string\n  public declare size: number\n  public declare weight: number\n  public declare style: StyleProperties\n  public declare leading: number\n  public declare absoluteLeading: boolean\n  public declare alignment: AlignmentProperties\n  public declare direction: DirectionProperties\n  public declare fill: string\n  public declare stroke: string\n  public declare linewidth: number\n  public declare decoration: DecorationProperties\n  public declare baseline: BaselineProperties\n  public declare opacity: number\n  public declare visible: boolean\n\n  public constructor (message: string, x: number = 0, y: number = 0, {\n    width = Infinity,\n    measure = 'font',\n    mode = 'normal',\n    family = 'sans-serif',\n    size = 13,\n    weight = 500,\n    style = 'normal',\n    leading = 1.3,\n    absoluteLeading = false,\n    alignment = 'left',\n    direction = 'ltr',\n    fill = '#000',\n    stroke = 'transparent',\n    linewidth = 1,\n    decoration = 'none',\n    baseline = 'baseline',\n    opacity = 1,\n    visible = true\n  }: {\n    width?: number\n    measure?: 'font' | 'monospace' | 'length'\n    mode?: 'normal' | 'pre' | 'nowrap'\n    family?: string\n    size?: number\n    weight?: number\n    style?: StyleProperties\n    leading?: number\n    absoluteLeading?: boolean\n    alignment?: AlignmentProperties\n    direction?: DirectionProperties\n    fill?: string\n    stroke?: string\n    linewidth?: number\n    decoration?: DecorationProperties\n    baseline?: BaselineProperties\n    opacity?: number\n    visible?: boolean\n  } = {}) {\n    super()\n\n    for (const key in proto) {\n      Object.defineProperty(this, key, proto[key])\n    }\n\n    for (const [backingField, flag] of groupBackingFields) {\n      let value: any\n      Object.defineProperty(this, backingField, {\n        get: () => value,\n        set: (v) => { value = v; (this as any)[flag] = true },\n        configurable: true,\n        enumerable: false\n      })\n    }\n\n    this.translation.set(x, y)\n    this.width = width\n    this.measure = measure\n    this.mode = mode\n    this.value = message\n    this.family = family\n    this.size = size\n    this.leading = leading\n    this.absoluteLeading = absoluteLeading\n    this.alignment = alignment\n    this.direction = direction\n    this.fill = fill\n    this.stroke = stroke\n    this.linewidth = linewidth\n    this.weight = weight\n    this.style = style\n    this.decoration = decoration\n    this.baseline = baseline\n    this.opacity = opacity\n    this.visible = visible\n  }\n\n  public get computedLeading(): number {\n    return this._absoluteLeading ? this._leading : this._size * this._leading\n  }\n\n  protected get context(): OptionallyOffscreenCanvasRenderingContext2D {\n    const value = (typeof OffscreenCanvas === 'function'\n      ? new OffscreenCanvas(1, 1)\n      : document.createElement('canvas')\n    ).getContext('2d')!\n\n    Object.defineProperty(MultilineText.prototype, 'context', {\n      value,\n      writable: false,\n      configurable: false\n    })\n\n    return value\n  }\n\n  protected get _measureMonospace(): (\n    text: string,\n    start: number,\n    end: number,\n    width: number\n  ) => { start: number, end: number } {\n    const charWidth = this.context.measureText('M').width\n    return (text: string, start: number, end: number, width: number) => ({\n      start,\n      end: start + Math.min(\n        end - start,\n        Math.floor(width / charWidth),\n        Math.floor((end - start) * charWidth)\n      )\n    })\n  }\n\n  protected _measureLength = (\n    text: string,\n    start: number,\n    end: number,\n    width: number\n  ): { start: number, end: number } => ({\n    start,\n    end: start + Math.min(width, end - start)\n  })\n\n  protected _measureFont = (\n    text: string,\n    start: number,\n    end: number,\n    width: number\n  ): { start: number, end: number } => {\n    while (this.context.measureText(text.slice(start, end)).width > width) {\n      end--\n    }\n\n    return { start, end }\n  }\n\n  protected _prepareMeasureContext(): void {\n    this.context.font = `${\n      this._style\n    } ${\n      this._weight\n    } ${\n      this._size\n    }px ${\n      this._family\n    }`\n  }\n}\n\nObject.assign(MultilineText.prototype as any, {\n  _update(this: MultilineText, bubbles: boolean): any {\n    if (this._flagWrapping) {\n      let measure: (\n        text: string,\n        start: number,\n        end: number,\n        width: number\n      ) => { start: number, end: number }\n\n      if (this._measure === 'length') {\n        measure = this._measureLength\n      } else {\n        this._prepareMeasureContext()\n        measure = this._measure === 'monospace'\n          ? this._measureMonospace\n          : this._measureFont\n      }\n\n      const texts = this.children as unknown as Text[]\n      const lines = wrap\n        .lines(this._value, {\n          measure,\n          width: this._width,\n          mode: this._mode\n        })\n        .map(({ start, end }: { start: number, end: number }) => this._value.slice(start, end))\n\n      while (texts.length > lines.length) {\n        this.remove(texts[0] as unknown as Shape)\n      }\n\n      texts.forEach((text: Text, index: number) => {\n        text.value = lines[index].trim()\n      })\n\n      while (texts.length < lines.length) {\n        this.add(new Two.Text(lines[texts.length].trim(), 0, 0))\n      }\n\n      this._flagStyle = true\n    }\n\n    if (this._flagStyle) {\n      const leading = this.computedLeading\n\n      let offset: number = 0\n\n      let orientedAlignment: AlignmentProperties = this._alignment\n\n      if (this._direction === 'rtl') {\n        switch (this._alignment) {\n          case 'left':\n            orientedAlignment = 'right'\n            break\n\n          case 'right':\n            orientedAlignment = 'left'\n            break\n        }\n      }\n\n      switch (this._alignment) {\n        case 'right':\n          offset = this._width\n          break\n\n        case 'center':\n          offset = this._width / 2\n          break\n      }\n\n      ;(this.children as unknown as (Text & {\n        direction: DirectionProperties\n      })[]).forEach((text, index) => {\n        text.family = this._family\n        text.size = this._size\n        text.leading = leading\n        text.alignment = orientedAlignment\n        text.direction = this._direction\n        text.fill = this._fill\n        text.stroke = this._stroke\n        text.linewidth = this._linewidth\n        text.style = this._style\n        text.weight = this._weight\n        text.decoration = this._decoration\n        text.baseline = this._baseline\n        text.opacity = this._opacity\n        text.visible = this._visible\n        text.translation.set(offset, leading * index)\n      })\n    }\n\n    (Two.Group.prototype as unknown as { _update (bubbles?: boolean): void })\n      ._update.call(this, bubbles)\n\n    return this\n  },\n\n  flagReset(): any {\n    this._flagWrapping = this._flagStyle = false\n\n    ;(Two.Group.prototype as unknown as { flagReset(): void })\n      .flagReset.call(this)\n\n    return this\n  }\n} as any)\n"],"mappings":";;;AAuCA,MAAM,QAA4C;CAChD,OAAO;EACL,YAAY;EACZ,MAAyB;AAAE,UAAO,KAAK;;EACvC,IAAyB,GAAW;AAAE,QAAK,SAAS;AAAG,QAAK,gBAAgB;;EAC7E;CACD,SAAS;EACP,YAAY;EACZ,MAAyB;AAAE,UAAO,KAAK;;EACvC,IAAyB,GAAoC;AAAE,QAAK,WAAW;AAAG,QAAK,gBAAgB;;EACxG;CACD,MAAM;EACJ,YAAY;EACZ,MAAyB;AAAE,UAAO,KAAK;;EACvC,IAAyB,GAAgC;AAAE,QAAK,QAAQ;AAAG,QAAK,gBAAgB;;EACjG;CACD,OAAO;EACL,YAAY;EACZ,MAAyB;AAAE,UAAO,KAAK;;EACvC,IAAyB,GAAW;AAAE,QAAK,SAAS;AAAG,QAAK,gBAAgB;;EAC7E;CACD,QAAQ;EACN,YAAY;EACZ,MAAyB;AAAE,UAAO,KAAK;;EACvC,IAAyB,GAAW;AAAE,QAAK,UAAU;AAAG,QAAK,gBAAgB;;EAC9E;CACD,MAAM;EACJ,YAAY;EACZ,MAAyB;AAAE,UAAO,KAAK;;EACvC,IAAyB,GAAW;AAAE,QAAK,QAAQ;AAAG,QAAK,gBAAgB;;EAC5E;CACD,QAAQ;EACN,YAAY;EACZ,MAAyB;AAAE,UAAO,KAAK;;EACvC,IAAyB,GAAW;AAAE,QAAK,UAAU;AAAG,QAAK,gBAAgB;;EAC9E;CACD,OAAO;EACL,YAAY;EACZ,MAAyB;AAAE,UAAO,KAAK;;EACvC,IAAyB,GAAoB;AAAE,QAAK,SAAS;AAAG,QAAK,aAAa;;EACnF;CACD,SAAS;EACP,YAAY;EACZ,MAAyB;AAAE,UAAO,KAAK;;EACvC,IAAyB,GAAW;AAAE,QAAK,WAAW;AAAG,QAAK,aAAa;;EAC5E;CACD,iBAAiB;EACf,YAAY;EACZ,MAAyB;AAAE,UAAO,KAAK;;EACvC,IAAyB,GAAY;AAAE,QAAK,mBAAmB;AAAG,QAAK,aAAa;;EACrF;CACD,WAAW;EACT,YAAY;EACZ,MAAyB;AAAE,UAAO,KAAK;;EACvC,IAAyB,GAAwB;AAAE,QAAK,aAAa;AAAG,QAAK,aAAa;;EAC3F;CACD,WAAW;EACT,YAAY;EACZ,MAAyB;AAAE,UAAO,KAAK;;EACvC,IAAyB,GAAwB;AAAE,QAAK,aAAa;AAAG,QAAK,aAAa;;EAC3F;CACD,YAAY;EACV,YAAY;EACZ,MAAyB;AAAE,UAAO,KAAK;;EACvC,IAAyB,GAAyB;AAAE,QAAK,cAAc;AAAG,QAAK,aAAa;;EAC7F;CACD,UAAU;EACR,YAAY;EACZ,MAAyB;AAAE,UAAO,KAAK;;EACvC,IAAyB,GAAuB;AAAE,QAAK,YAAY;AAAG,QAAK,aAAa;;EACzF;CACF;AAED,MAAM,qBAA8C;CAClD,CAAC,SAAS,aAAa;CACvB,CAAC,WAAW,aAAa;CACzB,CAAC,cAAc,aAAa;CAC5B,CAAC,YAAY,aAAa;CAC1B,CAAC,YAAY,aAAa;CAC3B;AAED,IAAa,gBAAb,MAAa,sBAAsB,IAAI,MAAsB;CAC3D,gBAAmC;CACnC,aAAgC;CAChC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CA2BA,YAAoB,SAAiB,IAAY,GAAG,IAAY,GAAG,EACjE,QAAQ,UACR,UAAU,QACV,OAAO,UACP,SAAS,cACT,OAAO,IACP,SAAS,KACT,QAAQ,UACR,UAAU,KACV,kBAAkB,OAClB,YAAY,QACZ,YAAY,OACZ,OAAO,QACP,SAAS,eACT,YAAY,GACZ,aAAa,QACb,WAAW,YACX,UAAU,GACV,UAAU,SAoBR,EAAE,EAAE;AACN,SAAO;AAEP,OAAK,MAAM,OAAO,MAChB,QAAO,eAAe,MAAM,KAAK,MAAM,KAAK;AAG9C,OAAK,MAAM,CAAC,cAAc,SAAS,oBAAoB;GACrD,IAAI;AACJ,UAAO,eAAe,MAAM,cAAc;IACxC,WAAW;IACX,MAAM,MAAM;AAAE,aAAQ;AAAI,UAAa,QAAQ;;IAC/C,cAAc;IACd,YAAY;IACb,CAAC;;AAGJ,OAAK,YAAY,IAAI,GAAG,EAAE;AAC1B,OAAK,QAAQ;AACb,OAAK,UAAU;AACf,OAAK,OAAO;AACZ,OAAK,QAAQ;AACb,OAAK,SAAS;AACd,OAAK,OAAO;AACZ,OAAK,UAAU;AACf,OAAK,kBAAkB;AACvB,OAAK,YAAY;AACjB,OAAK,YAAY;AACjB,OAAK,OAAO;AACZ,OAAK,SAAS;AACd,OAAK,YAAY;AACjB,OAAK,SAAS;AACd,OAAK,QAAQ;AACb,OAAK,aAAa;AAClB,OAAK,WAAW;AAChB,OAAK,UAAU;AACf,OAAK,UAAU;;CAGjB,IAAW,kBAA0B;AACnC,SAAO,KAAK,mBAAmB,KAAK,WAAW,KAAK,QAAQ,KAAK;;CAGnE,IAAc,UAAuD;EACnE,MAAM,SAAS,OAAO,oBAAoB,aACtC,IAAI,gBAAgB,GAAG,EAAE,GACzB,SAAS,cAAc,SAAS,EAClC,WAAW,KAAK;AAElB,SAAO,eAAe,cAAc,WAAW,WAAW;GACxD;GACA,UAAU;GACV,cAAc;GACf,CAAC;AAEF,SAAO;;CAGT,IAAc,oBAKsB;EAClC,MAAM,YAAY,KAAK,QAAQ,YAAY,IAAI,CAAC;AAChD,UAAQ,MAAc,OAAe,KAAa,WAAmB;GACnE;GACA,KAAK,QAAQ,KAAK,IAChB,MAAM,OACN,KAAK,MAAM,QAAQ,UAAU,EAC7B,KAAK,OAAO,MAAM,SAAS,UAAU,CACtC;GACF;;CAGH,kBACE,MACA,OACA,KACA,WACoC;EACpC;EACA,KAAK,QAAQ,KAAK,IAAI,OAAO,MAAM,MAAM;EAC1C;CAED,gBACE,MACA,OACA,KACA,UACmC;AACnC,SAAO,KAAK,QAAQ,YAAY,KAAK,MAAM,OAAO,IAAI,CAAC,CAAC,QAAQ,MAC9D;AAGF,SAAO;GAAE;GAAO;GAAK;;CAGvB,yBAAyC;AACvC,OAAK,QAAQ,OAAO,GAClB,KAAK,OACN,GACC,KAAK,QACN,GACC,KAAK,MACN,KACC,KAAK;;;AAKX,OAAO,OAAO,cAAc,WAAkB;CAC5C,QAA6B,SAAuB;AAClD,MAAI,KAAK,eAAe;GACtB,IAAI;AAOJ,OAAI,KAAK,aAAa,SACpB,WAAU,KAAK;QACV;AACL,SAAK,wBAAwB;AAC7B,cAAU,KAAK,aAAa,cACxB,KAAK,oBACL,KAAK;;GAGX,MAAM,QAAQ,KAAK;GACnB,MAAM,QAAQ,KACX,MAAM,KAAK,QAAQ;IAClB;IACA,OAAO,KAAK;IACZ,MAAM,KAAK;IACZ,CAAC,CACD,KAAK,EAAE,OAAO,UAA0C,KAAK,OAAO,MAAM,OAAO,IAAI,CAAC;AAEzF,UAAO,MAAM,SAAS,MAAM,OAC1B,MAAK,OAAO,MAAM,GAAuB;AAG3C,SAAM,SAAS,MAAY,UAAkB;AAC3C,SAAK,QAAQ,MAAM,OAAO,MAAM;KAChC;AAEF,UAAO,MAAM,SAAS,MAAM,OAC1B,MAAK,IAAI,IAAI,IAAI,KAAK,MAAM,MAAM,QAAQ,MAAM,EAAE,GAAG,EAAE,CAAC;AAG1D,QAAK,aAAa;;AAGpB,MAAI,KAAK,YAAY;GACnB,MAAM,UAAU,KAAK;GAErB,IAAI,SAAiB;GAErB,IAAI,oBAAyC,KAAK;AAElD,OAAI,KAAK,eAAe,MACtB,SAAQ,KAAK,YAAb;IACE,KAAK;AACH,yBAAoB;AACpB;IAEF,KAAK;AACH,yBAAoB;AACpB;;AAIN,WAAQ,KAAK,YAAb;IACE,KAAK;AACH,cAAS,KAAK;AACd;IAEF,KAAK;AACH,cAAS,KAAK,SAAS;AACvB;;AAGF,QAAK,SAED,SAAS,MAAM,UAAU;AAC7B,SAAK,SAAS,KAAK;AACnB,SAAK,OAAO,KAAK;AACjB,SAAK,UAAU;AACf,SAAK,YAAY;AACjB,SAAK,YAAY,KAAK;AACtB,SAAK,OAAO,KAAK;AACjB,SAAK,SAAS,KAAK;AACnB,SAAK,YAAY,KAAK;AACtB,SAAK,QAAQ,KAAK;AAClB,SAAK,SAAS,KAAK;AACnB,SAAK,aAAa,KAAK;AACvB,SAAK,WAAW,KAAK;AACrB,SAAK,UAAU,KAAK;AACpB,SAAK,UAAU,KAAK;AACpB,SAAK,YAAY,IAAI,QAAQ,UAAU,MAAM;KAC7C;;AAGH,MAAI,MAAM,UACR,QAAQ,KAAK,MAAM,QAAQ;AAE9B,SAAO;;CAGT,YAAiB;AACf,OAAK,gBAAgB,KAAK,aAAa;AAErC,MAAI,MAAM,UACT,UAAU,KAAK,KAAK;AAEvB,SAAO;;CAEV,CAAQ"}