npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

ftuttes

v0.10.0

Published

A UI component library built with TypeScript

Readme

THIS IS PURE TypeScript FRAMEWORK

fTutteSとは

fTutteSとは、TypeScriptで構成されたライブラリです。Flutterのような書き心地でWeb画面をデザインすることができます。fTutteSの中には、coretaterialscssKittiperestommandtransitusという6つのライブラリを備えています。(後述)

NPM Version NPM Unpacked Size : mjs and js NPM Last Update NPM Downloads NPM License npm package minimized gzipped size GitHub repo size GitHub branch status GitHub commit activity GitHub last commit X (formerly Twitter) URL

内部ライブラリとその説明

fTutteS-Core

コンポーネントやウィジェットを作成するために必要な基本的なコード群。 基本的にはレンダリング担当だと考えてもらえればと思います。

fTutteS-Taterials

基本的なコンポーネント群が格納されているライブラリです。 おおよそFlutterと同じラインナップを目指していますが、現段階でコンポーネントの数はそこまで多くはありません。 ただ、基本的なレイアウトは作成することができます。

fTutteS-CSSKit

Taterialsで定義されているコンポーネントに対して、CSSを簡単に適用することができるライブラリ。

fTutteS-Tiperes

providerによる状態管理と、それを使役するコンポーネントのライブラリ。 また、それらproviderへの状態監視も兼ね備えています。 そのうち、別ライブラリに分離するかもしれない...

fTutteS-Tommand(別ライブラリ)

ftuttesによるnpxコマンド定義ライブラリです。 このライブラリをインストールする必要はありません。 npx経由でftuttesプロジェクトとtransitusプロジェクトをセットアップするためのライブラリです。

  • https://github.com/Rerurate514/fTutteS-Tommand
  • https://www.npmjs.com/package/tommand

fTutteS-Transitus(別ライブラリ)

ftuttes専用に構築されたNodeJSウェブルーティングサーバーライブラリです。 このライブラリを使用するには、プロジェクト内で必ずftuttesをインストールする必要があります。

  • https://github.com/Rerurate514/fTutteS-Transitus
  • https://www.npmjs.com/package/transitus

fTutteS-Trystalize(別ライブラリ)

fTutteSで作成されたViewなどを静的なファイルに変換するライブラリです。

現在、開発中...

  • https://github.com/Rerurate514/fTutteS-Trystalize
  • https://www.npmjs.com/package/trystalize

インストール方法

npxが使用できる場合

もしnpxを使用できる環境なら以下のコマンドを打つだけでテンプレートプロジェクトが生成されます。

npx tommand create-ftuttes プロジェクト名

さらにtransitusを使用して、Webルーティングを行いたかったり、単体で動作させたい場合は以下のコマンドを使用します。

npx tommand create-transitus-ftuttes プロジェクト名

npxが使用できない場合はSETUP.mdを参照してください。 または、npxパッケージのダウンロード元テンプレートからファイルをローカルに落としてください。

用語集

  • View(ビュー):Viewクラスまたはその他UI構築クラスから継承して作成されたUI部品
  • コンポーネント:fTutteS側から提供されるViewのこと
  • ウィジェット:fTutteS使用者がコンポーネントを組み合わせて作成したViewのこと
  • インターフェース:fTutteSが提供している継承することで機能を使用できるクラスのこと。(ViewProviderScopeなど)

実際の使用方法

ウィジェットの作成

Viewの継承

Viewインターフェースの詳細はVIEW_INTERFACE_USAGE.mdにも示されています。このREADMEでは基本的で最も小さい形でViewを説明します。

まず、このフレームワークには全てのウィジェットの根幹となるViewインターフェースが提供されています。 このクラスを継承するのが、ウィジェット作成の第一段階です。

class SampleWidget extends View {
	...
}

Viewコンストラクタの呼び出し

そして、Viewクラス側でウィジェットを描画するのに必要な処理を行うためにViewのコンストラクタを呼び出します。

class SampleWidget extends View {
    constructor(){
        super();
    }
}

ウィジェットの要素定義

次にこのウィジェットのHTMLElement要素を定義します。 これにはViewクラスで定義されているcreateWrapViewをオーバーライドして作成します。 これにはJSで使用できるdocument.createElementメソッドを使用してHTMLElementを作成できます。

class SampleWidget extends View {
    constructor(){
        super();
    }

    override createWrapView(){
        let div = document.createElement("div");
        return div;
    }
}

もし作成したい要素がdivならばオーバーライドする必要はないです。 今回の例ではオーバーライドせずに例を示します。

ウィジェットのスタイル定義

