関数と型の詳細は、sobria の API に記述しています。
ファイルの種類と JSX の型
JSX の型は、ファイルを配置したディレクトリで決まります。
| 項目 | src/elements/ | src/routes/ | src/ のそのほかのファイル |
|---|---|---|---|
| 描画する場所 | ブラウザとサーバー | サーバー | 使用した側 |
| ネイティブのタグのイベントハンドラー | 使用できます | 型エラー | 型エラー |
ref | 使用できます | 型エラー | 型エラー |
要素に指定する load:* | 型エラー | 使用できます | 型エラー |
| 記述できるコンポーネント | 要素、補助コンポーネント、共通のコンポーネント | サーバーコンポーネント、要素、共通のコンポーネント | 共通のコンポーネント |
src/ のそのほかのファイルに配置した共通のコンポーネントは、ページの中ではサーバーで、要素の中ではブラウザで描画します。共通のコンポーネントには、ref、load:*、ホストの属性を指定できません。
属性の名前
ネイティブのタグの属性は、HTML の属性名で記述します。
| 記述する名前 | 型エラーになる名前 |
|---|---|
class | className |
for | htmlFor |
tabindex | tabIndex |
data-* と aria-* の属性は、名前を検査しません。SVG のタグと、名前に - を含むタグ(ほかのライブラリの Custom Element)も、属性の名前を検査しません。
属性の値
| 値 | 出力 |
|---|---|
true | 値の無い属性(disabled) |
false、null、undefined | 属性を出力しません |
aria-*、draggable、spellcheck、contenteditable の真偽値 | "true" か "false"。false でも出力します |
| 空の文字列 | 空の値の属性(alt="") |
class のオブジェクト | 値が真のキーを空白で区切った文字列。真のキーが無ければ出力しません |
style のオブジェクト | プロパティ:値 を ; で区切った文字列。値が null、undefined、false のプロパティを除き、残るプロパティが無ければ出力しません |
| そのほかの値 | 文字列に変換した値 |
classには、文字列か、キーをクラスの名前とするオブジェクトを指定しますstyleには、文字列か、background-colorのような CSS のプロパティ名と CSS 変数をキーとするオブジェクトを指定します。sobria は数値に単位を付与しないので、長さは"10px"のように文字列で指定します
<p class={{ active: selected(), done: false }} style={{ color: "red", "--gap": "4px" }}>
…
</p>プロパティに設定する値
要素の中では、次の値をプロパティに設定します。
| タグ | プロパティ |
|---|---|
input | value、checked |
textarea | value |
select | value |
option | selected |
サーバーで描画するときは、textarea の value をテキストの子として出力し、select の value に一致する option に selected を付与します。option の値は、value が無ければテキストの子の内容です。
値の更新
要素の中の JSX では、関数の呼び出し、プロパティの参照、タグ付きテンプレートのどれかを含む式が、Signal の値が変わるたびに再評価されます。
| 記述 | 値が変わったときの動作 |
|---|---|
{count()}、{props.max} | 再評価して表示を更新します |
class={{ active: selected() }} | 再評価して属性を更新します |
| リテラル、変数の名前だけの式 | 更新しません |
onClick、ref、key の値 | ノードを作成した時点の値を使用します |
サーバーコンポーネントは 1 回だけ実行します。async のサーバーコンポーネントでは、<h1>{await greet()}</h1> のように JSX の中に await を記述できます。
子
| 値 | 表示 |
|---|---|
| 文字列、数値、BigInt | テキスト。HTML の特殊文字はエスケープします |
true、false、null、undefined | 何も表示しません |
| 配列 | 要素を順に表示します |
| JSX | そのノード |
| DOM の Node | そのノードを表示します。要素の中でだけ記述できます |
| 関数 | 関数の戻り値。要素の中では、関数が参照する Signal の値が変わるたびに、表示を更新します |
イベント
ネイティブのタグのイベントハンドラーは、要素のファイルでだけ記述できます。
// src/elements/counter.tsx
<button onClick={() => setCount(count() + 1)}>{count()}</button>- イベント名は、
onの後の部分を小文字にした名前です。onClickはclick、onDblClickはdblclickのリスナーになります - HTML のタグに記述できるのは、DOM の標準のイベントの名前(
onClick、onInputなど)だけです。ほかの名前は型エラーになります - SVG のタグと、名前に
-を含むタグには、onと英字の大文字で始まる任意の名前を記述できます。onLikeClickはlikeclickのリスナーになるので、これらのタグのonで始まる名前の属性では、like-clickのように-を含む名前のイベントを受け取れません。受け取るには、refの関数の中でaddEventListenerを呼び出します - イベントの
currentTargetは、そのタグの DOM の要素の型になります - サーバーコンポーネントのタグに記述したイベントハンドラーは、ブラウザで実行されません。開発時は、SSR で警告を出力します
要素が発行するイベントの受け取り方は、関数の props とイベントに記述しています。
ref
ref には、ノードを受け取る関数を指定します。
| 項目 | 内容 |
|---|---|
| 記述できるファイル | 要素のファイルだけです |
| ネイティブのタグ | ノードを作成した時点で呼び出します |
| sobria の要素 | 要素が起動し、ctx.expose を実行した後に呼び出します。引数の型は ElementRef<typeof X> です |
| 戻り値 | 関数を返すと、条件の分岐でノードが消えるときと、要素がページから外れるときに、その関数を呼び出します |
| SSR | 呼び出しません |
// src/elements/chart.tsx
<canvas
ref={(el) => {
const chart = new Chart(el);
return () => chart.destroy();
}}
/>補助コンポーネントは要素ではないので、Props の型に ref を宣言できます。宣言した ref は、ほかの props と同じく関数の引数に渡ります。
key
key には文字列か数値を指定します。key は data-ok 属性として出力し、描き直しでは、同じキーのノードどうしを対応付けます。{...props} のようなスプレッドの後に key を記述すると、ビルドのエラーになります。
load:*
load:* は、要素の起動のタイミングを、要素を使用する箇所ごとに指定します。src/routes/ のファイルで要素に指定したときだけ有効です。
| 属性 | 起動する時点 |
|---|---|
load:load | すぐに起動します |
load:idle | ブラウザの処理が空いた時点 |
load:visible | 要素が初めてビューポートに入った時点 |
load:media="クエリ" | メディアクエリに一致した時点。最初から一致していれば、すぐに起動します |
// src/routes/page.tsx
<Chart load:visible data={data} />
<Chart load:media="(min-width: 800px)" data={data} />- 要素のデフォルトの起動のタイミングは、要素のファイルの
export const loadに"load"、"idle"、"visible"のどれかの文字列のリテラルで記述します。それぞれ、load:load、load:idle、load:visibleと同じ時点で起動します。変数などのほかの式を記述すると、ビルドのエラーになります。記述しなければ"load"です - 要素の Shadow DOM の中で使用した要素は、親の要素と同時に起動します。
load:*とexport const loadは適用しません - SSR では、
load:*をdata-sobria-load属性に変換して出力します
起動の詳細は読み込みと値の受け渡しに記述しています。
要素の props と属性
props の名前と属性の名前
props の属性の名前は、props の名前を kebab-case にしたものです。大文字の連続は 1 語として扱います。
| props の名前 | 属性の名前 |
|---|---|
userId | user-id |
imageURL | image-url |
2 つの props が同じ属性の名前になると、ビルドのエラーになります。
props の型と受け取り方
要素が props をどう受け取るかは、要素のファイルの Props の型で決まります。交差型や Pick で組み立てた型も、TypeScript が解決した各 props の型で、次の表の行が決まります。ホストのプロパティの名前は、props の名前と同じです。
| Props の型 | 受け取り方 |
|---|---|
string、number、boolean、それぞれのリテラル型、同じ種類のリテラル型のユニオン型("small" | "large" など)、enum、テンプレートのリテラル型 | プロパティと属性。属性の文字列を Props の型に変換します |
| 関数 | プロパティだけ |
JSX.Element、Slot | スロット |
オブジェクト、配列、Date、Map など | プロパティだけ |
そのほかの型(string と number のユニオン型、unknown など) | プロパティと属性。属性の文字列をそのまま渡します |
関数ではない型で宣言した props に関数を渡すと、sobria がその関数を呼び出し、戻り値を props の値として要素に渡します。
属性の文字列は、次のように変換します。number の props は、属性の値を Number で変換した数値を受け取るので、"" は 0 に、"abc" は NaN になります。
| Props の型 | 属性の値 | props の値 |
|---|---|---|
number | "3" | 3 |
boolean | "false" | false |
boolean | "false" 以外の値 | true |
| すべての型 | 属性を削除する | undefined |
サーバーコンポーネントから渡す値
| 値 | 渡し方 |
|---|---|
| 文字列、数値、真偽値 | ホストの属性として出力します。false と null は属性を出力しません。属性の値から元の値に戻せない値(false、null、-0 と、文字列と数値のユニオン型のように属性の変換の方法を型から決定できない props に渡した数値と真偽値)は、ページにも埋め込み、要素が起動した後も同じ値で渡します |
オブジェクト、配列、BigInt、Date、Map、Set、RegExp、型付き配列など、シリアライズできる値 | ページに埋め込み、型を保持したまま要素に渡します。要素はその値が届いてから起動します |
| サーバー関数 | 要素から呼び出すと、サーバーで実行します。オブジェクトの中に入れても渡せます |
| JSX | スロットの props にだけ渡せます |
次の値は渡せません。オブジェクトや配列の中に入れても渡せません。渡すと、SSR の時点でエラーになります。
- サーバー関数以外の関数
- クラスのインスタンス
- symbol
- Promise。
awaitで解決した値を渡します - Error。
{ message }のようなオブジェクトに変換して渡します - スロットの props 以外に渡した JSX
エラーのメッセージは、要素のタグ名と props の名前で始まります。例えば、sobria-event-form の要素の onSave にサーバー関数以外の関数を渡すと、<sobria-event-form> onSave は関数なので、シリアライズできません。 で始まるエラーになります。
要素の中で別の要素に渡す値には、この制約はありません。
Props の型に記述しなくても受け付ける props
| props | 内容 |
|---|---|
ref、key、load:* | 予約された props です。要素の Props の型に記述すると、ビルドのエラーになります |
name、disabled | フォームの部品の名前と無効の状態です(フォーム)。Props の型に宣言すると、その型で値を受け取ります |
id、class、style、role、aria-*、data-* など | ホストの属性として、そのままホストに出力します |
Props の型に無い、DOM の標準のイベントの名前(onClick など)の関数 | 要素のファイルで記述すると、ホストのイベントのリスナーにします。イベント名は、on の後の部分を小文字にした名前です。src/routes/ のファイルで記述すると、型エラーになります |
Props の型に無く、DOM の標準のイベントの名前でもない on で始まる名前(onLikeClick など)を sobria の要素のタグに記述すると、型エラーになります。
props にできない名前
ホストの属性やプロパティと衝突する名前は、props の名前にできません。Props の型に記述すると、ビルドのエラーになります。エラーのメッセージには、名前を使用できない理由と、代わりの名前の例が含まれます。
- ホストにそのまま出力する属性(
id、class、style、slot、roleなど) - ブラウザが動作を持つグローバル属性(
title、hidden、langなど) HTMLElementのプロパティとメソッド(remove、focus、textContentなど)- フォームの部品の API の名前(
form、labels、validityなど) - ライフサイクルのコールバックの名前(
connectedCallbackなど) ariaかdataの後に英字の大文字が続く名前(ariaLabel、dataSourceなど)
Props の型をヘルパー型 Props<T> で包むと、エディタで同じ検査が実行されます。名前の無いスロットの children は、props にできます。
関数の props とイベント
Props の型で関数として宣言した onX の形(on の後が英字の大文字)の props は、使用する側が値を渡していなければイベントになります。要素がその props を呼び出すと、ホストが CustomEvent を発行します。
| 項目 | 内容 |
|---|---|
| イベント名 | on の後の部分を kebab-case にした名前。onSelect は select、onLikeClick は like-click です |
detail | 呼び出したときの最初の引数 |
bubbles、composed | true |
sobria の JSX でイベントの props に関数を渡すと、要素はその関数を呼び出し、イベントを発行しません。要素の側では、関数が渡されたかどうかを判別できません。
// sobria の外から受け取る場合
el.addEventListener("like-click", (e) => e.detail);