@wcstack/view-transition 1.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +169 -0
- package/README.md +169 -0
- package/dist/auto.min.js +2 -0
- package/dist/auto.min.js.map +1 -0
- package/dist/index.d.ts +310 -0
- package/dist/index.esm.js +753 -0
- package/dist/index.esm.js.map +1 -0
- package/package.json +72 -0
package/README.ja.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# @wcstack/view-transition
|
|
2
|
+
|
|
3
|
+
> 🤖 **AI coding agents**: This README is a package-level reference, not the primary entry point for building a wcstack application. If you have not already done so, first read the repository [README](https://github.com/wcstack/wcstack#readme) and [AGENTS.md](https://github.com/wcstack/wcstack/blob/main/AGENTS.md), then use the [wcstack-app skill](https://github.com/wcstack/wcstack-skill).
|
|
4
|
+
|
|
5
|
+
`@wcstack/view-transition` は wcstack のページに**退場と移動のアニメーション**を与える。フレームワークが消した DOM に対して CSS だけでは届かない、ちょうどその 2 つを担当する。
|
|
6
|
+
|
|
7
|
+
English: [README.md](./README.md)
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<script type="module" src="https://esm.run/@wcstack/view-transition/auto"></script>
|
|
11
|
+
|
|
12
|
+
<wcs-view-transition></wcs-view-transition>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
opt-in はこれだけ。以後、`@wcstack/router` のルート差し替えと `@wcstack/state` のリスト/分岐更新が [View Transition](https://developer.mozilla.org/docs/Web/API/View_Transition_API) の中で行われ、見た目は `::view-transition-*` に対する CSS で書く。
|
|
16
|
+
|
|
17
|
+
`<wcs-view-transition>` は I/O ノードではなく**ポリシーノード**である。何も描画せず、データもバインドせず、アニメーションの中身も書かない。決めるのは「その DOM 変更をアニメーションさせるか」と「2 つがぶつかったらどうするか」だけ。アニメーション自体は CSS に残す。
|
|
18
|
+
|
|
19
|
+
- **入力**: `for` / `mode` / `naming` / `naming-limit` / `reduced-motion` / `types` / `disabled`
|
|
20
|
+
- **出力**: `active` / `error`
|
|
21
|
+
- **コマンド**: `skip()`
|
|
22
|
+
|
|
23
|
+
## このパッケージ抜きで既にできていたこと
|
|
24
|
+
|
|
25
|
+
インストールの前に知っておく価値がある。問題の 3 分の 2 はもともとパッケージを必要としていない。
|
|
26
|
+
|
|
27
|
+
```css
|
|
28
|
+
/* 入場: 新しく挿入された行/分岐は JS 無しでアニメーションする */
|
|
29
|
+
li {
|
|
30
|
+
transition: opacity 0.2s, transform 0.2s;
|
|
31
|
+
@starting-style { opacity: 0; transform: translateY(-4px); }
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`class.x:` / `style.y:` バインドは生きた要素へ書き込むので、値の変化には通常の CSS transition がそのまま効く。`@starting-style` は**入場する**要素に効く — 新規の `for` 行と mount する `if` 分岐がまさにそれ。
|
|
36
|
+
|
|
37
|
+
CSS が届かないのは**退場**。wcstack は削除ノードを同期で detach するので、次の描画時点でアニメーションさせる相手が居ない。並べ替えも `insertBefore` の列で中間状態が無いため、**移動**を補間できない。このパッケージはそこを担当する — ブラウザが変更前の状態をスナップショットするので、退場する要素は変更後まで生き残る必要がない。
|
|
38
|
+
|
|
39
|
+
## インストール
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install @wcstack/view-transition
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## クイックスタート
|
|
46
|
+
|
|
47
|
+
### ルート遷移だけ
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<wcs-view-transition for="router"></wcs-view-transition>
|
|
51
|
+
|
|
52
|
+
<style>
|
|
53
|
+
::view-transition-old(root) { animation: fade-out 0.2s both; }
|
|
54
|
+
::view-transition-new(root) { animation: fade-in 0.2s both; }
|
|
55
|
+
</style>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`for="router"` にすると `@wcstack/state` の更新タイミングは一切変わらない([契約](#契約)参照)。
|
|
59
|
+
|
|
60
|
+
### 移動とフェードをするリスト行
|
|
61
|
+
|
|
62
|
+
```html
|
|
63
|
+
<wcs-view-transition naming="auto"></wcs-view-transition>
|
|
64
|
+
|
|
65
|
+
<ul>
|
|
66
|
+
<template data-wcs="for: todos">
|
|
67
|
+
<li>{{ .title }}</li>
|
|
68
|
+
</template>
|
|
69
|
+
</ul>
|
|
70
|
+
|
|
71
|
+
<style>
|
|
72
|
+
/* 自動命名された行はすべて wcs-row グループクラスを共有する */
|
|
73
|
+
::view-transition-group(*.wcs-row) { animation-duration: 0.25s; }
|
|
74
|
+
::view-transition-old(*.wcs-row) { animation: fade-out 0.25s both; }
|
|
75
|
+
::view-transition-new(*.wcs-row) { animation: fade-in 0.25s both; }
|
|
76
|
+
</style>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 属性
|
|
80
|
+
|
|
81
|
+
| 属性 | 値 | 既定 | 意味 |
|
|
82
|
+
|---|---|---|---|
|
|
83
|
+
| `for` | `router` / `state`(空白区切り) | `router state` | どの参加者をアニメーションさせるか。 |
|
|
84
|
+
| `mode` | `latest` / `queue` / `exhaust` | `latest` | 遷移実行中に変更が来たときの挙動 — [排他](#排他)参照。 |
|
|
85
|
+
| `naming` | `manual` / `auto` | `manual` | `view-transition-name` を誰が付けるか — [命名](#命名)参照。 |
|
|
86
|
+
| `naming-limit` | 整数 | `200` | 自動命名の上限。 |
|
|
87
|
+
| `reduced-motion` | `skip` / `animate` | `skip` | `skip` は `prefers-reduced-motion: reduce` を尊重し、アニメーション無しで適用する。 |
|
|
88
|
+
| `types` | 空白区切り | — | 対応環境で `startViewTransition({ types })` へ渡す(`:active-view-transition-type()` 用)。 |
|
|
89
|
+
| `disabled` | boolean | 無し | 不活性。全変更が即時適用される。バインド可能なので state からアニメーションを切れる。 |
|
|
90
|
+
|
|
91
|
+
1 ドキュメントに `<wcs-view-transition>` は 1 つ。2 つ目は警告して不活性になる(排他を提供するために存在するものが排他を奪い合っては本末転倒なので)。
|
|
92
|
+
|
|
93
|
+
## 契約
|
|
94
|
+
|
|
95
|
+
他のすべてが従属する規則: **DOM 変更は、アニメーションがどうなろうと、ちょうど 1 回適用される。** 未対応ブラウザ、非表示タブ、`prefers-reduced-motion`、`disabled`、衝突、`startViewTransition` の throw — どれも「変更は適用された」で終わる。アニメーションが再生できなかったせいで古い DOM が残ることはない。
|
|
96
|
+
|
|
97
|
+
タグを足す前に知っておくべき帰結が 2 つ。
|
|
98
|
+
|
|
99
|
+
1. **`for="state"`(既定で有効)は state の drain を非同期にする。** 現在の drain は microtask で着地するが、遷移の中ではフレームで着地する。state に書いてから `await Promise.resolve()` で DOM を読むコードは、代わりに遷移を待つ必要がある。`$updatedCallback` は影響を受けない(バインディング適用直後に発火する)。drain を完全に元のままにしたいなら `for="router"`。
|
|
100
|
+
2. **参加は要素単位ではなくドキュメント単位。** 1 つの updater がページ上の全 `<wcs-state>` をまとめて drain するので、`for="state"` は全部に効く。
|
|
101
|
+
|
|
102
|
+
遷移がスキップされ、変更が現行どおり同期適用されるのは: `startViewTransition` が無い環境、`document.hidden` が true のとき(バックグラウンドタブには描画機会が無く、遷移を張ると見に戻るまで DOM が凍る)、`reduced-motion="animate"` でない状態で `prefers-reduced-motion: reduce` のとき、`disabled` のとき、そして SSR 中。
|
|
103
|
+
|
|
104
|
+
## 排他
|
|
105
|
+
|
|
106
|
+
遷移は入れ子にできないので、誰かが調停しなければならない。同一 microtask のリクエストは**1 つ**の遷移へ合流し(ルート変更とそれが引き起こす state drain は互いを潰さず一緒にアニメーションする)、後から衝突したときの挙動を `mode` が決める。
|
|
107
|
+
|
|
108
|
+
| `mode` | 遷移実行中に来たとき |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `latest` | 実行中をスキップして新しい方をアニメーションする(既定)。 |
|
|
111
|
+
| `queue` | 連結。実行中が終わってから開始する。 |
|
|
112
|
+
| `exhaust` | アニメーションせず即時適用する。 |
|
|
113
|
+
|
|
114
|
+
`exhaust` が落とすのは*アニメーション*であって DOM 更新ではない。また 3 モードとも、実行中の遷移がスナップショットを撮る前に届いたリクエストはその遷移に合流するので、変更の順序が入れ替わることはない。
|
|
115
|
+
|
|
116
|
+
`skip()` は実行中の遷移を即座に終わらせる。その遷移が運んでいた DOM 変更は行われる。
|
|
117
|
+
|
|
118
|
+
## 命名
|
|
119
|
+
|
|
120
|
+
`view-transition-name` はブラウザがスナップショットを撮る**前**に要素へ付いている必要があり、変更中に付けることはできない。したがって「変わったものだけ命名する」は原理的に不可能で、2 つの戦略から選ぶことになる。
|
|
121
|
+
|
|
122
|
+
**`manual`(既定)** — 自分でバインドし、モーフさせたいものにだけ名前を付ける:
|
|
123
|
+
|
|
124
|
+
```html
|
|
125
|
+
<template data-wcs="for: todos">
|
|
126
|
+
<li data-wcs="style.viewTransitionName: .cssName">{{ .title }}</li>
|
|
127
|
+
</template>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**`auto`** — `@wcstack/state` がリスト行と `if` 分岐の最初の要素に mount 時へ名前を付け、`view-transition-class`(`wcs-row` / `wcs-branch`)も添えるので CSS からグループをまとめて指せる。名前は *content* に付いて回るため、プールから再利用された行は同じ名前を保つ — 実際に DOM がしたことと一致する。
|
|
131
|
+
|
|
132
|
+
自動命名には `naming-limit`(既定 200)の上限がある。命名された要素は 1 つずつスナップショットグループになり、数百個あると遷移は目に見えて重くなるため。上限を超えると命名を止め、コンソールに一度だけ通知する。大きなリストは `manual` で意図的に命名すべき。
|
|
133
|
+
|
|
134
|
+
**`auto` はロード順に依存する。** 名前は content の mount 時に割り当てられるので、このタグが upgrade した時点で既にページに載っていた行・分岐には付かない — 後から見直す仕組みは無い。arbiter が先に install されるよう、このパッケージの script タグを `@wcstack/state` より**前**に置くこと。さもないと初回描画の分は行ごとに morph せず、ルートのスナップショットに含まれる。`manual` は名前が普通のバインディングなので、この順序制約を持たない。
|
|
135
|
+
|
|
136
|
+
## state からのバインド
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<wcs-view-transition
|
|
140
|
+
data-wcs="disabled: animationsOff; active: transitionRunning"
|
|
141
|
+
></wcs-view-transition>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`active` は遷移実行中かどうか、`error` は最後の開始失敗(どちらも observable)。`disabled` / `mode` / `naming` / `types` / `participants` は書き込み可能な input で、`skip` は command token として使える。
|
|
145
|
+
|
|
146
|
+
## 直接利用(DOM 無し)
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
import { ViewTransitionCore } from "@wcstack/view-transition";
|
|
150
|
+
|
|
151
|
+
const core = new ViewTransitionCore();
|
|
152
|
+
core.naming = "auto";
|
|
153
|
+
core.install(); // ページの arbiter になる
|
|
154
|
+
await core.run(() => { /* DOM を変更する */ });
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`install()` は core をよく知られたグローバル Symbol に載せる。`@wcstack/state` と `@wcstack/router` はこのパッケージを import せずにそこから見つける。プロトコルは [docs/view-transition-design.ja.md](https://github.com/wcstack/wcstack/blob/main/docs/view-transition-design.ja.md) §4 に規定があり、独自 arbiter を載せたい採用者向けに `getTransitionRunner` / `runTransition` / `TRANSITION_RUNNER_KEY` を export している。
|
|
158
|
+
|
|
159
|
+
## デモ
|
|
160
|
+
|
|
161
|
+
[`examples/list-transitions`](./examples/list-transitions/) — 入場・退場・移動を 1 ページで並べ、チェックボックスで arbiter を止めて差を見られるようにしたデモ。ビルド不要で `index.html` を開くだけ。
|
|
162
|
+
|
|
163
|
+
## ブラウザ対応
|
|
164
|
+
|
|
165
|
+
same-document View Transition は Chromium 111+ と Safari 18+ で利用可能。Firefox は未対応。そこを含め API が無い環境では、全変更が即時適用され、アニメーションもエラーも起きない — ページは普通に動き、ただアニメーションしないだけ。
|
|
166
|
+
|
|
167
|
+
## ライセンス
|
|
168
|
+
|
|
169
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# @wcstack/view-transition
|
|
2
|
+
|
|
3
|
+
> 🤖 **AI coding agents**: This README is a package-level reference, not the primary entry point for building a wcstack application. If you have not already done so, first read the repository [README](https://github.com/wcstack/wcstack#readme) and [AGENTS.md](https://github.com/wcstack/wcstack/blob/main/AGENTS.md), then use the [wcstack-app skill](https://github.com/wcstack/wcstack-skill).
|
|
4
|
+
|
|
5
|
+
`@wcstack/view-transition` gives a wcstack page **leave and move animations** — the two things CSS alone cannot do for DOM that a framework removes.
|
|
6
|
+
|
|
7
|
+
日本語版: [README.ja.md](./README.ja.md)
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<script type="module" src="https://esm.run/@wcstack/view-transition/auto"></script>
|
|
11
|
+
|
|
12
|
+
<wcs-view-transition></wcs-view-transition>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
That is the whole opt-in. From then on, route swaps by `@wcstack/router` and list / branch updates by `@wcstack/state` run inside a [View Transition](https://developer.mozilla.org/docs/Web/API/View_Transition_API), and you style them in CSS against `::view-transition-*`.
|
|
16
|
+
|
|
17
|
+
`<wcs-view-transition>` is a **policy node**, not an I/O node: it renders nothing, binds no data, and describes no animation. It decides *whether* a DOM change animates and *what happens when two of them collide*. The animation itself stays in CSS, where it belongs.
|
|
18
|
+
|
|
19
|
+
- **inputs**: `for`, `mode`, `naming`, `naming-limit`, `reduced-motion`, `types`, `disabled`
|
|
20
|
+
- **outputs**: `active`, `error`
|
|
21
|
+
- **commands**: `skip()`
|
|
22
|
+
|
|
23
|
+
## What you already had without this package
|
|
24
|
+
|
|
25
|
+
Worth knowing before you install anything, because two thirds of the problem never needed a package:
|
|
26
|
+
|
|
27
|
+
```css
|
|
28
|
+
/* Enter: a newly inserted row / branch animates in, no JS involved. */
|
|
29
|
+
li {
|
|
30
|
+
transition: opacity 0.2s, transform 0.2s;
|
|
31
|
+
@starting-style { opacity: 0; transform: translateY(-4px); }
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`class.x:` and `style.y:` bindings write to the live element, so ordinary CSS transitions apply to any value change. `@starting-style` covers **entering** elements — which is exactly what a new `for` row and a mounting `if` branch are.
|
|
36
|
+
|
|
37
|
+
What CSS cannot reach is **leaving**: wcstack detaches removed nodes synchronously, so by the next paint there is nothing left to animate. And a reorder is a series of `insertBefore` calls with no intermediate state, so a **move** cannot be tweened either. That is what this package is for — the browser snapshots the old state before the change, so a leaving element does not have to survive it.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install @wcstack/view-transition
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
46
|
+
|
|
47
|
+
### Route transitions only
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<wcs-view-transition for="router"></wcs-view-transition>
|
|
51
|
+
|
|
52
|
+
<style>
|
|
53
|
+
::view-transition-old(root) { animation: fade-out 0.2s both; }
|
|
54
|
+
::view-transition-new(root) { animation: fade-in 0.2s both; }
|
|
55
|
+
</style>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`for="router"` keeps `@wcstack/state`'s update timing untouched (see [Contract](#contract)).
|
|
59
|
+
|
|
60
|
+
### List rows that move and fade
|
|
61
|
+
|
|
62
|
+
```html
|
|
63
|
+
<wcs-view-transition naming="auto"></wcs-view-transition>
|
|
64
|
+
|
|
65
|
+
<ul>
|
|
66
|
+
<template data-wcs="for: todos">
|
|
67
|
+
<li>{{ .title }}</li>
|
|
68
|
+
</template>
|
|
69
|
+
</ul>
|
|
70
|
+
|
|
71
|
+
<style>
|
|
72
|
+
/* every auto-named row shares the wcs-row group class */
|
|
73
|
+
::view-transition-group(*.wcs-row) { animation-duration: 0.25s; }
|
|
74
|
+
::view-transition-old(*.wcs-row) { animation: fade-out 0.25s both; }
|
|
75
|
+
::view-transition-new(*.wcs-row) { animation: fade-in 0.25s both; }
|
|
76
|
+
</style>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Attributes
|
|
80
|
+
|
|
81
|
+
| Attribute | Values | Default | Meaning |
|
|
82
|
+
|---|---|---|---|
|
|
83
|
+
| `for` | `router`, `state` (space-separated) | `router state` | Which participants animate. |
|
|
84
|
+
| `mode` | `latest` / `queue` / `exhaust` | `latest` | What happens when a change arrives while a transition is running — see [Exclusion](#exclusion). |
|
|
85
|
+
| `naming` | `manual` / `auto` | `manual` | Who assigns `view-transition-name` — see [Naming](#naming). |
|
|
86
|
+
| `naming-limit` | integer | `200` | Cap on auto-assigned names. |
|
|
87
|
+
| `reduced-motion` | `skip` / `animate` | `skip` | `skip` honors `prefers-reduced-motion: reduce` by applying changes without animating. |
|
|
88
|
+
| `types` | space-separated | — | Passed to `startViewTransition({ types })` where supported, for `:active-view-transition-type()`. |
|
|
89
|
+
| `disabled` | boolean | absent | Inert: every change applies immediately, no transitions. Bindable, so a page can switch animation off from state. |
|
|
90
|
+
|
|
91
|
+
One `<wcs-view-transition>` per document. A second one warns and stays inert rather than fighting the first over the exclusion it exists to provide.
|
|
92
|
+
|
|
93
|
+
## Contract
|
|
94
|
+
|
|
95
|
+
The rule everything else is subordinate to: **a DOM change is applied exactly once, whatever happens to its animation.** An unsupported browser, a hidden tab, `prefers-reduced-motion`, `disabled`, a collision, or a `startViewTransition` that throws all still end with the change applied. The page is never left showing stale DOM because an animation could not play.
|
|
96
|
+
|
|
97
|
+
Two consequences worth knowing before you add the tag:
|
|
98
|
+
|
|
99
|
+
1. **`for="state"` (on by default) makes the state drain asynchronous.** Today's drain lands on a microtask; inside a transition it lands on a frame. Code that writes state and then reads the DOM after `await Promise.resolve()` needs to wait for the transition instead. `$updatedCallback` is unaffected — it still fires right after the bindings are applied. Use `for="router"` to keep the drain exactly as it was.
|
|
100
|
+
2. **Participation is per document, not per element.** One updater drains every `<wcs-state>` on the page, so `for="state"` turns transitions on for all of them.
|
|
101
|
+
|
|
102
|
+
Transitions are skipped — and the change applied synchronously, on exactly today's timing — when the browser has no `startViewTransition`, when `document.hidden` is true (a background tab gets no rendering opportunities, so a transition there would freeze the DOM until you look at the tab again), under `prefers-reduced-motion: reduce` unless `reduced-motion="animate"`, while `disabled`, and during SSR.
|
|
103
|
+
|
|
104
|
+
## Exclusion
|
|
105
|
+
|
|
106
|
+
Transitions cannot nest, so something has to arbitrate. Every request made in the same microtask joins **one** transition (a route change and the state drain it triggers animate together rather than cancelling each other), and `mode` decides what a later collision does:
|
|
107
|
+
|
|
108
|
+
| `mode` | While a transition is running |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `latest` | Skip the running one, animate the newcomer. The default. |
|
|
111
|
+
| `queue` | Chain: the newcomer starts once the running one has finished. |
|
|
112
|
+
| `exhaust` | Apply the newcomer's change immediately, without animating it. |
|
|
113
|
+
|
|
114
|
+
`exhaust` drops the *animation*, never the DOM update. And in all three modes, a request that arrives before the running transition has taken its snapshot joins that transition, so changes are never applied out of order.
|
|
115
|
+
|
|
116
|
+
`skip()` finishes the running transition immediately; the DOM change it carries still happens.
|
|
117
|
+
|
|
118
|
+
## Naming
|
|
119
|
+
|
|
120
|
+
`view-transition-name` has to be on an element **before** the browser snapshots it, so it cannot be assigned while the change is being made. "Name only what changed" is therefore impossible, and you pick one of two strategies:
|
|
121
|
+
|
|
122
|
+
**`manual` (default)** — you bind it, and only what should morph gets a name:
|
|
123
|
+
|
|
124
|
+
```html
|
|
125
|
+
<template data-wcs="for: todos">
|
|
126
|
+
<li data-wcs="style.viewTransitionName: .cssName">{{ .title }}</li>
|
|
127
|
+
</template>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**`auto`** — `@wcstack/state` names the first element of every list row and `if` branch as it mounts, and adds a `view-transition-class` (`wcs-row` / `wcs-branch`) so CSS can address the whole group. The name follows the *content*, so a pooled row reused for another item keeps its name — which is what the DOM actually did.
|
|
131
|
+
|
|
132
|
+
Auto naming is capped at `naming-limit` (200 by default) because every named element becomes its own snapshot group and a few hundred of them make a transition visibly slow. Past the cap naming stops and says so once in the console. Big lists should use `manual` and name deliberately.
|
|
133
|
+
|
|
134
|
+
**`auto` is load-order sensitive.** Names are assigned as content mounts, so rows and branches that were already on the page when this tag upgraded never get one — nothing revisits them. Put this package's script tag **before** the `@wcstack/state` one so the arbiter is installed first; otherwise the first render participates in the root snapshot rather than morphing row by row. `manual` has no such ordering constraint, because the name is an ordinary binding.
|
|
135
|
+
|
|
136
|
+
## Binding it from state
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<wcs-view-transition
|
|
140
|
+
data-wcs="disabled: animationsOff; active: transitionRunning"
|
|
141
|
+
></wcs-view-transition>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`active` tracks whether a transition is running; `error` carries the last failure to start one (both observable). `disabled`, `mode`, `naming`, `types` and `participants` are writable inputs, and `skip` is available as a command token.
|
|
145
|
+
|
|
146
|
+
## Direct use (no DOM)
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
import { ViewTransitionCore } from "@wcstack/view-transition";
|
|
150
|
+
|
|
151
|
+
const core = new ViewTransitionCore();
|
|
152
|
+
core.naming = "auto";
|
|
153
|
+
core.install(); // become the page's arbiter
|
|
154
|
+
await core.run(() => { /* mutate the DOM */ });
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`install()` publishes the core on a well-known global symbol; `@wcstack/state` and `@wcstack/router` find it there without importing this package. The protocol is described in [docs/view-transition-design.md](https://github.com/wcstack/wcstack/blob/main/docs/view-transition-design.md) §4, and `getTransitionRunner` / `runTransition` / `TRANSITION_RUNNER_KEY` are exported for adopters who want to install an arbiter of their own.
|
|
158
|
+
|
|
159
|
+
## Demo
|
|
160
|
+
|
|
161
|
+
[`examples/list-transitions`](./examples/list-transitions/) — enter, leave and move in one page, with a checkbox that switches the arbiter off so the difference is visible. Buildless: open `index.html`.
|
|
162
|
+
|
|
163
|
+
## Browser support
|
|
164
|
+
|
|
165
|
+
Same-document View Transitions ship in Chromium 111+ and Safari 18+. Firefox does not have them yet; there, and anywhere else the API is missing, every change applies immediately with no animation and no error — the page keeps working, it just does not animate.
|
|
166
|
+
|
|
167
|
+
## License
|
|
168
|
+
|
|
169
|
+
MIT
|
package/dist/auto.min.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const t={tagNames:{viewTransition:"wcs-view-transition"}},e=Symbol.for("wcstack.transition-runner"),i=["router","state"];function s(t){return"string"==typeof t?t.split(/\s+/).filter(t=>""!==t):[...t]}class n extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"active",event:"wcs-view-transition:active-changed",semantics:"state"},{name:"error",event:"wcs-view-transition:error",semantics:"state"}],commands:[{name:"skip"}]};_target;_mode="latest";_naming="manual";_namingLimit=200;_reducedMotion="skip";_types=[];_disabled=!1;_participants=new Set(i);_active=!1;_error=null;_pending=null;_flushScheduled=!1;_batch=null;_transition=null;_queue=[];constructor(t){super(),this._target=t??this}get protocol(){return"wcs-transition-runner"}get version(){return 1}get naming(){return this._naming}set naming(t){this._naming="auto"===t?"auto":"manual"}get namingLimit(){return this._namingLimit}set namingLimit(t){this._namingLimit=Number.isFinite(t)&&t>=0?Math.floor(t):200}accepts(t){return this._participants.has(t)}install(){const t=globalThis,i=t[e];return null!=i&&i!==this?(console.warn("[@wcstack/view-transition] a transition runner is already installed; this element is inert. Use one <wcs-view-transition> per document."),!1):(t[e]=this,!0)}uninstall(){const t=globalThis;t[e]===this&&delete t[e]}get mode(){return this._mode}set mode(t){this._mode="queue"===t||"exhaust"===t?t:"latest"}get reducedMotion(){return this._reducedMotion}set reducedMotion(t){this._reducedMotion="animate"===t?"animate":"skip"}get types(){return this._types}set types(t){this._types=s(t)}get disabled(){return this._disabled}set disabled(t){this._disabled=!0===t}get participants(){return[...this._participants]}set participants(t){const e=s(t);this._participants=new Set(e.length>0?e:i)}get active(){return this._active}get error(){return this._error}skip(){this._transition?.skipTransition()}run(t,e){return this._canTransition()?new Promise((e,i)=>{const s={mutate:t,resolve:e,reject:i};null===this._batch?null===this._transition||"exhaust"!==this._mode?((this._pending??=[]).push(s),this._schedule()):this._settle(s):this._batch.push(s)}):this._applyNow(t)}dispose(){this.uninstall();const t=this._batch;this._batch=null;const e=[...t??[],...this._pending??[],...this._queue.flat()];this._pending=null,this._queue=[];for(const t of e)this._settle(t)}_canTransition(){if(this._disabled)return!1;const t=globalThis.document;return void 0!==t&&"function"==typeof t.startViewTransition&&(!0!==t.documentElement?.hasAttribute("data-wcs-server")&&(!0!==t.hidden&&("skip"!==this._reducedMotion||!function(){try{const t=globalThis.matchMedia;return"function"==typeof t&&!0===t.call(globalThis,"(prefers-reduced-motion: reduce)").matches}catch{return!1}}())))}_applyNow(t){try{t()}catch(t){return Promise.reject(t)}return Promise.resolve()}_settle(t){try{t.mutate(),t.resolve()}catch(e){t.reject(e)}}_schedule(){this._flushScheduled||(this._flushScheduled=!0,queueMicrotask(()=>this._flush()))}_flush(){this._flushScheduled=!1;const t=this._pending;if(this._pending=null,null!==t&&0!==t.length){if(null!==this._transition){if("queue"===this._mode)return void this._queue.push(t);if("exhaust"===this._mode){for(const e of t)this._settle(e);return}}this._start(t)}}_start(t){const e=globalThis.document,i=e.startViewTransition;this._batch=t;const s=()=>{const t=this._batch;if(this._batch=null,null!==t)for(const e of t)this._settle(e)};let n;try{n=this._types.length>0&&function(){try{const t=globalThis.ViewTransition;return void 0!==t&&"types"in t.prototype}catch{return!1}}()?i.call(e,{update:s,types:[...this._types]}):i.call(e,s)}catch(e){this._batch=null,this._setError((r=e)instanceof Error?r:new Error(String(r)));for(const e of t)this._settle(e);return}var r;this._transition=n,this._setError(null),this._setActive(!0);const a=()=>this._onFinished(n);n.finished.then(a,a),n.ready.then(void 0,()=>{}),n.updateCallbackDone.then(void 0,()=>{})}_onFinished(t){if(this._transition!==t)return;this._transition=null,this._setActive(!1);const e=this._queue.shift();void 0!==e&&this._start(e)}_setActive(t){this._active!==t&&(this._active=t,this._dispatch("wcs-view-transition:active-changed",t))}_setError(t){this._error!==t&&(this._error=t,this._dispatch("wcs-view-transition:error",t))}_dispatch(t,e){this._target.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!0,composed:!0}))}}function r(t,e){let i=Object.getPrototypeOf(t);for(;null!==i;){const t=Object.getOwnPropertyDescriptor(i,e);if(void 0!==t)return"function"==typeof t.get||"function"==typeof t.set;i=Object.getPrototypeOf(i)}return!1}class a extends HTMLElement{static observedAttributes=["mode","naming","naming-limit","reduced-motion","types","disabled","for"];static wcBindable={...n.wcBindable,inputs:[{name:"disabled",attribute:"disabled"},{name:"mode",attribute:"mode"},{name:"naming",attribute:"naming"},{name:"namingLimit",attribute:"naming-limit"},{name:"reducedMotion",attribute:"reduced-motion"},{name:"types",attribute:"types"},{name:"participants",attribute:"for"}]};_core;_internals=null;_installed=!1;constructor(){super(),this._core=new n(this),this._internals=this._initInternals(),this._wireStates({"wcs-view-transition:active-changed":t=>({active:!0===t}),"wcs-view-transition:error":t=>({error:null!=t})})}get core(){return this._core}get debugStates(){return this._internals?[...this._internals.states]:[]}_initInternals(){try{if("function"!=typeof this.attachInternals)return null;const t=this.attachInternals();return t.states.add("wcs-probe"),t.states.delete("wcs-probe"),t}catch{return null}}_wireStates(t){if(null===this._internals)return;const e=this._internals.states;for(const[i,s]of Object.entries(t))this.addEventListener(i,t=>{const i=this.hasAttribute("debug-states");for(const[n,r]of Object.entries(s(t.detail))){try{r?e.add(n):e.delete(n)}catch{}i&&this.toggleAttribute(`data-wcs-state-${n}`,r)}})}get disabled(){return this._core.disabled}set disabled(t){this._core.disabled=!0===t,this.toggleAttribute("disabled",!0===t)}get mode(){return this._core.mode}set mode(t){this._core.mode=t}get naming(){return this._core.naming}set naming(t){this._core.naming=t}get namingLimit(){return this._core.namingLimit}set namingLimit(t){this._core.namingLimit=Number(t)}get reducedMotion(){return this._core.reducedMotion}set reducedMotion(t){this._core.reducedMotion=t}get types(){return this._core.types}set types(t){this._core.types=t}get participants(){return this._core.participants}set participants(t){this._core.participants=t}get active(){return this._core.active}get error(){return this._core.error}skip(){this._core.skip()}connectedCallback(){!function(t){const e=t.constructor?.wcBindable,i=e?.inputs;if(void 0!==i)for(const e of i){const i=e.name;if(!Object.prototype.hasOwnProperty.call(t,i))continue;if(!r(t,i))continue;const s=t,n=s[i];delete s[i],s[i]=n}}(this),this._syncAllAttributes(),this._installed=this._core.install()}disconnectedCallback(){this._installed&&(this._core.dispose(),this._installed=!1)}attributeChangedCallback(t,e,i){e!==i&&this._applyAttribute(t,i)}_syncAllAttributes(){for(const t of a.observedAttributes){const e=this.getAttribute(t);null!==e&&this._applyAttribute(t,e)}}_applyAttribute(t,e){switch(t){case"mode":this._core.mode=e??"latest";break;case"naming":this._core.naming=e??"manual";break;case"naming-limit":this._core.namingLimit=null===e?Number.NaN:Number(e);break;case"reduced-motion":this._core.reducedMotion=e??"skip";break;case"types":this._core.types=e??"";break;case"disabled":this._core.disabled=null!==e;break;case"for":this._core.participants=e??""}}}var o;!function(e=customElements){e.get(t.tagNames.viewTransition)||e.define(t.tagNames.viewTransition,a)}(o);
|
|
2
|
+
//# sourceMappingURL=auto.min.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto.min.js","sources":["../src/config.ts","../src/protocol/transitionRunner.ts","../src/core/ViewTransitionCore.ts","../src/protocol/upgradeProperties.ts","../src/components/ViewTransition.ts","../src/bootstrapViewTransition.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\r\n\r\ninterface IInternalConfig extends IConfig {\r\n tagNames: {\r\n viewTransition: string;\r\n };\r\n}\r\n\r\nconst _config: IInternalConfig = {\r\n tagNames: {\r\n viewTransition: \"wcs-view-transition\",\r\n },\r\n};\r\n\r\nfunction deepFreeze<T>(obj: T): T {\r\n if (obj === null || typeof obj !== \"object\") return obj;\r\n Object.freeze(obj);\r\n for (const key of Object.keys(obj)) {\r\n deepFreeze((obj as Record<string, unknown>)[key]);\r\n }\r\n return obj;\r\n}\r\n\r\nfunction deepClone<T>(obj: T): T {\r\n if (obj === null || typeof obj !== \"object\") return obj;\r\n const clone: Record<string, unknown> = {};\r\n for (const key of Object.keys(obj)) {\r\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\r\n }\r\n return clone as T;\r\n}\r\n\r\nlet frozenConfig: IConfig | null = null;\r\n\r\n// Note: this is the live, mutable internal config. It is not part of the public\r\n// package exports (see exports.ts) — only `getConfig()` (a frozen snapshot) is\r\n// surfaced. `setConfig()` is applied internally via `bootstrapViewTransition()` and\r\n// is not re-exported from the package root, though a deep path import\r\n// (`.../src/config.js`) can still reach and mutate it. Accepted as-is for\r\n// cross-package consistency: every @wcstack package follows this same shape.\r\n// Use `getConfig()` for a frozen, safe read.\r\nexport const config: IConfig = _config as IConfig;\r\n\r\nexport function getConfig(): IConfig {\r\n if (!frozenConfig) {\r\n frozenConfig = deepFreeze(deepClone(_config));\r\n }\r\n return frozenConfig;\r\n}\r\n\r\nexport function setConfig(partialConfig: IWritableConfig): void {\r\n if (partialConfig.tagNames) {\r\n Object.assign(_config.tagNames, partialConfig.tagNames);\r\n }\r\n frozenConfig = null;\r\n}\r\n","// ===========================================================================\r\n// AUTO-GENERATED FILE - DO NOT EDIT.\r\n// Generated from /protocol/transition-runner.ts by scripts/sync-protocol-types.mjs.\r\n// Run `node scripts/sync-protocol-types.mjs` after editing the source.\r\n// ===========================================================================\r\n\r\n// transition-runner protocol — how a package that mutates the DOM hands that\r\n// mutation to whoever is arbitrating view transitions on the page.\r\n//\r\n// @wcstack/state and @wcstack/router must not depend on @wcstack/view-transition\r\n// (zero runtime dependencies, independently publishable), so the arbiter installs\r\n// itself on a well-known global symbol and the participants look it up lazily.\r\n// No arbiter installed means the mutation is invoked directly, synchronously —\r\n// byte-for-byte the behavior these packages had before the protocol existed.\r\n//\r\n// docs/view-transition-design.md §4 is the normative description.\r\n//\r\n// SINGLE SOURCE OF TRUTH: edit only this file (/protocol/transition-runner.ts), then run\r\n// `node scripts/sync-protocol-types.mjs` to regenerate the per-package copies\r\n// (packages/<pkg>/src/protocol/transitionRunner.ts). Those copies are generated — do not edit them.\r\n\r\n/**\r\n * Global key the arbiter installs itself under. `Symbol.for` so independently\r\n * loaded copies of this file (two CDN bundles on one page) still agree.\r\n */\r\nexport const TRANSITION_RUNNER_KEY = Symbol.for(\"wcstack.transition-runner\");\r\n\r\n/** Who is asking. Backs the arbiter's `for=` participant gate. */\r\nexport type TransitionSource = \"router\" | \"state\";\r\n\r\n/** `view-transition-name` assignment policy the arbiter declares for participants. */\r\nexport type TransitionNaming = \"manual\" | \"auto\";\r\n\r\nexport interface IWcsTransitionRunOptions {\r\n /** Participant id, for the `for=` gate and for diagnostics. */\r\n readonly source?: string;\r\n /** Transition types, where the environment supports `startViewTransition({ types })`. */\r\n readonly types?: readonly string[];\r\n}\r\n\r\nexport interface IWcsTransitionRunner {\r\n readonly protocol: \"wcs-transition-runner\";\r\n /** Integer protocol version. All versions >= 1 are participant-compatible. */\r\n readonly version: number;\r\n /** `\"auto\"` licenses a participant to assign `view-transition-name` itself. */\r\n readonly naming: TransitionNaming;\r\n /** Upper bound on auto-assigned names; past it a participant stops naming. */\r\n readonly namingLimit: number;\r\n /** Whether this participant animates at all. */\r\n accepts(source: string): boolean;\r\n /**\r\n * Invoke `mutate` inside a view transition when one is possible.\r\n *\r\n * Contract (docs/view-transition-design.md §4):\r\n * - `mutate` is invoked exactly once, whatever happens to the transition.\r\n * - The promise resolves once `mutate` has run — never waits for the animation.\r\n * - When no transition is started, `mutate` runs synchronously inside `run()`.\r\n * - It rejects only if `mutate` threw.\r\n */\r\n run(mutate: () => void, options?: IWcsTransitionRunOptions): Promise<void>;\r\n}\r\n\r\n/**\r\n * The installed arbiter, or null when there is none, it speaks a version this\r\n * reader does not, or it does not accept this participant.\r\n *\r\n * Looked up on every call rather than cached: the tag can be added, removed, or\r\n * reconfigured at any point in a page's life, and a stale cache would either\r\n * animate what the author just switched off or miss what they switched on.\r\n */\r\nexport function getTransitionRunner(source: string): IWcsTransitionRunner | null {\r\n const candidate = (globalThis as Record<symbol, unknown>)[TRANSITION_RUNNER_KEY] as\r\n | IWcsTransitionRunner\r\n | undefined;\r\n if (candidate === undefined || candidate === null) return null;\r\n if (candidate.protocol !== \"wcs-transition-runner\") return null;\r\n if (typeof candidate.version !== \"number\" || candidate.version < 1) return null;\r\n if (typeof candidate.run !== \"function\") return null;\r\n if (typeof candidate.accepts !== \"function\" || !candidate.accepts(source)) return null;\r\n return candidate;\r\n}\r\n\r\n/**\r\n * Run `mutate` under the installed arbiter, or directly when there is none.\r\n *\r\n * Returns `undefined` in the no-arbiter case instead of a resolved promise: the\r\n * state drain calls this on every batch, and awaiting is a caller's choice, not\r\n * an allocation the common path should pay for. `await` accepts both.\r\n */\r\nexport function runTransition(\r\n source: string,\r\n mutate: () => void,\r\n types?: readonly string[],\r\n): Promise<void> | undefined {\r\n const runner = getTransitionRunner(source);\r\n if (runner === null) {\r\n mutate();\r\n return undefined;\r\n }\r\n return runner.run(mutate, { source, types });\r\n}\r\n","import { IWcBindable, ReducedMotionPolicy, TransitionMode } from \"../types.js\";\r\nimport {\r\n IWcsTransitionRunOptions,\r\n IWcsTransitionRunner,\r\n TRANSITION_RUNNER_KEY,\r\n TransitionNaming,\r\n} from \"../protocol/transitionRunner.js\";\r\n\r\n/**\r\n * Minimal structural views of the View Transition API. Declared locally rather\r\n * than relying on `lib.dom`'s (still moving) definitions, so the package\r\n * type-checks identically across TypeScript lib versions and an environment\r\n * without the API is a value check, never a type error.\r\n */\r\ninterface IViewTransitionLike {\r\n readonly updateCallbackDone: Promise<void>;\r\n readonly finished: Promise<void>;\r\n readonly ready: Promise<void>;\r\n skipTransition(): void;\r\n}\r\n\r\ninterface IStartViewTransitionOptions {\r\n update: () => void;\r\n types?: string[];\r\n}\r\n\r\ntype StartViewTransition = (\r\n callbackOrOptions: (() => void) | IStartViewTransitionOptions,\r\n) => IViewTransitionLike;\r\n\r\ninterface IPendingEntry {\r\n readonly mutate: () => void;\r\n readonly resolve: () => void;\r\n readonly reject: (reason: unknown) => void;\r\n}\r\n\r\nconst DEFAULT_NAMING_LIMIT = 200;\r\nconst DEFAULT_PARTICIPANTS: readonly string[] = [\"router\", \"state\"];\r\n\r\nfunction toError(value: unknown): Error {\r\n return value instanceof Error ? value : new Error(String(value));\r\n}\r\n\r\n/**\r\n * Accept both the array form and the space-separated string an attribute (or a\r\n * `data-wcs` binding) produces. The Core is a public export, so `core.types = \"a b\"`\r\n * is a call an adopter can make — and without normalizing it here that string\r\n * would degrade into single characters (`new Set(\"router\")` contains no\r\n * `\"router\"`, so `accepts()` would answer false for every participant).\r\n */\r\nfunction toStringList(value: readonly string[] | string): string[] {\r\n if (typeof value === \"string\") {\r\n return value.split(/\\s+/).filter((token) => token !== \"\");\r\n }\r\n return [...value];\r\n}\r\n\r\nfunction prefersReducedMotion(): boolean {\r\n // never-throw: matchMedia is absent in happy-dom and in non-browser hosts.\r\n try {\r\n const mm = (globalThis as { matchMedia?: (q: string) => { matches: boolean } }).matchMedia;\r\n if (typeof mm !== \"function\") return false;\r\n return mm.call(globalThis, \"(prefers-reduced-motion: reduce)\").matches === true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Whether `startViewTransition({ update, types })` is understood. Detected on\r\n * `ViewTransition.prototype`, because passing the object form to an\r\n * implementation that only accepts a callback would throw at the call site —\r\n * after the browser has already decided it has no update callback to run.\r\n */\r\nfunction supportsTypes(): boolean {\r\n try {\r\n const ctor = (globalThis as { ViewTransition?: { prototype: object } }).ViewTransition;\r\n return ctor !== undefined && \"types\" in ctor.prototype;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Headless view-transition arbiter — the single place on a page that decides\r\n * whether a DOM mutation animates, and what happens when two of them collide.\r\n *\r\n * It is not an I/O node: nothing is read from a device and there is no data to\r\n * bind. It is a *policy* node. Participants (`@wcstack/router`, `@wcstack/state`)\r\n * never import it; they find it through the transition-runner protocol on a\r\n * well-known global symbol and hand it a mutation to run\r\n * (docs/view-transition-design.md §4).\r\n *\r\n * The one invariant everything else is subordinate to: **a mutation handed to\r\n * `run()` is applied exactly once**, whatever is decided about animating it. An\r\n * unsupported browser, a hidden tab, reduced motion, a colliding transition and a\r\n * `startViewTransition` that throws all end in the mutation running — the page\r\n * must never be left showing stale DOM because an animation could not be played.\r\n */\r\nexport class ViewTransitionCore extends EventTarget {\r\n static wcBindable: IWcBindable = {\r\n protocol: \"wc-bindable\",\r\n version: 1,\r\n properties: [\r\n { name: \"active\", event: \"wcs-view-transition:active-changed\", semantics: \"state\" },\r\n { name: \"error\", event: \"wcs-view-transition:error\", semantics: \"state\" },\r\n ],\r\n commands: [\r\n { name: \"skip\" },\r\n ],\r\n };\r\n\r\n private _target: EventTarget;\r\n\r\n private _mode: TransitionMode = \"latest\";\r\n private _naming: TransitionNaming = \"manual\";\r\n private _namingLimit: number = DEFAULT_NAMING_LIMIT;\r\n private _reducedMotion: ReducedMotionPolicy = \"skip\";\r\n private _types: string[] = [];\r\n private _disabled: boolean = false;\r\n private _participants: Set<string> = new Set(DEFAULT_PARTICIPANTS);\r\n\r\n private _active: boolean = false;\r\n private _error: Error | null = null;\r\n\r\n /** Requests waiting for the microtask flush that starts a transition. */\r\n private _pending: IPendingEntry[] | null = null;\r\n private _flushScheduled: boolean = false;\r\n /**\r\n * The batch handed to the running transition while its update callback has not\r\n * fired yet. Non-null means \"capturing\": a request arriving now still joins this\r\n * batch, which is both the coalescing window and the only ordering guarantee\r\n * that keeps a later `exhaust`/`latest` request from applying ahead of it.\r\n */\r\n private _batch: IPendingEntry[] | null = null;\r\n private _transition: IViewTransitionLike | null = null;\r\n private _queue: IPendingEntry[][] = [];\r\n\r\n constructor(target?: EventTarget) {\r\n super();\r\n this._target = target ?? this;\r\n }\r\n\r\n // --- transition-runner protocol surface ---\r\n\r\n get protocol(): \"wcs-transition-runner\" {\r\n return \"wcs-transition-runner\";\r\n }\r\n\r\n get version(): number {\r\n return 1;\r\n }\r\n\r\n get naming(): TransitionNaming {\r\n return this._naming;\r\n }\r\n\r\n set naming(value: TransitionNaming) {\r\n this._naming = value === \"auto\" ? \"auto\" : \"manual\";\r\n }\r\n\r\n get namingLimit(): number {\r\n return this._namingLimit;\r\n }\r\n\r\n set namingLimit(value: number) {\r\n this._namingLimit = Number.isFinite(value) && value >= 0 ? Math.floor(value) : DEFAULT_NAMING_LIMIT;\r\n }\r\n\r\n accepts(source: string): boolean {\r\n return this._participants.has(source);\r\n }\r\n\r\n /**\r\n * Install this core as the page's arbiter. Returns false (and warns) when\r\n * another one already holds the slot — two arbiters would each think they own\r\n * the exclusion, which is precisely the thing an arbiter exists to prevent.\r\n */\r\n install(): boolean {\r\n const slot = globalThis as Record<symbol, unknown>;\r\n const current = slot[TRANSITION_RUNNER_KEY];\r\n if (current !== undefined && current !== null && current !== this) {\r\n console.warn(\r\n \"[@wcstack/view-transition] a transition runner is already installed; \" +\r\n \"this element is inert. Use one <wcs-view-transition> per document.\",\r\n );\r\n return false;\r\n }\r\n slot[TRANSITION_RUNNER_KEY] = this as unknown as IWcsTransitionRunner;\r\n return true;\r\n }\r\n\r\n /** Release the arbiter slot, but only if it is still ours. */\r\n uninstall(): void {\r\n const slot = globalThis as Record<symbol, unknown>;\r\n if (slot[TRANSITION_RUNNER_KEY] === (this as unknown)) {\r\n delete slot[TRANSITION_RUNNER_KEY];\r\n }\r\n }\r\n\r\n // --- configuration ---\r\n\r\n get mode(): TransitionMode {\r\n return this._mode;\r\n }\r\n\r\n set mode(value: TransitionMode) {\r\n this._mode = value === \"queue\" || value === \"exhaust\" ? value : \"latest\";\r\n }\r\n\r\n get reducedMotion(): ReducedMotionPolicy {\r\n return this._reducedMotion;\r\n }\r\n\r\n set reducedMotion(value: ReducedMotionPolicy) {\r\n this._reducedMotion = value === \"animate\" ? \"animate\" : \"skip\";\r\n }\r\n\r\n get types(): readonly string[] {\r\n return this._types;\r\n }\r\n\r\n set types(value: readonly string[] | string) {\r\n this._types = toStringList(value);\r\n }\r\n\r\n get disabled(): boolean {\r\n return this._disabled;\r\n }\r\n\r\n set disabled(value: boolean) {\r\n this._disabled = value === true;\r\n }\r\n\r\n get participants(): readonly string[] {\r\n return [...this._participants];\r\n }\r\n\r\n set participants(value: readonly string[] | string) {\r\n const list = toStringList(value);\r\n this._participants = new Set(list.length > 0 ? list : DEFAULT_PARTICIPANTS);\r\n }\r\n\r\n // --- observable outputs ---\r\n\r\n get active(): boolean {\r\n return this._active;\r\n }\r\n\r\n get error(): Error | null {\r\n return this._error;\r\n }\r\n\r\n // --- commands ---\r\n\r\n /**\r\n * Finish the running transition now. Per spec the update callback still runs if\r\n * it has not yet, so skipping loses the animation and never the DOM update.\r\n */\r\n skip(): void {\r\n this._transition?.skipTransition();\r\n }\r\n\r\n // --- the protocol entry point ---\r\n\r\n run(mutate: () => void, _options?: IWcsTransitionRunOptions): Promise<void> {\r\n if (!this._canTransition()) {\r\n return this._applyNow(mutate);\r\n }\r\n return new Promise<void>((resolve, reject) => {\r\n const entry: IPendingEntry = { mutate, resolve, reject };\r\n // Capturing: the running transition has not called its update callback yet,\r\n // so this mutation can still ride along — and must, or it would be applied\r\n // before mutations that were requested earlier.\r\n if (this._batch !== null) {\r\n this._batch.push(entry);\r\n return;\r\n }\r\n if (this._transition !== null && this._mode === \"exhaust\") {\r\n this._settle(entry);\r\n return;\r\n }\r\n (this._pending ??= []).push(entry);\r\n this._schedule();\r\n });\r\n }\r\n\r\n dispose(): void {\r\n this.uninstall();\r\n // Anything still unapplied belongs to a page that is going away; apply it so\r\n // the DOM does not stay behind the state that asked for the change.\r\n //\r\n // Order is request order, and that is why the capturing batch has to be taken\r\n // first: its mutations were requested *before* everything in _pending and\r\n // _queue, but they are the ones still waiting on a frame. Settling only the\r\n // later two would apply them out of order. Clearing _batch also turns the\r\n // running transition's update callback into a no-op, which is what keeps\r\n // \"applied exactly once\" true across a dispose.\r\n const capturing = this._batch;\r\n this._batch = null;\r\n const abandoned = [...(capturing ?? []), ...(this._pending ?? []), ...this._queue.flat()];\r\n this._pending = null;\r\n this._queue = [];\r\n for (const entry of abandoned) {\r\n this._settle(entry);\r\n }\r\n }\r\n\r\n // --- internals ---\r\n\r\n private _canTransition(): boolean {\r\n if (this._disabled) return false;\r\n const doc = (globalThis as { document?: Document }).document;\r\n if (doc === undefined || typeof (doc as { startViewTransition?: unknown }).startViewTransition !== \"function\") {\r\n return false;\r\n }\r\n // SSR: no transition is started while rendering on the server (G5). The gate\r\n // lives here rather than in each participant because the protocol is public —\r\n // a third-party participant has no reason to know wcstack's SSR marker, and\r\n // the arbiter is the one place that owns the policy. `@wcstack/server` sets\r\n // the attribute on its own document and it never reaches the client HTML.\r\n if (doc.documentElement?.hasAttribute(\"data-wcs-server\") === true) return false;\r\n // A hidden tab gets no rendering opportunities, so the update callback would\r\n // not run until the page is looked at again — the DOM would silently freeze\r\n // for as long as the tab stays in the background. Apply straight through.\r\n if (doc.hidden === true) return false;\r\n if (this._reducedMotion === \"skip\" && prefersReducedMotion()) return false;\r\n return true;\r\n }\r\n\r\n private _applyNow(mutate: () => void): Promise<void> {\r\n try {\r\n mutate();\r\n } catch (error) {\r\n return Promise.reject(error);\r\n }\r\n return Promise.resolve();\r\n }\r\n\r\n private _settle(entry: IPendingEntry): void {\r\n try {\r\n entry.mutate();\r\n entry.resolve();\r\n } catch (error) {\r\n entry.reject(error);\r\n }\r\n }\r\n\r\n private _schedule(): void {\r\n if (this._flushScheduled) return;\r\n this._flushScheduled = true;\r\n queueMicrotask(() => this._flush());\r\n }\r\n\r\n private _flush(): void {\r\n this._flushScheduled = false;\r\n const batch = this._pending;\r\n this._pending = null;\r\n if (batch === null || batch.length === 0) return;\r\n if (this._transition !== null) {\r\n if (this._mode === \"queue\") {\r\n this._queue.push(batch);\r\n return;\r\n }\r\n if (this._mode === \"exhaust\") {\r\n for (const entry of batch) {\r\n this._settle(entry);\r\n }\r\n return;\r\n }\r\n // \"latest\": starting a new transition skips the running one, and the\r\n // running one is past its update callback (a capturing batch is joined in\r\n // run(), never reaching here), so ordering holds.\r\n }\r\n this._start(batch);\r\n }\r\n\r\n private _start(batch: IPendingEntry[]): void {\r\n const doc = (globalThis as unknown as { document: Document }).document;\r\n const start = (doc as unknown as { startViewTransition: StartViewTransition }).startViewTransition;\r\n this._batch = batch;\r\n const update = (): void => {\r\n const running = this._batch;\r\n this._batch = null;\r\n if (running === null) return;\r\n for (const entry of running) {\r\n this._settle(entry);\r\n }\r\n };\r\n let transition: IViewTransitionLike;\r\n try {\r\n transition = this._types.length > 0 && supportsTypes()\r\n ? start.call(doc, { update, types: [...this._types] })\r\n : start.call(doc, update);\r\n } catch (error) {\r\n // Could not even start: apply the mutations rather than lose them.\r\n this._batch = null;\r\n this._setError(toError(error));\r\n for (const entry of batch) {\r\n this._settle(entry);\r\n }\r\n return;\r\n }\r\n this._transition = transition;\r\n this._setError(null);\r\n this._setActive(true);\r\n // `finished` rejects when the update callback throws — it cannot here, since\r\n // _settle catches per entry — and `ready` rejects whenever the transition is\r\n // skipped, which is routine. Both are attached defensively so a routine skip\r\n // never surfaces as an unhandled rejection.\r\n const done = (): void => this._onFinished(transition);\r\n transition.finished.then(done, done);\r\n transition.ready.then(undefined, () => { /* skipped: not an error */ });\r\n transition.updateCallbackDone.then(undefined, () => { /* settled per entry */ });\r\n }\r\n\r\n private _onFinished(transition: IViewTransitionLike): void {\r\n // A superseded transition (\"latest\") still settles; only the current one owns\r\n // the active flag and the queue.\r\n if (this._transition !== transition) return;\r\n this._transition = null;\r\n this._setActive(false);\r\n const next = this._queue.shift();\r\n if (next !== undefined) {\r\n this._start(next);\r\n }\r\n }\r\n\r\n private _setActive(value: boolean): void {\r\n if (this._active === value) return;\r\n this._active = value;\r\n this._dispatch(\"wcs-view-transition:active-changed\", value);\r\n }\r\n\r\n private _setError(error: Error | null): void {\r\n if (this._error === error) return;\r\n this._error = error;\r\n this._dispatch(\"wcs-view-transition:error\", error);\r\n }\r\n\r\n private _dispatch(type: string, detail: unknown): void {\r\n this._target.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true }));\r\n }\r\n}\r\n","// ===========================================================================\r\n// AUTO-GENERATED FILE - DO NOT EDIT.\r\n// Generated from /protocol/upgrade-properties.ts by scripts/sync-protocol-types.mjs.\r\n// Run `node scripts/sync-protocol-types.mjs` after editing the source.\r\n// ===========================================================================\r\n\r\n// custom element の property upgrade — `static wcBindable.inputs` に宣言した入力のうち、\r\n// 要素が upgrade される前に代入された own データプロパティを取り込み直す。\r\n//\r\n// なぜ必要か:\r\n// 未定義タグの要素は素の HTMLElement なので、`el.url = \"...\"` は own データプロパティを作る。\r\n// upgrade 後にクラスの accessor が prototype へ入っても own プロパティが優先されるため、\r\n// setter は二度と呼ばれず、値は要素へ届かないまま消える(エラーも警告も出ない)。\r\n// 常にプロパティ代入を行う framework(Angular の `[prop]`、Lit の `.prop=`、\r\n// Solid の `prop:`、Vue の `.prop` 修飾子)× 遅延定義(autoloader / CDN / code-split)で\r\n// 常態的に起きる。docs/architecture-hardening/13-framework-adapter-binding-constraints.md §1.2。\r\n//\r\n// 安全側の判定:\r\n// own プロパティがあっても、prototype チェーンに accessor が無ければ「シャドウ」ではなく\r\n// その own プロパティ自体が正規の格納先なので触らない(public class field を壊さない)。\r\n//\r\n// SINGLE SOURCE OF TRUTH: edit only this file (/protocol/upgrade-properties.ts), then run\r\n// `node scripts/sync-protocol-types.mjs` to regenerate the per-package copies\r\n// (packages/<pkg>/src/protocol/upgradeProperties.ts). Those copies are generated — do not edit them.\r\nimport { IWcBindable } from \"./wcBindable.js\";\r\n\r\nfunction hasAccessorOnPrototype(target: object, name: string): boolean {\r\n let proto = Object.getPrototypeOf(target);\r\n while (proto !== null) {\r\n const descriptor = Object.getOwnPropertyDescriptor(proto, name);\r\n if (descriptor !== undefined) {\r\n return typeof descriptor.get === \"function\" || typeof descriptor.set === \"function\";\r\n }\r\n proto = Object.getPrototypeOf(proto);\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * `connectedCallback` の先頭で呼ぶ。宣言済み input のうち upgrade 前の代入で\r\n * accessor をシャドウしている own プロパティを、delete → 再代入で setter に通し直す。\r\n *\r\n * - 冪等: 再代入は accessor を通るので own プロパティは残らず、2 回目以降は no-op。\r\n * - 宣言に `inputs` が無い要素、`wcBindable` を持たない要素では何もしない。\r\n * - 値の意味は変えない。今まで捨てられていた代入が届くようになる一方向の変化。\r\n */\r\nexport function upgradeProperties(element: object): void {\r\n const declaration = (element as { constructor?: { wcBindable?: IWcBindable } }).constructor?.wcBindable;\r\n const inputs = declaration?.inputs;\r\n if (inputs === undefined) return;\r\n for (const input of inputs) {\r\n const name = input.name;\r\n if (!Object.prototype.hasOwnProperty.call(element, name)) continue;\r\n if (!hasAccessorOnPrototype(element, name)) continue;\r\n const record = element as Record<string, unknown>;\r\n const value = record[name];\r\n delete record[name];\r\n record[name] = value;\r\n }\r\n}\r\n","import { ViewTransitionCore } from \"../core/ViewTransitionCore.js\";\r\nimport { upgradeProperties } from \"../protocol/upgradeProperties.js\";\r\nimport { TransitionNaming } from \"../protocol/transitionRunner.js\";\r\nimport { IWcBindable, ReducedMotionPolicy, TransitionMode } from \"../types.js\";\r\n\r\n/**\r\n * `<wcs-view-transition>` — the page's view-transition policy node.\r\n *\r\n * It renders nothing and binds no data. It declares *how* the DOM changes that\r\n * `@wcstack/router` and `@wcstack/state` make should animate, and it is the single\r\n * arbiter that decides what happens when two of those changes collide. Dropping\r\n * the tag on a page is the opt-in; removing it restores the framework's original\r\n * synchronous behavior exactly (docs/view-transition-design.md §3, G1/G2).\r\n *\r\n * ```html\r\n * <wcs-view-transition for=\"router\" mode=\"latest\"></wcs-view-transition>\r\n * ```\r\n *\r\n * The animation itself is written in CSS against `::view-transition-*`. This tag\r\n * starts and arbitrates transitions; it never describes one.\r\n */\r\nexport class WcsViewTransition extends HTMLElement {\r\n static observedAttributes = [\r\n \"mode\", \"naming\", \"naming-limit\", \"reduced-motion\", \"types\", \"disabled\", \"for\",\r\n ];\r\n\r\n // `properties` and `commands` come from the Core through the spread, so a\r\n // member added there cannot be missed here. Only `inputs` — the attribute\r\n // surface, which exists on the element and not on the Core — is declared.\r\n static wcBindable: IWcBindable = {\r\n ...ViewTransitionCore.wcBindable,\r\n inputs: [\r\n { name: \"disabled\", attribute: \"disabled\" },\r\n { name: \"mode\", attribute: \"mode\" },\r\n { name: \"naming\", attribute: \"naming\" },\r\n { name: \"namingLimit\", attribute: \"naming-limit\" },\r\n { name: \"reducedMotion\", attribute: \"reduced-motion\" },\r\n { name: \"types\", attribute: \"types\" },\r\n { name: \"participants\", attribute: \"for\" },\r\n ],\r\n };\r\n\r\n private _core: ViewTransitionCore;\r\n private _internals: ElementInternals | null = null;\r\n private _installed: boolean = false;\r\n\r\n constructor() {\r\n super();\r\n this._core = new ViewTransitionCore(this);\r\n this._internals = this._initInternals();\r\n this._wireStates({\r\n \"wcs-view-transition:active-changed\": (d) => ({ active: d === true }),\r\n \"wcs-view-transition:error\": (d) => ({ error: d != null }),\r\n });\r\n }\r\n\r\n /** The headless arbiter, for direct (non-DOM) use. */\r\n get core(): ViewTransitionCore {\r\n return this._core;\r\n }\r\n\r\n // CSS state reflection (:state()) — debug-only snapshot getter. NOT part of\r\n // wc-bindable. MUST NOT return the live CustomStateSet.\r\n get debugStates(): string[] {\r\n return this._internals ? [...this._internals.states] : [];\r\n }\r\n\r\n private _initInternals(): ElementInternals | null {\r\n // never-throw: attachInternals is absent in happy-dom / older environments,\r\n // and pre-125 Chromium rejects non-dashed state names (probed and discarded).\r\n try {\r\n if (typeof this.attachInternals !== \"function\") return null;\r\n const internals = this.attachInternals();\r\n internals.states.add(\"wcs-probe\");\r\n internals.states.delete(\"wcs-probe\");\r\n return internals;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n private _wireStates(map: Record<string, (detail: any) => Record<string, boolean>>): void {\r\n if (this._internals === null) return;\r\n const states = this._internals.states;\r\n for (const [event, toStates] of Object.entries(map)) {\r\n this.addEventListener(event, (e) => {\r\n const debug = this.hasAttribute(\"debug-states\");\r\n for (const [name, on] of Object.entries(toStates((e as CustomEvent).detail))) {\r\n try {\r\n if (on) { states.add(name); } else { states.delete(name); }\r\n } catch { /* never-throw */ }\r\n if (debug) this.toggleAttribute(`data-wcs-state-${name}`, on);\r\n }\r\n });\r\n }\r\n }\r\n\r\n // --- inputs ---\r\n\r\n get disabled(): boolean {\r\n return this._core.disabled;\r\n }\r\n\r\n set disabled(value: boolean) {\r\n this._core.disabled = value === true;\r\n this.toggleAttribute(\"disabled\", value === true);\r\n }\r\n\r\n get mode(): TransitionMode {\r\n return this._core.mode;\r\n }\r\n\r\n set mode(value: TransitionMode) {\r\n this._core.mode = value;\r\n }\r\n\r\n get naming(): TransitionNaming {\r\n return this._core.naming;\r\n }\r\n\r\n set naming(value: TransitionNaming) {\r\n this._core.naming = value;\r\n }\r\n\r\n get namingLimit(): number {\r\n return this._core.namingLimit;\r\n }\r\n\r\n set namingLimit(value: number) {\r\n this._core.namingLimit = Number(value);\r\n }\r\n\r\n get reducedMotion(): ReducedMotionPolicy {\r\n return this._core.reducedMotion;\r\n }\r\n\r\n set reducedMotion(value: ReducedMotionPolicy) {\r\n this._core.reducedMotion = value;\r\n }\r\n\r\n get types(): readonly string[] {\r\n return this._core.types;\r\n }\r\n\r\n set types(value: readonly string[] | string) {\r\n this._core.types = value;\r\n }\r\n\r\n get participants(): readonly string[] {\r\n return this._core.participants;\r\n }\r\n\r\n set participants(value: readonly string[] | string) {\r\n this._core.participants = value;\r\n }\r\n\r\n // --- observable outputs ---\r\n\r\n get active(): boolean {\r\n return this._core.active;\r\n }\r\n\r\n get error(): Error | null {\r\n return this._core.error;\r\n }\r\n\r\n // --- commands ---\r\n\r\n skip(): void {\r\n this._core.skip();\r\n }\r\n\r\n // --- lifecycle ---\r\n\r\n connectedCallback(): void {\r\n upgradeProperties(this);\r\n this._syncAllAttributes();\r\n this._installed = this._core.install();\r\n }\r\n\r\n disconnectedCallback(): void {\r\n if (this._installed) {\r\n // dispose(), not uninstall(): a mutation already handed to this arbiter must\r\n // still be applied even though the page just removed its policy tag.\r\n this._core.dispose();\r\n this._installed = false;\r\n }\r\n }\r\n\r\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\r\n if (oldValue === newValue) return;\r\n this._applyAttribute(name, newValue);\r\n }\r\n\r\n /**\r\n * Apply the attributes present at connect time. Absent ones are deliberately\r\n * skipped rather than applied as null: a property assigned before upgrade\r\n * (Angular's `[prop]`, Lit's `.prop=`, or plain `el.mode = ...`) has just been\r\n * replayed through the setter by `upgradeProperties`, and re-applying a missing\r\n * attribute would immediately reset it to the default. Removing an attribute\r\n * still resets, via `attributeChangedCallback`.\r\n */\r\n private _syncAllAttributes(): void {\r\n for (const name of WcsViewTransition.observedAttributes) {\r\n const value = this.getAttribute(name);\r\n if (value === null) continue;\r\n this._applyAttribute(name, value);\r\n }\r\n }\r\n\r\n private _applyAttribute(name: string, value: string | null): void {\r\n switch (name) {\r\n case \"mode\":\r\n this._core.mode = (value ?? \"latest\") as TransitionMode;\r\n break;\r\n case \"naming\":\r\n this._core.naming = (value ?? \"manual\") as TransitionNaming;\r\n break;\r\n case \"naming-limit\":\r\n this._core.namingLimit = value === null ? Number.NaN : Number(value);\r\n break;\r\n case \"reduced-motion\":\r\n this._core.reducedMotion = (value ?? \"skip\") as ReducedMotionPolicy;\r\n break;\r\n case \"types\":\r\n this._core.types = value ?? \"\";\r\n break;\r\n case \"disabled\":\r\n this._core.disabled = value !== null;\r\n break;\r\n case \"for\":\r\n this._core.participants = value ?? \"\";\r\n break;\r\n }\r\n }\r\n}\r\n","import { setConfig } from \"./config.js\";\r\nimport { registerComponents } from \"./registerComponents.js\";\r\nimport { IWritableConfig } from \"./types.js\";\r\n\r\nexport function bootstrapViewTransition(userConfig?: IWritableConfig, registry?: CustomElementRegistry): void {\r\n if (userConfig) {\r\n setConfig(userConfig);\r\n }\r\n registerComponents(registry);\r\n}\r\n","import { WcsViewTransition } from \"./components/ViewTransition.js\";\r\nimport { config } from \"./config.js\";\r\n\r\n/**\r\n * Register this package's tags. Pass a scoped `CustomElementRegistry` to define\r\n * them for a single shadow tree -- scoped registries do not inherit the global\r\n * one, so a tree using one needs its own definitions.\r\n */\r\nexport function registerComponents(registry: CustomElementRegistry = customElements): void {\r\n if (!registry.get(config.tagNames.viewTransition)) {\r\n registry.define(config.tagNames.viewTransition, WcsViewTransition);\r\n }\r\n}\r\n"],"names":["config","tagNames","viewTransition","TRANSITION_RUNNER_KEY","Symbol","for","DEFAULT_PARTICIPANTS","toStringList","value","split","filter","token","ViewTransitionCore","EventTarget","static","protocol","version","properties","name","event","semantics","commands","_target","_mode","_naming","_namingLimit","_reducedMotion","_types","_disabled","_participants","Set","_active","_error","_pending","_flushScheduled","_batch","_transition","_queue","constructor","target","super","this","naming","namingLimit","Number","isFinite","Math","floor","accepts","source","has","install","slot","globalThis","current","console","warn","uninstall","mode","reducedMotion","types","disabled","participants","list","length","active","error","skip","skipTransition","run","mutate","_options","_canTransition","Promise","resolve","reject","entry","push","_schedule","_settle","_applyNow","dispose","capturing","abandoned","flat","doc","document","undefined","startViewTransition","documentElement","hasAttribute","hidden","mm","matchMedia","call","matches","prefersReducedMotion","queueMicrotask","_flush","batch","_start","start","update","running","transition","ctor","ViewTransition","prototype","supportsTypes","_setError","Error","String","_setActive","done","_onFinished","finished","then","ready","updateCallbackDone","next","shift","_dispatch","type","detail","dispatchEvent","CustomEvent","bubbles","composed","hasAccessorOnPrototype","proto","Object","getPrototypeOf","descriptor","getOwnPropertyDescriptor","get","set","WcsViewTransition","HTMLElement","wcBindable","inputs","attribute","_core","_internals","_installed","_initInternals","_wireStates","d","core","debugStates","states","attachInternals","internals","add","delete","map","toStates","entries","addEventListener","e","debug","on","toggleAttribute","connectedCallback","element","declaration","input","hasOwnProperty","record","upgradeProperties","_syncAllAttributes","disconnectedCallback","attributeChangedCallback","oldValue","newValue","_applyAttribute","observedAttributes","getAttribute","NaN","registry","customElements","define","registerComponents"],"mappings":"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"}
|