createWrapViewで作成したHTMLElement要素に対してスタイルを適用するにはstyledViewメソッドをオーバーライドします。

class SampleWidget extends View {
	constructor(){
		super();
	}

	override styledView(element: HTMLElement): HTMLElement {
		element.className = "sample-widget";

		element.style.backgroundColor = "red";
		element.style.width = "100px";
		element.style.height = "100px";

		return element;
	}
}

styledViewメソッドには引数として、createWrapViewで作成したHTMLElementが渡されます。 スタイルの適用の詳細にはHTMLElementを参照してください。 このメソッドの最後で必ずスタイルを適用した要素をreturnで返却してください。 返却しない場合、以下のエラーが返されます。

throw new TypeError("styledViewには必ずHTMLElenmentオブジェクトを格納してください。 渡された型:", typeof child);

なお、このメソッドに用がない場合、オーバーライドせずに無視してもらっても構いません。

embedScriptToView

もしウィジェットに何らかのJSで標準用意されているスクリプトを埋め込みたいならembedScriptToView内で行ってください。 例えば、ラジオボタンのイベントの発火などです。

override embedScriptToView(element: HTMLLabelElement): HTMLLabelElement {
    this._setEventListenerToRadioBtn(element);
    return element;
}

private _setEventListenerToRadioBtn(radioBtn: HTMLLabelElement): void {
    const radioInput = radioBtn.firstElementChild as HTMLInputElement;
    radioInput.addEventListener("change", (e: Event) => {
        const target = e.target as HTMLInputElement;
        if (target.checked) {
            //イベントの発火により動作するコード
        }
    });
}

このメソッドでも最後に要素をreturnで返却してください。 返却しない場合、以下のエラーが返されます。

throw new TypeError("embedScriptToViewには必ずHTMLElenmentオブジェクトを格納してください。 渡された型:", typeof child);

なお、このメソッドに用がない場合、オーバーライドせずに無視してもらっても構いません。

ウィジェットの子要素を作成する。

createWrapViewで作成した要素の中に子要素を入れていくにはbuildメソッドをオーバーライドして使用します。 ここには自身で作成したウィジェットやfTutteSで用意されているコンポーネントが使用できます。

class SampleWidget extends View {
	constructor(){
		super();
	}

	override styledView(element){
		element.className = "sample-widget";

		element.style.backgroundColor = "red";
		element.style.width = "100px";
		element.style.height = "100px";

		return element;
	}

    override build() {
        return new Text({
            text: "Hello World"
        });
    }
}

ここではTextコンポーネントを使用して文字を表示してみます。 このとき必ず、コンポーネントやウィジェットをreturnで返却してください。 これで一つの基本的なウィジェットを作成することができました。

ウィジェットに値の受け渡し

例えば、ウィジェットに子要素を渡して、それを子要素でビルドして欲しい時や親要素のプロパティを子要素に渡して表示して欲しい時があるかもしれません。 その際のやり方をこのセクションでは解説します。

まず、ftuttesでは親要素から渡された文字列をTextコンポーネントで表示したいとき、このように記述します。

class SampleWidget extends View {
    private text: string;

    constructor(text: string){
        super();
        this.text = text;//ここでSampleWidgetのインスタンス変数に格納
    }

    override styledView(element: HTMLElement){
        element.className = "sample-widget";

        element.style.backgroundColor = "red";
        element.style.width = "100px";
        element.style.height = "100px";

        return element;
    }

    override build() {
        return new Text({
            text: this.text
        });
    }
}

同様に子要素を渡された場合でも、

class SampleWidget extends View {
    private child: View;

    constructor(child: View) {
        super();
        this.child = child;
    }

    override styledView(element: HTMLElement){
        element.className = "sample-widget";

        element.style.backgroundColor = "red";
        element.style.width = "100px";
        element.style.height = "100px";

        return element;
    }

    override build() {
        return new Column({
            children: [
                this.child,
                this.child,
                this.child,
            ]
        });
    }
}

と書くことで、簡単に子要素を描画することができます。

Providerによる状態管理

このfTutteSフレームワークにはTiperesという状態管理ライブラリが付属しています。 値が変更されたことによって、ウィジェットをリビルド、再描画したい際にはProviderを使用して行います。

Providerの作成

Providerを作成するにはProviderクラスのファクトリメソッドcreateProvider()を使用して行います。 以下に試しに作成してみます。

const sampleProvider = Provider.createProvider(() => {
	return 0;
})

引数には関数オブジェクトを渡し、その中で初期値をreturnで返却します。これはプリミティブな数値を管理、保持、監視するProviderです。ただし、なんの設定もしていないと値の変更の監視はできません。

