import { shrewdStatic } from "bp/global" ; import { SheetObject } from "bp/design/class" ; import { action } from "bp/content/changes/action" ; import type { IQueryableTagObject , ITagObject } from "bp/content/interface" ; /**
 * 抽象類別  Store 能根據指定的生成器和建造器產生物件陣列，
 * 並且提供方法來在陣列之中瀏覽不同的物件。
 *
 * 基於效能，它的運作模式如下：
 * 1. 在拖曳期間只會產出生成器傳回的第一個原型資料建造出來的物件實體。
 * 2. 拖曳結束之後，生成全部的原型資料。
 * 3. 之後只有在索引值發生切換的時候才視需要根據原型資料建構出對應的物件實體。
 *
 *  typedef P typedef T export abstract class Store < P , T extends SheetObject & IQueryableTagObject > extends SheetObject implements IQueryableTagObject { public abstract get $tag ( ) : string ; public $query ( tag ) { if ( ! tag ) return this ; let m = tag . match ( /^(\d+)(?:\.(.+))?$/ ) ; if ( m ) { let id = Number ( m [ 1 ] ) , then = m [ 2 ] ; return this . $get ( id ) ?. $query ( then ) ; } return undefined ; } /** 原型資料生成器 */ protected abstract $generator : Generator < P > ; /** 從原型資料產生實體物件的建造器 */ protected abstract $builder ( prototype ) ; /** 目前選取的 entry 索引 */ @ action public index : number = 0 ; /** 目前的  Collection 所有可用的 prototype  @ shrewd protected get _prototypes ( ) : P [ ] { if ( ! this . $generator ) return this . _cache ; if ( this . $design . $dragging ) { this . _buildFirst ( ) ; return this . _cache . concat ( ) ; } else { if ( this . _entries . length == 0 ) this . _buildFirst ( ) ; for ( let entry of this . $generator ) this . _cache . push ( entry ) ; delete this . $generator ; return this . _cache ; } } protected $restore ( prototypes , index ) { this . _cache = prototypes ; this . index = index ; } private _buildFirst ( ) { let entry = this . $generator . next ( ) ; if ( ! entry . done ) { try { this . _entries [ 0 ] = this . $builder ( entry . value ) ; this . _cache . push ( entry . value ) ; } catch ( e ) { console . log ( "Incompatible old version." ) ; } } } /** prototype 快取 */ private _cache : P [ ] = [ ] ; /** 所有的 entry */ private _entries : T [ ] = [ ] ; protected get $memento ( ) : ( P | T ) [ ] { let result = [ ] ; for ( let i = 0 ; i < this . _prototypes . length ; i ++ ) { result . push ( this . _entries [ i ] || this . _cache [ i ] ) ; } return result ; } /**
     * 當前選用的 entry。
     *
     * 只要當前的  Store 非空就會有一個被選中，所以傳回 null 也表示  Store 為空。
     *  exports @ shrewdStatic public get entry ( ) : T | null { return this . $get ( this . index ) ?? null ; } exports public move ( by = 1 ) { let l = this . _prototypes . length ; this . index = ( this . index + by % l + l ) % l ; this . $onMove ( ) ; } /**
     * 當前  Store 的大小
     *
     *  exports public get size ( ) : number { return this . _prototypes . length ; } public $indexOf ( entry ) { return this . _entries . indexOf ( entry ) ; } public $get ( i ) { let e = this . _prototypes ; if ( e . length == 0 ) return undefined ; i %= e . length ; return this . _entries [ i ] = this . _entries [ i ] || this . $builder ( e [ i ] ) ; } /** 給  Store 的實作類別註冊 entry 切換之後要發生的事情  protected abstract $onMove ( ) ; }