Mehログ 2024-11
仮想DOMじゃないHTMLコンポーネントライブラリ
仮想DOMじゃない HTMLコンポーネントのJS/TSライブラリ「Meh」の、きわめてマイペースな開発・河原の石積んでくずし日記。
無職になり1年ぶりの投稿です。
値モデルの課題
Vue でいう ref のようなオブジェクト、末端・枝葉の葉のやくわりを担うので「Leaf」と名付け、それと参照し合いチェンジイベントをうけとる側を「Ref」と名付けているので、Vue の ref とまぎらわしくなったかもしれません。
オブジェクトの生成は new Leaf < string > ( "い・ろ・は" ) のように、クラスを new していましたが、値実体を持たないインターフェースの需要があったので、実体は Leaf.Entity とし、生成は leaf < V > という関数で簡潔に作るようにしました。
また、Leaf は TSインターフェースとはせず abstract class にして、実行時に instanceof で判別できるようにしました。
タブやページなどの「切り替え」の宣言的表現
switch という名前の関数がつくれればうれしいですが、JSのそれと被ってしますので、抽象クラスで place という疑似名前空間を作り、そこに static メンバとして閉じ込めてみました。
「sw」のように略してもいいでしょう。
引数順は、
place.switch < K >
(
選択キー : K | Leafr < K > ,
Mehエレメント作成関数 | [ キー, Mehエレメント実体 ][] ,
初期化オプション
)
のようにしました。
「キー」には真偽型・数値型・文字列型のほかにオブジェクト型もつかえます。
「SPA=ブラウザ内ブラウザ」ナビゲーションの課題
「サイト構造」を模した「Index」オブジェクトのツリー。
やっと気に入る「ページ切り替え機構の表現」ができたので、路線・駅APIを使った簡単なツリーナビでも、と思いましたが、「パス path」を表す動的アレイも作らなければ.. 、いうことになり、ちょっと一休み。
リファクト!!
「leaf , Leaf , leafr , Leafr」と、値生成・型・クラス・アクセス性などで、4つに分けていた値モデルのシンボルを「leaf」ひとつに統一。
const l : leaf.str = leaf.str ( "value で読み書き可" ) ;
const lr : leaf.r = leaf.r.str ( "value は読み専用" ) ;
l.value = "書き換えるよ。" ;
lr.value = "いいじゃないか" ; // TSエラー
const ll : leaf.ll.str = "動静どちらかの値" ;
console.log ( ll instanceof leaf.Base ? "動的な、リアクティブな、値。" : "静的な値。" ) ;
読み専用 R を基底クラスとして、読み書き可 W/R の方もクラスとして定義するやり方に矛盾が生じ、
基底側を W/R とし、値実体や返還などの具象クラスの実装は W/R のみとし、インターフェイスと2種の生成関数で、TS文法上の書きアクセスを閉じるやり方へ。
JS上ではエラーにならないでしょうが..
import { log } from "../common.js" ;
/* W/R 実体生成 */
export function leaf < V > ( value : V , branch ? : leaf.branch < V > ) : leaf < V >
{
return new leaf.Entity ( value , branch ) ;
}
export namespace leaf
{
export const str = leaf < string > ;
export const num = leaf < number > ;
export const bool = leaf < boolean > ;
export const get = < V > ( lol : ll < V > ) =>
{
return lol instanceof Source ? lol.value : lol ;
}
}
/* W/R 型定義 */
export const set_value = Symbol() ;
export interface leaf < V >
{
add_ref
(
ref : leaf.ref < V > ,
old_v ? : V
) : void ;
remove_ref ( ref : leaf.ref < V > ) : void ;
get value () : V ;
set value ( value : V ) ;
set ( value : V , changer ? : object ) : void ;
[ set_value ] ( value : V , changer ? : object ) : void ;
}
export namespace leaf
{
export type str = leaf < string > ;
export type num = leaf < number > ;
export type bool = leaf < boolean > ;
}
export namespace leaf
{
export type ll < V > = leaf < V > | V ;
export namespace ll
{
export type str = ll < string > ;
export type num = ll < number > ;
export type bool = ll < boolean > ;
}
}
/* readonly */
export namespace leaf
{
/* R 実体生成 */
export function r < V > ( value : V , branch ? : leaf.branch < V > ) : r < V >
{
return new leaf.Entity ( value , branch ) ;
}
export namespace r
{
export const str = r < string > ;
export const num = r < number > ;
export const bool = r < boolean > ;
}
/* R 型定義 */
export interface r < V > extends leaf < V >
{
get value () : V ;
}
export namespace r
{
export type str = r < string > ;
export type num = r < number > ;
export type bool = r < boolean > ;
}
export namespace r
{
export type ll < V > = r < V > | V ;
export namespace ll
{
export type str = ll < string > ;
export type num = ll < number > ;
export type bool = ll < boolean > ;
}
}
}
/* */
export namespace leaf
{
/* 基底抽象クラス */
export abstract class Source < V > implements leaf < V >
{
protected refs = new Set < ref < V > > ;
public add_ref ( ref : ref < V > , old_v ? : V ) : void
{
this.refs.add ( ref ) ;
ref.src_value_change ( this.value , old_v ) ;
}
public remove_ref ( ref : ref < V > ) : void
{
this.refs.delete ( ref );
}
public abstract get value () : V ;
public abstract set value ( value : V ) ;
public abstract set ( value : V , changer ? : object ) : void ;
public [ set_value ] ( new_v: V , changer ? : object ) : void
{
this.set ( new_v , changer ) ;
}
}
/* 値実体クラス */
export class Entity < V > extends Source < V >
{
constructor
(
protected p_value : V ,
protected p_branch ? : branch < V >
)
{
super () ;
}
public override get value ()
{
return this.p_value ;
}
public override set value ( new_v : V ) { this.set ( new_v ) }
public override set( new_v: V , changer ? : object ) : void
{
if( new_v === this.p_value ) return ;
const old_v = this.p_value ;
this.p_value = new_v ;
if( changer != this.p_branch )
{
this.p_branch?.update ( new_v , old_v ) ;
}
this.refs.forEach
(
ref => ref != changer &&
(
ref.src_value_change ( new_v , old_v )
)
);
}
}
/* 参照・変換クラス */
export class Converter < S , R = S > extends Source < R >
{
constructor
(
protected src : Source < S > ,
protected to_ref : conv < S , R > ,
x ? : [ conv < S , R > , conv < R , S > ]
)
{
super () ;
src.add_ref( this ) ;
}
public override get value () : R
{
return this.to_ref ( this.src.value ) ;
}
public override set value ( new_v : R )
{ /* 未実装 */ }
public override set ( value : R, changer ? : object ) : void
{
/* 未実装 */ ;
}
/* */
public src_value_change ( new_v : S , old_v ? : S )
{
const new_rv = this.to_ref ( new_v ) ;
const old_rv = old_v === undefined ? undefined : this.to_ref ( old_v ) ;
this.refs.forEach
(
ref => ref.src_value_change ( new_rv , old_rv )
);
}
}
type conv < S ,R > = ( s : S ) => R ;
/* */
export const ref = < V >
(
src : leaf < V > ,
src_value_change : update < V >
) : ref < V > => new Ref ( src , src_value_change ) ;
export interface ref < V >
{
src_value_change : update < V > ;
src_term ? () : void ;
term ? () : void ;
}
export class Ref < V > implements ref < V >
{
constructor
(
protected src : leaf < V > ,
public src_value_change : update < V > ,
)
{
src ?.add_ref ( this ) ;
}
public term ? () : void
{
this.src ?.remove_ref ( this ) ;
}
}
/* */
export type update < V > = ( new_v : V , old_v ? : V ) => void ;
export type update_ref < V > = ( new_v ? : V , old_v ? : V ) => void ;
export interface branch < V >
{
update : update < V > ;
}
}