Providerの使用-ProviderScope-read

Providerの値の変更を監視するためにはView単位で行います。 fTutteSでは、値の変更を自動的に監視し、再描画を行うProviderScopeというインターフェースを提供しています。 ProviderScopeを継承してウィジェットを作成します。

import { assembleView, Column, ElevatedButton, Provider, ProviderScope, Row, Text, View } from "ftuttes";

const sampleProvider = Provider.createProvider(() => {
    return 0;
});

class SampleWidget extends ProviderScope {
	constructor(private child: View, providers: Provider<any>[]){
        super({
            providers: providers
        });
	}

	override styledView(element: HTMLElement): HTMLElement{
		element.className = "sample-widget";

		element.style.backgroundColor = "yellow";
		element.style.width = "100px";
		element.style.height = "100px";

		return element;
	}

	override build(){
		let num = sampleProvider.read();

		return new Column({
            children: [
                new ElevatedButton({
                    onClick: () => {
                        sampleProvider.update((currentValue) => {
                            return currentValue + 1;
                        })
                    },
                    child: new Text({
                        text: "Click Here!"
                    }),
                }),
                new Row({
                    children: [
                        this.child,
                        new Text({
                            text: num.toString()
                        })
                    ]
                }),
            ]
        });
    }
}

assembleView(
    new SampleWidget(
        new Text({
            text: "value="
        }),
        [ sampleProvider ]
    );
);

ProviderScopeクラスにはコンストラクタとして、三つのプロパティを渡すことができます。 propswatchingProviderchildです。 propsにはViewと同じ役割を持ちます。 watchingProviderには、Providerの配列を渡します。 ProviderScopeに渡されたProviderは自動的にリッスン状態になり、配列のProviderの一つでも値が変更されると、ProviderScopeを継承したウィジェットが再ビルドされます。

ここではProviderクラスのreadメソッドを使用して値を読み取っています。 readメソッドはただ値を読み取るためのメソッドです。

Providerの使用-ProviderScope-update

Providerの値を変更してウィジェットを再描画するにはProviderクラスのupdateメソッドを使用します。 今回はElevatedButtonを押したらcounterの値をインクリメントして、Textに反映されるコードを作成してみます。

import { 
    assembleView, 
    Text, 
    Card, 
    Column,
    ElevatedButton,
    BaseCSS,
    SpaceBox,
    Center, 
    TextCSS, 
    FontCSS, 
    Provider, 
    ProviderObserver, 
    ProviderScope,
    ShadowLevel,
} from 'ftuttes';

const counter = Provider.createProvider((ref) => {
    return 0;
}, "counter");

class ProviderExample extends ProviderScope {
    constructor(){
        super({
            watchingProviders: [ counter ]
        });
    }

    override styledView(element: HTMLElement){
        element.style.height = "90vh";

        return element;
    }

    override build(){
        return new Center(
            new Card({
                radius:"16px",
                padding: "15px",
                background: "wheat",
                elevation: ShadowLevel.LVL5,
                child: new Column({
                    children: [
                        new ElevatedButton({
                            child: new Text("CLICK!"),
                            baseCSS: new BaseCSS({
                                height: "32px",
                            }),
                            onClick: () => {
                                counter.update((value) => {
                                    return value + 1;
                                })
                            }
                        }),
                        new SpaceBox({height: "16px"}),
                        new Text("click count : " + counter.read()),
                    ]
                }),
            })
        );
    }
}

assembleView(
    new ProviderExample()
);

ElevatedButtonコンポーネントのonClickプロパティにてProviderupdateを実行しています。 updateにはそのProviderの現在の値が渡されるので、その値にインクリメントをしてreturnで返却し値を変更しています。 その結果、ProviderScopeを継承したウィジェット自身がProvider内の値の変更を検知し自身を再描画します。

Provider例-依存関係

Providerクラスには依存関係を管理する機能があります。 ここでは簡単なユーザを管理するProviderを作成します。

//プロバイダーを作成
const userProvider = Provider.createProvider(ref => {
    return { name: "Jhon", age: 25 };
});

そしてuserProvider内のageを監視するには以下のようにrefを使用してproviderを作成します。

const userAgeProvider = Provider.createProvider(ref => {
    ref.watch(userProvider, (user, currentValue) => {
        return user.age;
    });
    return ref.read(userProvider).age;
});

このように記述すると、自動的にuserProviderがリッスン状態になり、userProviderageが変更された際にuserAgeProviderの値を自動的に変更します。これはwatchまたはProviderScopeuserAgeProviderの変更を監視することができます。

LimitedProviderScope

ProviderScopeインターフェースはViewを継承しなければならず、さらにwatchしているproviderの値が変更されるたびに再描画されてパフォーマンスが下がってしまいます。 これを解決するためにfTutteSはそのrebuildのスコープを狭めてくれるLimitedProviderScopeコンポーネントを提供しています。

import { 
    assembleView, 
    BaseCSS, 
    Center, 
    Column, 
    ElevatedButton, 
    LimitedProviderScope, 
    Provider, 
    SpaceBox, 
    Text, 
    View 
} from "ftuttes";

const counter = Provider.createProvider(() => {
    return 0;
}, "counter");

class ProviderExample extends View {
    constructor(){
        super();
    }

    override styledView(element: HTMLElement): HTMLElement{
        element.style.height = "90vh";

        return element;
    }

    override build(){
        return new Center({
            child: new Column({
                children: [
                    new ElevatedButton({
                        child: new Text({
                            text: "CLICK!"
                        }),
                        baseCSS: new BaseCSS({
                            padding: "32px",
                        }),
                        onClick: () => {
                            counter.update((value: any) => {
                                return value + 1;
                            })
                        }
                    }),
                    new SpaceBox({height: "16px"}),
                    new LimitedProviderScope({
                        providers: [ counter ],
                        builder: ([providerValue]) => {
                            return new Text({
                                text: "click count : " + providerValue
                            });
                        }
                    })
                ]
            }),
        });
    }
}

assembleView(
    new ProviderExample()
);

通常のProviderScopeを継承したやり方では、このProviderExampleウィジェット全体が再描画されてしまいます。しかし、このLimitedProviderScopeを使用したやり方ではTextコンポーネントのみが再描画されます。このbuild関数オブジェクトの引数ですが、providerprovidersで格納した順番でそれぞれのProviderの値が格納された配列が返されます。

ProviderObserverによる値の変更確認

TiperesにはProviderObserverというProviderの値の変更履歴や依存関係を記録するクラスが実装されています。

そして、以下のコードを使用してログを確認することができます。

Providerの更新時、依存関係構築時にログを出力する。
new ProviderObserver().outLogs()
Providerの更新履歴
console.log(new ProviderObserver().getAllUpdateHistory());
特定のProviderの更新履歴
console.log(new ProviderObserver().getFilteredUpdateHistory(userProvider));
Providerの依存関係を表示
console.log(new ProviderObserver().getDependencyGraph());

Notifierによる状態管理

さらに、TiperesにはProviderだけでなくNotifierも作成することができます。 NotifierProviderの値変更をもっと柔軟に定義することができるクラスです。

まず初めにNotifierを継承したクラスを作成します。 今回は簡単に値を増加させたり、初期値に戻したりする動作を想定します。 ジェネリクスには管理する値の型を入れます。 値の変更には、Notifierクラス内で実装されているupdateメソッドを使用して行います。

class CountNotifier extends Notifier<number> {
    protected build(): number {
        return 0;
    }

    public increment() {
        this.update((arg) => {
            return ++arg;
        });
    }

    public reset() {
        this.update((arg) => {
            return 0;
        });
    }
}

そしてこのNotifierクラスを使用するにはNotifierProviderにこの作成したクラスを渡します。

let cnp: NotifierProvider<CountNotifier, number> = new NotifierProvider<CountNotifier, number>(
    () => new CountNotifier()
);

この作成したNotifierProviderLimitedProviderScopeで監視、状態変更を行うことができます。 定義したNotifierProviderからNotifierで定義したメソッドにアクセスするにはNotifierProvider内のプロパティであるnotifierからアクセスすることができます。 例:cnp.notifier.reset()

LimitedProviderScopeNotifierProviderを渡した際には、Notifierbuilderメソッドの引数として渡されるのでstateにアクセスして値を表示することができます。

new Column({
    children: [
        new LimitedProviderScope({
            providers: [ cnp ],
            builder(count) {
                return new Text({
                    text: count[0].state,
                })
            },
        }),
        new ElevatedButton({
            radius: "4px",
            onClick: () => {
                cnp.notifier.increment();
            },
            child: new Center({
                child: new Text({
                    text: "increment",
                })
            })
        }),
        new ElevatedButton({
            radius: "4px",
            onClick: () => {
                cnp.notifier.reset();
            },
            child: new Center({
                child: new Text({
                    text: "reset",
                })
            })
        }),
    ]
})

コンポーネントの一覧

現在実装されているコンポーネントの一覧はCOMPONENTS.mdにて閲覧することができます。

最後に余談

//TODO

ライセンス

MIT