@wcstack/fullscreen 1.16.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 ADDED
@@ -0,0 +1,131 @@
1
+ # @wcstack/fullscreen
2
+
3
+ `@wcstack/fullscreen` は wcstack エコシステム向けのヘッドレスな Fullscreen API コンポーネントです。
4
+
5
+ 視覚的な UI ウィジェットではありません。
6
+ **制御ノード**です。大半の wcstack IO ノードは自分自身を操作対象としますが、`<wcs-fullscreen>` は `@wcstack/intersection` が自分自身ではなく参照先の要素を観測するのと同じように、`target` で指し示した**参照先の要素**に対して `requestFullscreen()` / `exitFullscreen()` を実行します。
7
+
8
+ `@wcstack/state` と組み合わせると、`<wcs-fullscreen>` はパス契約で直接バインドできます:
9
+
10
+ - **入力サーフェス**: `target`(操作対象の要素。下記参照)
11
+ - **出力 state サーフェス**: `active`、`error`
12
+ - **コマンド**: `requestFullscreen()`、`exitFullscreen()`
13
+
14
+ `@wcstack/fullscreen` は [CSBC](https://github.com/csbc-dev/arch/blob/main/README.md)(Core / Shell / Binding Contract)アーキテクチャに従います:
15
+
16
+ - **Core**(`FullscreenCore`)が Fullscreen API を操作し、`document` の `fullscreenchange` イベントを追従
17
+ - **Shell**(`<wcs-fullscreen target="...">`)が `target` を DOM 要素へ解決し、Core の state を DOM ライフサイクルに接続
18
+ - **Binding Contract**(`static wcBindable`)が観測可能な `active` プロパティと `requestFullscreen`/`exitFullscreen` コマンドを宣言
19
+
20
+ ## なぜ存在するか — 操作対象は「タグ自身」ではなく「参照先」
21
+
22
+ `Element.requestFullscreen()` はfullscreen化したい要素(画像・動画・カードUIなど)に対するメソッドであり、`<wcs-fullscreen>` 自身に対するものではありません。そのため本タグは非表示の制御要素(`display` はターゲット解決モードに応じて決まります — 下表参照)として存在し、`<wcs-intersect>` と全く同じ規則で `target` 属性を介して別の要素を指し示します:
23
+
24
+ | `target` | 操作対象 | display | 典型的な用途 |
25
+ | --------------------- | -------------------------- | ------------ | ---------------------------- |
26
+ | 省略 | 最初の子要素 | `contents` | ギャラリー画像/動画をラップ |
27
+ | `"#hero"` / セレクタ | マッチした要素 | `none` | 離れた要素を指し示す |
28
+ | `"self"` | 自分自身 | `block` | ラッパー自体をfullscreen化 |
29
+
30
+ ## インストール
31
+
32
+ ```bash
33
+ npm install @wcstack/fullscreen
34
+ ```
35
+
36
+ ## クイックスタート
37
+
38
+ ### 1. ボタンクリックで画像をfullscreen化
39
+
40
+ ```html
41
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
42
+ <script type="module" src="https://esm.run/@wcstack/fullscreen/auto"></script>
43
+
44
+ <wcs-state>
45
+ <script type="module">
46
+ export default {
47
+ $commandTokens: ["goFullscreen"],
48
+ };
49
+ </script>
50
+ </wcs-state>
51
+
52
+ <wcs-fullscreen target="#hero" data-wcs="command.requestFullscreen: $command.goFullscreen"></wcs-fullscreen>
53
+ <img id="hero" src="/photo.jpg">
54
+ <button data-wcs="onclick: $command.goFullscreen">Fullscreen</button>
55
+ ```
56
+
57
+ ボタンは `<wcs-fullscreen>` を直接操作しません。クリックで `goFullscreen` コマンドトークンをemitし、`<wcs-fullscreen>` は `command.requestFullscreen: $command.goFullscreen` でそのトークンを購読します([command-tokenプロトコル](../state/) — コマンドメソッドを持つ要素側が購読者、emitする側はボタン)。
58
+
59
+ ### 2. 動画をラップし、fullscreen中のみ終了ボタンを表示
60
+
61
+ ```html
62
+ <wcs-state>
63
+ <script type="module">
64
+ export default {
65
+ $commandTokens: ["exitFs"],
66
+ isFullscreen: false,
67
+ };
68
+ </script>
69
+ </wcs-state>
70
+
71
+ <wcs-fullscreen data-wcs="active: isFullscreen; command.exitFullscreen: $command.exitFs">
72
+ <video src="/movie.mp4" controls></video>
73
+ </wcs-fullscreen>
74
+ <button data-wcs="hidden: isFullscreen|not; onclick: $command.exitFs">終了</button>
75
+ ```
76
+
77
+ バインドする state パスは事前宣言が必須です(ここでは `isFullscreen: false`。未宣言パスへのバインドは初期化時に throw します)。`data-wcs` のパスでの否定は先頭 `!` ではなく `|not` フィルタで行います(`isFullscreen|not`)— パス構文に前置演算子は存在しません。
78
+
79
+ ## 観測可能プロパティ(出力)
80
+
81
+ | プロパティ | イベント | 説明 |
82
+ | ----------- | ------------------------ | ---- |
83
+ | `active` | `wcs-fullscreen:change` | `document.fullscreenElement` が**このインスタンスが解決したtarget**と一致している間 `true`、それ以外は `false`。 |
84
+ | `error` | *(無し — 単純なgetter、data-wcsでバインド不可)* | 直近の失敗: rejectされたPromise(gesture外呼び出しなら `TypeError` 等)、プラットフォームAPI非対応なら `{ message: "Fullscreen API is not supported." }`、`target` が要素へ未解決なら `{ message: "Fullscreen target could not be resolved." }`。直近の呼び出しが成功済み・まだ何も失敗していない場合は `null`。 |
85
+
86
+ ## コマンド
87
+
88
+ | コマンド | 非同期 | 説明 |
89
+ | ----------------------- | ------ | ---- |
90
+ | `requestFullscreen()` | あり | `target` を解決し、その要素に対して `requestFullscreen()` を呼ぶ。 |
91
+ | `exitFullscreen()` | あり | `document.exitFullscreen()` を呼ぶ。何もfullscreenでなければ何もせず終了する(silent no-op)。 |
92
+
93
+ ## 属性 / 入力
94
+
95
+ | 属性 | 説明 |
96
+ | ---------- | ---- |
97
+ | `target` | `@wcstack/intersection` の `target` と同じ3モード解決: `"self"`、CSSセレクタ、または省略(最初の子要素)。 |
98
+
99
+ ## 注意・制限
100
+
101
+ - **user gesture制約。** `requestFullscreen()` は実際のuser gesture(クリックハンドラ等)内から同期的に呼ばれた場合のみ成功します。本ノードはgestureを生成できません — command-tokenプロトコル経由(`<wcs-fullscreen>` 側の `command.requestFullscreen: $command.<token>`、ボタン側の `onclick: $command.<token>`)で呼び出す場合は、**起動元のイベント自体**が本物のuser gestureであることを確認してください。`setTimeout` の中やPromiseチェーンの奥深くから呼び出すと、呼び出し方法に関わらず `TypeError`(WHATWG Fullscreen仕様のtransient-activationチェックによる。`NotAllowedError` ではない)でrejectされます — これはブラウザレベルの制約であり、wcstack側で回避する手段はありません。
102
+ - **ベンダープレフィックス。** 一部の古いSafariバージョンは `webkitRequestFullscreen` / `webkitExitFullscreen` / `webkitFullscreenElement` / `webkitfullscreenchange` のみを実装しています。Coreは標準名を優先的にプローブし、**呼び出しの都度**(非キャッシュ)レガシー名にフォールバックするため、両方とも透過的にサポートされます。
103
+ - **複数インスタンス。** `document.fullscreenElement` はdocument全体で単一の値です。異なるtargetを指す複数の `<wcs-fullscreen>` インスタンスが存在する場合、`target` が `document.fullscreenElement` と一致するインスタンスのみが `active: true` を報告し、他は正しく `false` を報告します。各インスタンスは内部で**自分自身が解決したtarget**を追跡しており、単純に「何かがfullscreenかどうか」をミラーしているわけではありません。ただし非対称な点に注意: `exitFullscreen()` はインスタンス単位にスコープされて**いません** — document全体に作用する `document.exitFullscreen()` を呼ぶため、どのインスタンスから呼んでも、現在fullscreenの要素(別インスタンスの `target` がfullscreen化した要素であっても)を解除します(silent no-opの判定も同様にdocument全体の「何かがfullscreenか」であり、「自分のtargetがfullscreenか」ではありません)。これはプラットフォームAPI自体の挙動をそのまま反映したものです。
104
+ - **`exitFullscreen()` は安全なno-op。** 何もfullscreenでない状態(またはAPI非対応)で呼び出してもエラーなくresolveします — 失敗しうる事前条件チェックではなく、べき等な「fullscreenでないことを保証する」コマンドとして扱われます。
105
+ - **`error` に専用イベントは無く、`data-wcs` でバインドもできない。** 大半のwcstack IOノードと異なり、`error` は専用の `wcs-fullscreen:error` イベントを持たない単純なgetterで、`static wcBindable.properties` にも宣言されていません — バインディング側が購読できる対象が無いため、リアクティブに観測することはできません。コマンドのPromiseがsettleした後、`element.error` を命令的に読み取ってください(例: `await el.requestFullscreen(); if (el.error) { ... }`)。
106
+ - **`_gen` 世代ガード。** `dispose()` 後(または後続の呼び出しに追い越された後)にsettleした進行中の `requestFullscreen()`/`exitFullscreen()` 呼び出しは、破棄済みの状態を書き換えません。
107
+ - **SSR(`@wcstack/server`)。** `static hasConnectedCallbackPromise = true` を宣言し `connectedCallbackPromise` を公開しますが、`fullscreenchange` の購読が同期的なため、この promise は常に即座に settle します。
108
+
109
+ ## ヘッドレス利用(`FullscreenCore`)
110
+
111
+ CoreはDOM依存が `document` と明示的に渡された対象 `Element` のみで、セレクタの解決自体は一切行いません:
112
+
113
+ ```typescript
114
+ import { FullscreenCore } from "@wcstack/fullscreen";
115
+
116
+ const core = new FullscreenCore();
117
+ core.addEventListener("wcs-fullscreen:change", (e) => {
118
+ console.log((e as CustomEvent).detail); // { active: true | false }
119
+ });
120
+
121
+ await core.observe(); // document の fullscreenchange を購読
122
+ await core.requestFullscreen(myElement); // user gesture 内から呼ぶ必要がある
123
+ console.log(core.active); // fullscreenchange で確認されると true
124
+
125
+ await core.exitFullscreen();
126
+ core.dispose(); // fullscreenchange リスナーを外す
127
+ ```
128
+
129
+ ## ライセンス
130
+
131
+ MIT
package/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # @wcstack/fullscreen
2
+
3
+ `@wcstack/fullscreen` is a headless Fullscreen API component for the wcstack ecosystem.
4
+
5
+ It is not a visual UI widget.
6
+ It is a **control node**: unlike most wcstack IO nodes (which act on themselves), `<wcs-fullscreen>` drives `requestFullscreen()` / `exitFullscreen()` on a *referenced* element — the same way `@wcstack/intersection` observes a referenced element rather than itself.
7
+
8
+ With `@wcstack/state`, `<wcs-fullscreen>` can be bound directly through path contracts:
9
+
10
+ - **input surface**: `target` (which element to operate on — see below)
11
+ - **output state surface**: `active`, `error`
12
+ - **commands**: `requestFullscreen()`, `exitFullscreen()`
13
+
14
+ `@wcstack/fullscreen` follows the [CSBC](https://github.com/csbc-dev/arch/blob/main/README.md) (Core / Shell / Binding Contract) architecture:
15
+
16
+ - **Core** (`FullscreenCore`) drives the Fullscreen API and tracks `document`'s `fullscreenchange` event
17
+ - **Shell** (`<wcs-fullscreen target="...">`) resolves `target` to a DOM element and connects Core state to DOM lifecycle
18
+ - **Binding Contract** (`static wcBindable`) declares the observable `active` property and the `requestFullscreen`/`exitFullscreen` commands
19
+
20
+ ## Why this exists — you operate on the *target*, not on the tag itself
21
+
22
+ `Element.requestFullscreen()` is a method on the element you want to fullscreen — an image, a video, a card UI — not on `<wcs-fullscreen>` itself. So this tag is a non-visual control element (its `display` is set per target-resolution mode — see the table below) that points at another element via its `target` attribute, exactly like `<wcs-intersect>`:
23
+
24
+ | `target` | operates on | display | typical use |
25
+ | ---------------- | ------------------------ | ------------ | -------------------------- |
26
+ | omitted | first element child | `contents` | wrap a gallery image/video |
27
+ | `"#hero"` / selector | the matched element | `none` | point at a distant node |
28
+ | `"self"` | the element itself | `block` | fullscreen the wrapper itself |
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ npm install @wcstack/fullscreen
34
+ ```
35
+
36
+ ## Quick Start
37
+
38
+ ### 1. Fullscreen an image on button click
39
+
40
+ ```html
41
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
42
+ <script type="module" src="https://esm.run/@wcstack/fullscreen/auto"></script>
43
+
44
+ <wcs-state>
45
+ <script type="module">
46
+ export default {
47
+ $commandTokens: ["goFullscreen"],
48
+ };
49
+ </script>
50
+ </wcs-state>
51
+
52
+ <wcs-fullscreen target="#hero" data-wcs="command.requestFullscreen: $command.goFullscreen"></wcs-fullscreen>
53
+ <img id="hero" src="/photo.jpg">
54
+ <button data-wcs="onclick: $command.goFullscreen">Fullscreen</button>
55
+ ```
56
+
57
+ The button never touches `<wcs-fullscreen>` directly: its click emits the `goFullscreen` command token, and `<wcs-fullscreen>` subscribes to that token via `command.requestFullscreen: $command.goFullscreen` (the [command-token protocol](../state/) — the element with the command method is the *subscriber*, not the emitter).
58
+
59
+ ### 2. Wrap a video and show an exit button while active
60
+
61
+ ```html
62
+ <wcs-state>
63
+ <script type="module">
64
+ export default {
65
+ $commandTokens: ["exitFs"],
66
+ isFullscreen: false,
67
+ };
68
+ </script>
69
+ </wcs-state>
70
+
71
+ <wcs-fullscreen data-wcs="active: isFullscreen; command.exitFullscreen: $command.exitFs">
72
+ <video src="/movie.mp4" controls></video>
73
+ </wcs-fullscreen>
74
+ <button data-wcs="hidden: isFullscreen|not; onclick: $command.exitFs">Exit fullscreen</button>
75
+ ```
76
+
77
+ Every bound state path must be declared up front — `isFullscreen: false` here; binding an undeclared path throws at initialization. Negation in a `data-wcs` path is done with the `|not` filter (`isFullscreen|not`), not a leading `!` — paths do not support prefix operators.
78
+
79
+ ## Observable Properties (outputs)
80
+
81
+ | Property | Event | Description |
82
+ | --------- | ---------------------- | ------------ |
83
+ | `active` | `wcs-fullscreen:change` | `true` while `document.fullscreenElement` is *this instance's resolved target*; `false` otherwise. |
84
+ | `error` | *(none — plain getter, not data-wcs bindable)* | The most recent failure: a rejected promise (e.g. a `TypeError` for a gesture-less call), `{ message: "Fullscreen API is not supported." }` when the platform API is missing, `{ message: "Fullscreen target could not be resolved." }` when `target` did not resolve to an element, or `null` if the last attempt succeeded / nothing has failed yet. |
85
+
86
+ ## Commands
87
+
88
+ | Command | Async | Description |
89
+ | --------------------- | ------ | ------------ |
90
+ | `requestFullscreen()` | yes | Resolve `target` and call `requestFullscreen()` on it. |
91
+ | `exitFullscreen()` | yes | Call `document.exitFullscreen()`. Silent no-op if nothing is currently fullscreen. |
92
+
93
+ ## Attributes / Inputs
94
+
95
+ | Attribute | Description |
96
+ | ---------- | ------------ |
97
+ | `target` | Same 3-mode resolution as `@wcstack/intersection`'s `target`: `"self"`, a CSS selector, or omitted (first child). |
98
+
99
+ ## Notes & limitations
100
+
101
+ - **User gesture requirement.** `requestFullscreen()` only succeeds when called synchronously from within a real user gesture (e.g. a click handler). This node cannot manufacture a gesture — if you invoke `requestFullscreen` via the command-token protocol (`command.requestFullscreen: $command.<token>` on `<wcs-fullscreen>`, emitted by a button's `onclick: $command.<token>`), make sure the *triggering* event itself is a genuine user gesture. Calling it from inside a `setTimeout` or deep in a promise chain will reject with a `TypeError` (per the WHATWG Fullscreen spec's transient-activation check — not `NotAllowedError`) regardless of how it was invoked — this is a browser-level constraint, not something wcstack can work around.
102
+ - **Vendor prefixes.** Some older Safari versions only implement `webkitRequestFullscreen` / `webkitExitFullscreen` / `webkitFullscreenElement` / `webkitfullscreenchange`. The Core probes the standard name first and falls back to the legacy name at *call time* (never cached), so both are supported transparently.
103
+ - **Multiple instances.** `document.fullscreenElement` is a single, document-wide value. If you have several `<wcs-fullscreen>` instances pointed at different targets, only the instance whose `target` matches `document.fullscreenElement` reports `active: true` — the others correctly report `false`. Each instance tracks *its own* resolved target internally; it does not simply mirror "is anything fullscreen". Note the asymmetry: `exitFullscreen()` is **not** scoped per instance — it calls the document-global `document.exitFullscreen()`, so invoking it on any instance exits whatever element is currently fullscreen, even one put there by another instance's `target` (its silent no-op check is likewise document-wide: "is anything fullscreen", not "is *my* target fullscreen"). This mirrors the platform API itself.
104
+ - **`exitFullscreen()` is a safe no-op.** Calling it when nothing is fullscreen (or when the API is unsupported) resolves without error — it is treated as an idempotent "make sure we're not fullscreen" command, not a failable precondition check.
105
+ - **`error` has no dedicated event, and is not `data-wcs` bindable.** Unlike most wcstack IO nodes, `error` is a plain getter with no `wcs-fullscreen:error` event of its own, and it is not declared in `static wcBindable.properties` — a binding system has nothing to subscribe to and cannot observe it reactively. Read `element.error` imperatively after a command's promise settles (e.g. `await el.requestFullscreen(); if (el.error) { ... }`).
106
+ - **`_gen` generation guard.** In-flight `requestFullscreen()`/`exitFullscreen()` calls that settle after `dispose()` (or after a superseding call) do not write to torn-down state.
107
+ - **SSR (`@wcstack/server`).** Declares `static hasConnectedCallbackPromise = true` and exposes `connectedCallbackPromise`, though since subscribing to `fullscreenchange` is synchronous this promise always settles immediately.
108
+
109
+ ## Headless usage (`FullscreenCore`)
110
+
111
+ The Core has no DOM dependency beyond `document` and the target `Element` you pass it explicitly — it never resolves selectors itself:
112
+
113
+ ```typescript
114
+ import { FullscreenCore } from "@wcstack/fullscreen";
115
+
116
+ const core = new FullscreenCore();
117
+ core.addEventListener("wcs-fullscreen:change", (e) => {
118
+ console.log((e as CustomEvent).detail); // { active: true | false }
119
+ });
120
+
121
+ await core.observe(); // subscribe to document's fullscreenchange
122
+ await core.requestFullscreen(myElement); // must be called from within a user gesture
123
+ console.log(core.active); // true once fullscreenchange confirms it
124
+
125
+ await core.exitFullscreen();
126
+ core.dispose(); // detach the fullscreenchange listener
127
+ ```
128
+
129
+ ## License
130
+
131
+ MIT
package/dist/auto.js ADDED
@@ -0,0 +1,3 @@
1
+ import { bootstrapFullscreen } from "./index.esm.js";
2
+
3
+ bootstrapFullscreen();
@@ -0,0 +1 @@
1
+ import{bootstrapFullscreen}from"./index.esm.min.js";bootstrapFullscreen();
@@ -0,0 +1,180 @@
1
+ interface IWcBindableProperty {
2
+ readonly name: string;
3
+ readonly event: string;
4
+ readonly getter?: (event: Event) => any;
5
+ }
6
+ interface IWcBindableInput {
7
+ readonly name: string;
8
+ readonly attribute?: string;
9
+ }
10
+ interface IWcBindableCommand {
11
+ readonly name: string;
12
+ readonly async?: boolean;
13
+ }
14
+ interface IWcBindable {
15
+ readonly protocol: "wc-bindable";
16
+ readonly version: 1;
17
+ readonly properties: readonly IWcBindableProperty[];
18
+ readonly inputs?: readonly IWcBindableInput[];
19
+ readonly commands?: readonly IWcBindableCommand[];
20
+ }
21
+
22
+ interface ITagNames {
23
+ readonly fullscreen: string;
24
+ }
25
+ interface IWritableTagNames {
26
+ fullscreen?: string;
27
+ }
28
+ interface IConfig {
29
+ readonly tagNames: ITagNames;
30
+ }
31
+ interface IWritableConfig {
32
+ tagNames?: IWritableTagNames;
33
+ }
34
+
35
+ /**
36
+ * Value types for FullscreenCore (headless) — the Core's readable value
37
+ * surface. Note that only `active` is *observable* (declared in
38
+ * `wcBindable.properties` with a change event); `error` is an
39
+ * imperative-read-only getter with no event of its own — a wc-bindable
40
+ * binding core will never deliver it, so read it after a command settles
41
+ * (docs/fullscreen-tag-design.md §8, README "Notes & limitations").
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * const core = new FullscreenCore();
46
+ * // bind() only ever delivers "active" — see the note above about "error".
47
+ * bind(core, (name: keyof WcsFullscreenCoreValues, value) => { ... });
48
+ * ```
49
+ */
50
+ interface WcsFullscreenCoreValues {
51
+ active: boolean;
52
+ error: any;
53
+ }
54
+ /**
55
+ * Value types for the Shell (`<wcs-fullscreen target="...">`) — identical
56
+ * value surface to the Core (same caveat: only `active` is observable).
57
+ * The Shell adds the `target` input (attribute-mirrored) that resolves which
58
+ * element requestFullscreen()/exitFullscreen() operate on
59
+ * (docs/fullscreen-tag-design.md §1/§9).
60
+ */
61
+ type WcsFullscreenValues = WcsFullscreenCoreValues;
62
+
63
+ declare function bootstrapFullscreen(userConfig?: IWritableConfig): void;
64
+
65
+ declare function getConfig(): IConfig;
66
+
67
+ /**
68
+ * Headless Fullscreen API primitive. Unlike most wcstack IO nodes, this Core
69
+ * does not operate on itself: it drives `requestFullscreen()` /
70
+ * `exitFullscreen()` on a *referenced* Element that the Shell resolves via its
71
+ * `target` attribute (docs/fullscreen-tag-design.md §0). The Core only ever
72
+ * receives already-resolved `Element`s from its callers — it has no opinion on
73
+ * how `target` selectors are parsed.
74
+ *
75
+ * `document.fullscreenElement` is a single document-wide value, so this Core
76
+ * always compares against the *last element it resolved* (via
77
+ * `requestFullscreen()`/`setTarget()`), never against "is the document
78
+ * fullscreen at all" — that comparison is what keeps multiple concurrent
79
+ * `<wcs-fullscreen>` instances from all reporting the same `active` value
80
+ * (docs/fullscreen-tag-design.md §2.1, MUST).
81
+ */
82
+ declare class FullscreenCore extends EventTarget {
83
+ static wcBindable: IWcBindable;
84
+ private _target;
85
+ private _active;
86
+ private _error;
87
+ private _resolvedTarget;
88
+ private _gen;
89
+ private _subscribed;
90
+ private _ready;
91
+ constructor(target?: EventTarget);
92
+ get ready(): Promise<void>;
93
+ get active(): boolean;
94
+ get error(): any;
95
+ /**
96
+ * Update the resolved target without issuing a fullscreen request (e.g. the
97
+ * Shell re-resolves `target` on attribute change / connect). Re-evaluates
98
+ * `active` against the current `document.fullscreenElement` so the state
99
+ * stays correct even if the target changed while already fullscreen.
100
+ */
101
+ setTarget(element: Element | null): void;
102
+ observe(): Promise<void>;
103
+ dispose(): void;
104
+ /**
105
+ * Request fullscreen on `element`. never-throw (§3/§6): a missing API or a
106
+ * rejected promise (e.g. a `TypeError` from a call outside a user
107
+ * gesture, per the WHATWG Fullscreen spec's transient-activation check) is
108
+ * caught and surfaced via `error`, never thrown. The caller
109
+ * (Shell) is responsible for resolving `target` and for ensuring this is
110
+ * invoked from within an actual user gesture — this Core cannot manufacture
111
+ * one (docs/fullscreen-tag-design.md §3).
112
+ */
113
+ requestFullscreen(element: Element | null): Promise<void>;
114
+ /**
115
+ * Exit fullscreen. Silent no-op (§7) when nothing is currently fullscreen or
116
+ * the API is unsupported — both are treated as "already achieved the exit
117
+ * intent", not as errors, keeping repeated calls safe and never-throw.
118
+ */
119
+ exitFullscreen(): Promise<void>;
120
+ private _requestFullscreenFn;
121
+ private _elementFullscreenFn;
122
+ private _exitFullscreenFn;
123
+ private _fullscreenElement;
124
+ private _fullscreenChangeEventName;
125
+ private _onFullscreenChange;
126
+ private _applyActive;
127
+ private _setActive;
128
+ private _setError;
129
+ }
130
+
131
+ /**
132
+ * `<wcs-fullscreen target="...">` — declarative Fullscreen API control.
133
+ *
134
+ * Like `intersection`/`resize`, this Shell operates on a *referenced* element,
135
+ * not itself (docs/fullscreen-tag-design.md §0): `target` resolves which
136
+ * element `requestFullscreen()`/`exitFullscreen()` are invoked on, using the
137
+ * exact same 3-mode resolution as `<wcs-intersect>`
138
+ * (docs/fullscreen-tag-design.md §1):
139
+ *
140
+ * | `target` | operates on | display | use case |
141
+ * |-----------------|-------------------------|-------------|--------------------------|
142
+ * | omitted | first element child | `contents` | wrap a gallery image/video |
143
+ * | `"#hero"` / sel | the matched element | `none` | point at a distant node |
144
+ * | `"self"` | the element itself | `block` | fullscreen the wrapper |
145
+ *
146
+ * `requestFullscreen()` requires an active user gesture — this element cannot
147
+ * manufacture one. Invoke the command from within a real click handler
148
+ * (typically via the command-token protocol: this element subscribes with
149
+ * `command.requestFullscreen: $command.<token>`, and a button emits the
150
+ * token from its own click handler, e.g. `onclick: $command.<token>`).
151
+ */
152
+ declare class WcsFullscreen extends HTMLElement {
153
+ static hasConnectedCallbackPromise: boolean;
154
+ static observedAttributes: string[];
155
+ static wcBindable: IWcBindable;
156
+ private _core;
157
+ private _connectedCallbackPromise;
158
+ constructor();
159
+ get connectedCallbackPromise(): Promise<void>;
160
+ get target(): string;
161
+ set target(value: string);
162
+ get active(): boolean;
163
+ get error(): any;
164
+ /**
165
+ * Resolve `target` and request fullscreen on it. never-throw: an
166
+ * unresolvable target or an unsupported/rejected API call are both
167
+ * surfaced via `error`, never thrown (docs/fullscreen-tag-design.md §3/§6).
168
+ */
169
+ requestFullscreen(): Promise<void>;
170
+ exitFullscreen(): Promise<void>;
171
+ private _resolveTarget;
172
+ private _safeQuery;
173
+ private _reresolve;
174
+ connectedCallback(): void;
175
+ disconnectedCallback(): void;
176
+ attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null): void;
177
+ }
178
+
179
+ export { FullscreenCore, WcsFullscreen, bootstrapFullscreen, getConfig };
180
+ export type { IWritableConfig, IWritableTagNames, WcsFullscreenCoreValues, WcsFullscreenValues };
@@ -0,0 +1,392 @@
1
+ const _config = {
2
+ tagNames: {
3
+ fullscreen: "wcs-fullscreen",
4
+ },
5
+ };
6
+ function deepFreeze(obj) {
7
+ if (obj === null || typeof obj !== "object")
8
+ return obj;
9
+ Object.freeze(obj);
10
+ for (const key of Object.keys(obj)) {
11
+ deepFreeze(obj[key]);
12
+ }
13
+ return obj;
14
+ }
15
+ function deepClone(obj) {
16
+ if (obj === null || typeof obj !== "object")
17
+ return obj;
18
+ const clone = {};
19
+ for (const key of Object.keys(obj)) {
20
+ clone[key] = deepClone(obj[key]);
21
+ }
22
+ return clone;
23
+ }
24
+ let frozenConfig = null;
25
+ const config = _config;
26
+ function getConfig() {
27
+ if (!frozenConfig) {
28
+ frozenConfig = deepFreeze(deepClone(_config));
29
+ }
30
+ return frozenConfig;
31
+ }
32
+ function setConfig(partialConfig) {
33
+ if (partialConfig.tagNames) {
34
+ Object.assign(_config.tagNames, partialConfig.tagNames);
35
+ }
36
+ frozenConfig = null;
37
+ }
38
+
39
+ /**
40
+ * Headless Fullscreen API primitive. Unlike most wcstack IO nodes, this Core
41
+ * does not operate on itself: it drives `requestFullscreen()` /
42
+ * `exitFullscreen()` on a *referenced* Element that the Shell resolves via its
43
+ * `target` attribute (docs/fullscreen-tag-design.md §0). The Core only ever
44
+ * receives already-resolved `Element`s from its callers — it has no opinion on
45
+ * how `target` selectors are parsed.
46
+ *
47
+ * `document.fullscreenElement` is a single document-wide value, so this Core
48
+ * always compares against the *last element it resolved* (via
49
+ * `requestFullscreen()`/`setTarget()`), never against "is the document
50
+ * fullscreen at all" — that comparison is what keeps multiple concurrent
51
+ * `<wcs-fullscreen>` instances from all reporting the same `active` value
52
+ * (docs/fullscreen-tag-design.md §2.1, MUST).
53
+ */
54
+ class FullscreenCore extends EventTarget {
55
+ static wcBindable = {
56
+ protocol: "wc-bindable",
57
+ version: 1,
58
+ properties: [
59
+ { name: "active", event: "wcs-fullscreen:change", getter: (e) => e.detail.active },
60
+ ],
61
+ commands: [
62
+ { name: "requestFullscreen", async: true },
63
+ { name: "exitFullscreen", async: true },
64
+ ],
65
+ };
66
+ _target;
67
+ _active = false;
68
+ // Single error slot (§8): null means "no recent failure". Fullscreen's
69
+ // gesture-rejection failure is a one-shot event, not a persistent state
70
+ // machine like permission's 4-value surface — active/error are two
71
+ // orthogonal, independently-observable axes.
72
+ _error = null;
73
+ // The last Element this Core resolved via requestFullscreen()/setTarget().
74
+ // Compared against document.fullscreenElement on every fullscreenchange so
75
+ // each instance judges only its own target (§2.1). null means "no target
76
+ // resolved yet" — active must stay false in that case.
77
+ _resolvedTarget = null;
78
+ // Generation guard (§6): Core-scoped (one per Core, not per-target),
79
+ // mirroring fetch/upload. document.fullscreenElement is a single
80
+ // document-wide slot, so at most one in-flight request/exit is meaningful
81
+ // per Core at a time.
82
+ _gen = 0;
83
+ // True once observe() has attached the document-level fullscreenchange
84
+ // listener. Guards observe() so a redundant call does not double-subscribe;
85
+ // dispose() resets it so a later observe() resumes cleanly.
86
+ _subscribed = false;
87
+ // SSR (§10): no asynchronous probe to await — observe() completes
88
+ // synchronously, so readiness is immediate.
89
+ _ready = Promise.resolve();
90
+ constructor(target) {
91
+ super();
92
+ this._target = target ?? this;
93
+ }
94
+ get ready() {
95
+ return this._ready;
96
+ }
97
+ get active() {
98
+ return this._active;
99
+ }
100
+ get error() {
101
+ return this._error;
102
+ }
103
+ /**
104
+ * Update the resolved target without issuing a fullscreen request (e.g. the
105
+ * Shell re-resolves `target` on attribute change / connect). Re-evaluates
106
+ * `active` against the current `document.fullscreenElement` so the state
107
+ * stays correct even if the target changed while already fullscreen.
108
+ */
109
+ setTarget(element) {
110
+ this._resolvedTarget = element;
111
+ this._applyActive();
112
+ }
113
+ // Lifecycle (§10/§3.5). Idempotent: a second observe() while already
114
+ // subscribed is a no-op (no double listener). Synchronous overall (no probe
115
+ // to await), so the returned promise is only for API uniformity with other
116
+ // IO nodes.
117
+ observe() {
118
+ if (!this._subscribed) {
119
+ this._subscribed = true;
120
+ document.addEventListener(this._fullscreenChangeEventName(), this._onFullscreenChange);
121
+ }
122
+ return this._ready;
123
+ }
124
+ dispose() {
125
+ this._gen++;
126
+ if (this._subscribed) {
127
+ this._subscribed = false;
128
+ document.removeEventListener(this._fullscreenChangeEventName(), this._onFullscreenChange);
129
+ }
130
+ }
131
+ /**
132
+ * Request fullscreen on `element`. never-throw (§3/§6): a missing API or a
133
+ * rejected promise (e.g. a `TypeError` from a call outside a user
134
+ * gesture, per the WHATWG Fullscreen spec's transient-activation check) is
135
+ * caught and surfaced via `error`, never thrown. The caller
136
+ * (Shell) is responsible for resolving `target` and for ensuring this is
137
+ * invoked from within an actual user gesture — this Core cannot manufacture
138
+ * one (docs/fullscreen-tag-design.md §3).
139
+ */
140
+ async requestFullscreen(element) {
141
+ const gen = ++this._gen;
142
+ this._resolvedTarget = element;
143
+ if (!element) {
144
+ // Distinct from "API is not supported" (below): the Shell's `target`
145
+ // selector did not resolve to any element (missing/typo'd selector).
146
+ // Conflating the two previously misled users into thinking Fullscreen
147
+ // itself was unsupported when only their selector was wrong.
148
+ this._setError({ message: "Fullscreen target could not be resolved." });
149
+ return;
150
+ }
151
+ const fn = this._requestFullscreenFn(element);
152
+ if (!fn) {
153
+ this._setError({ message: "Fullscreen API is not supported." });
154
+ return;
155
+ }
156
+ try {
157
+ await fn.call(element);
158
+ if (gen !== this._gen)
159
+ return; // stale: dispose()/superseding call ran
160
+ this._setError(null);
161
+ this._applyActive();
162
+ }
163
+ catch (e) {
164
+ if (gen !== this._gen)
165
+ return; // stale
166
+ this._setError(e);
167
+ }
168
+ }
169
+ /**
170
+ * Exit fullscreen. Silent no-op (§7) when nothing is currently fullscreen or
171
+ * the API is unsupported — both are treated as "already achieved the exit
172
+ * intent", not as errors, keeping repeated calls safe and never-throw.
173
+ */
174
+ async exitFullscreen() {
175
+ // no-op checks come BEFORE the generation bump: a call that does nothing
176
+ // must not supersede an in-flight requestFullscreen() — bumping first
177
+ // would make the pending request's settle handling stale and silently
178
+ // swallow its error/active updates.
179
+ if (this._fullscreenElement() === null)
180
+ return; // already not fullscreen: silent no-op
181
+ const fn = this._exitFullscreenFn();
182
+ if (!fn)
183
+ return; // unsupported: silent no-op (semantically already "not fullscreen")
184
+ const gen = ++this._gen;
185
+ try {
186
+ await fn();
187
+ if (gen !== this._gen)
188
+ return; // stale
189
+ this._setError(null);
190
+ this._applyActive();
191
+ }
192
+ catch (e) {
193
+ if (gen !== this._gen)
194
+ return; // stale
195
+ this._setError(e);
196
+ }
197
+ }
198
+ // --- API resolution layer (§4): call-time, never cached. Lets tests
199
+ // install/remove the standard/legacy APIs freely and lets an unsupported
200
+ // environment be detected correctly on every call. ---
201
+ _requestFullscreenFn(el) {
202
+ return this._elementFullscreenFn(el, "requestFullscreen")
203
+ ?? this._elementFullscreenFn(el, "webkitRequestFullscreen");
204
+ }
205
+ // Resolve a fullscreen method for `el` WITHOUT a naive `el[name]` lookup.
206
+ // A plain lookup walks the whole prototype chain — and <wcs-fullscreen>
207
+ // itself declares a `requestFullscreen()` *command* method, so when the
208
+ // resolved target is the Shell element (target="self", or target omitted
209
+ // with no children), the naive lookup would find the Shell's own command
210
+ // instead of the platform API and recurse infinitely (stack overflow).
211
+ // Instead: check the element's own properties (how tests install stubs —
212
+ // happy-dom has no Fullscreen API — and how a deliberate per-element
213
+ // monkey-patch would appear), then jump straight to Element.prototype,
214
+ // where the platform defines the real methods. Both the standard and the
215
+ // legacy webkit name go through this same resolution for symmetry.
216
+ _elementFullscreenFn(el, name) {
217
+ if (Object.prototype.hasOwnProperty.call(el, name)) {
218
+ return el[name];
219
+ }
220
+ return Element.prototype[name];
221
+ }
222
+ _exitFullscreenFn() {
223
+ const d = document;
224
+ return d.exitFullscreen?.bind(document) ?? d.webkitExitFullscreen?.bind(document);
225
+ }
226
+ _fullscreenElement() {
227
+ const d = document;
228
+ return d.fullscreenElement ?? d.webkitFullscreenElement ?? null;
229
+ }
230
+ _fullscreenChangeEventName() {
231
+ return "onfullscreenchange" in document ? "fullscreenchange" : "webkitfullscreenchange";
232
+ }
233
+ // --- Internal ---
234
+ _onFullscreenChange = () => {
235
+ this._applyActive();
236
+ };
237
+ // Re-derive `active` by comparing document.fullscreenElement (incl. legacy
238
+ // fallback) against *this instance's* resolved target (§2/§2.1/§5). A null
239
+ // resolved target always yields active=false — there is nothing for this
240
+ // instance to claim as "mine".
241
+ _applyActive() {
242
+ const next = this._resolvedTarget !== null && this._fullscreenElement() === this._resolvedTarget;
243
+ this._setActive(next);
244
+ }
245
+ _setActive(v) {
246
+ if (this._active === v)
247
+ return; // same-value guard (§3.3 MUST)
248
+ this._active = v;
249
+ this._target.dispatchEvent(new CustomEvent("wcs-fullscreen:change", {
250
+ detail: { active: v },
251
+ bubbles: true,
252
+ }));
253
+ }
254
+ _setError(error) {
255
+ this._error = error;
256
+ }
257
+ }
258
+
259
+ /**
260
+ * `<wcs-fullscreen target="...">` — declarative Fullscreen API control.
261
+ *
262
+ * Like `intersection`/`resize`, this Shell operates on a *referenced* element,
263
+ * not itself (docs/fullscreen-tag-design.md §0): `target` resolves which
264
+ * element `requestFullscreen()`/`exitFullscreen()` are invoked on, using the
265
+ * exact same 3-mode resolution as `<wcs-intersect>`
266
+ * (docs/fullscreen-tag-design.md §1):
267
+ *
268
+ * | `target` | operates on | display | use case |
269
+ * |-----------------|-------------------------|-------------|--------------------------|
270
+ * | omitted | first element child | `contents` | wrap a gallery image/video |
271
+ * | `"#hero"` / sel | the matched element | `none` | point at a distant node |
272
+ * | `"self"` | the element itself | `block` | fullscreen the wrapper |
273
+ *
274
+ * `requestFullscreen()` requires an active user gesture — this element cannot
275
+ * manufacture one. Invoke the command from within a real click handler
276
+ * (typically via the command-token protocol: this element subscribes with
277
+ * `command.requestFullscreen: $command.<token>`, and a button emits the
278
+ * token from its own click handler, e.g. `onclick: $command.<token>`).
279
+ */
280
+ class WcsFullscreen extends HTMLElement {
281
+ // SSR (§10): the fullscreenchange subscription is established synchronously
282
+ // on connect, but the Shell still exposes connectedCallbackPromise so the
283
+ // state binder can await it uniformly across all IO nodes before
284
+ // snapshotting.
285
+ static hasConnectedCallbackPromise = true;
286
+ static observedAttributes = ["target"];
287
+ static wcBindable = {
288
+ ...FullscreenCore.wcBindable,
289
+ inputs: [{ name: "target", attribute: "target" }],
290
+ // Core の commands をそのまま継承(単一情報源)。network/intersection と同型。
291
+ commands: FullscreenCore.wcBindable.commands,
292
+ };
293
+ _core;
294
+ _connectedCallbackPromise = Promise.resolve();
295
+ constructor() {
296
+ super();
297
+ this._core = new FullscreenCore(this);
298
+ }
299
+ get connectedCallbackPromise() {
300
+ return this._connectedCallbackPromise;
301
+ }
302
+ // --- Attribute accessors ---
303
+ get target() {
304
+ return this.getAttribute("target") ?? "";
305
+ }
306
+ set target(value) {
307
+ this.setAttribute("target", value);
308
+ }
309
+ // --- Core delegated getters ---
310
+ get active() {
311
+ return this._core.active;
312
+ }
313
+ get error() {
314
+ return this._core.error;
315
+ }
316
+ // --- Commands ---
317
+ /**
318
+ * Resolve `target` and request fullscreen on it. never-throw: an
319
+ * unresolvable target or an unsupported/rejected API call are both
320
+ * surfaced via `error`, never thrown (docs/fullscreen-tag-design.md §3/§6).
321
+ */
322
+ async requestFullscreen() {
323
+ const { element } = this._resolveTarget();
324
+ await this._core.requestFullscreen(element);
325
+ }
326
+ async exitFullscreen() {
327
+ await this._core.exitFullscreen();
328
+ }
329
+ // --- Internal ---
330
+ // Copied verbatim from <wcs-intersect> (Intersect.ts _resolveTarget/_safeQuery,
331
+ // docs/fullscreen-tag-design.md §1): identical 3-mode resolution, only the
332
+ // "what to do with the resolved element" step differs.
333
+ _resolveTarget() {
334
+ const target = this.target;
335
+ if (target === "self") {
336
+ return { element: this, display: "block" };
337
+ }
338
+ if (target !== "") {
339
+ const scope = this.getRootNode();
340
+ return { element: this._safeQuery(scope, target), display: "none" };
341
+ }
342
+ const child = this.firstElementChild;
343
+ if (child) {
344
+ return { element: child, display: "contents" };
345
+ }
346
+ return { element: this, display: "block" };
347
+ }
348
+ _safeQuery(scope, selector) {
349
+ try {
350
+ return scope.querySelector(selector);
351
+ }
352
+ catch {
353
+ return null;
354
+ }
355
+ }
356
+ _reresolve() {
357
+ const { element, display } = this._resolveTarget();
358
+ this.style.display = display;
359
+ this._core.setTarget(element);
360
+ }
361
+ // --- Lifecycle ---
362
+ connectedCallback() {
363
+ this._reresolve();
364
+ this._connectedCallbackPromise = this._core.observe();
365
+ }
366
+ disconnectedCallback() {
367
+ this._core.dispose();
368
+ }
369
+ attributeChangedCallback(_name, oldValue, newValue) {
370
+ if (oldValue === newValue)
371
+ return;
372
+ if (!this.isConnected)
373
+ return;
374
+ this._reresolve();
375
+ }
376
+ }
377
+
378
+ function registerComponents() {
379
+ if (!customElements.get(config.tagNames.fullscreen)) {
380
+ customElements.define(config.tagNames.fullscreen, WcsFullscreen);
381
+ }
382
+ }
383
+
384
+ function bootstrapFullscreen(userConfig) {
385
+ if (userConfig) {
386
+ setConfig(userConfig);
387
+ }
388
+ registerComponents();
389
+ }
390
+
391
+ export { FullscreenCore, WcsFullscreen, bootstrapFullscreen, getConfig };
392
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/FullscreenCore.ts","../src/components/Fullscreen.ts","../src/registerComponents.ts","../src/bootstrapFullscreen.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n fullscreen: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n fullscreen: \"wcs-fullscreen\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable } from \"../types.js\";\n\n/**\n * Headless Fullscreen API primitive. Unlike most wcstack IO nodes, this Core\n * does not operate on itself: it drives `requestFullscreen()` /\n * `exitFullscreen()` on a *referenced* Element that the Shell resolves via its\n * `target` attribute (docs/fullscreen-tag-design.md §0). The Core only ever\n * receives already-resolved `Element`s from its callers — it has no opinion on\n * how `target` selectors are parsed.\n *\n * `document.fullscreenElement` is a single document-wide value, so this Core\n * always compares against the *last element it resolved* (via\n * `requestFullscreen()`/`setTarget()`), never against \"is the document\n * fullscreen at all\" — that comparison is what keeps multiple concurrent\n * `<wcs-fullscreen>` instances from all reporting the same `active` value\n * (docs/fullscreen-tag-design.md §2.1, MUST).\n */\nexport class FullscreenCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"active\", event: \"wcs-fullscreen:change\", getter: (e: Event) => (e as CustomEvent).detail.active },\n ],\n commands: [\n { name: \"requestFullscreen\", async: true },\n { name: \"exitFullscreen\", async: true },\n ],\n };\n\n private _target: EventTarget;\n private _active: boolean = false;\n // Single error slot (§8): null means \"no recent failure\". Fullscreen's\n // gesture-rejection failure is a one-shot event, not a persistent state\n // machine like permission's 4-value surface — active/error are two\n // orthogonal, independently-observable axes.\n private _error: any = null;\n\n // The last Element this Core resolved via requestFullscreen()/setTarget().\n // Compared against document.fullscreenElement on every fullscreenchange so\n // each instance judges only its own target (§2.1). null means \"no target\n // resolved yet\" — active must stay false in that case.\n private _resolvedTarget: Element | null = null;\n\n // Generation guard (§6): Core-scoped (one per Core, not per-target),\n // mirroring fetch/upload. document.fullscreenElement is a single\n // document-wide slot, so at most one in-flight request/exit is meaningful\n // per Core at a time.\n private _gen = 0;\n\n // True once observe() has attached the document-level fullscreenchange\n // listener. Guards observe() so a redundant call does not double-subscribe;\n // dispose() resets it so a later observe() resumes cleanly.\n private _subscribed = false;\n\n // SSR (§10): no asynchronous probe to await — observe() completes\n // synchronously, so readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get ready(): Promise<void> {\n return this._ready;\n }\n\n get active(): boolean {\n return this._active;\n }\n\n get error(): any {\n return this._error;\n }\n\n /**\n * Update the resolved target without issuing a fullscreen request (e.g. the\n * Shell re-resolves `target` on attribute change / connect). Re-evaluates\n * `active` against the current `document.fullscreenElement` so the state\n * stays correct even if the target changed while already fullscreen.\n */\n setTarget(element: Element | null): void {\n this._resolvedTarget = element;\n this._applyActive();\n }\n\n // Lifecycle (§10/§3.5). Idempotent: a second observe() while already\n // subscribed is a no-op (no double listener). Synchronous overall (no probe\n // to await), so the returned promise is only for API uniformity with other\n // IO nodes.\n observe(): Promise<void> {\n if (!this._subscribed) {\n this._subscribed = true;\n document.addEventListener(this._fullscreenChangeEventName(), this._onFullscreenChange);\n }\n return this._ready;\n }\n\n dispose(): void {\n this._gen++;\n if (this._subscribed) {\n this._subscribed = false;\n document.removeEventListener(this._fullscreenChangeEventName(), this._onFullscreenChange);\n }\n }\n\n /**\n * Request fullscreen on `element`. never-throw (§3/§6): a missing API or a\n * rejected promise (e.g. a `TypeError` from a call outside a user\n * gesture, per the WHATWG Fullscreen spec's transient-activation check) is\n * caught and surfaced via `error`, never thrown. The caller\n * (Shell) is responsible for resolving `target` and for ensuring this is\n * invoked from within an actual user gesture — this Core cannot manufacture\n * one (docs/fullscreen-tag-design.md §3).\n */\n async requestFullscreen(element: Element | null): Promise<void> {\n const gen = ++this._gen;\n this._resolvedTarget = element;\n if (!element) {\n // Distinct from \"API is not supported\" (below): the Shell's `target`\n // selector did not resolve to any element (missing/typo'd selector).\n // Conflating the two previously misled users into thinking Fullscreen\n // itself was unsupported when only their selector was wrong.\n this._setError({ message: \"Fullscreen target could not be resolved.\" });\n return;\n }\n const fn = this._requestFullscreenFn(element);\n if (!fn) {\n this._setError({ message: \"Fullscreen API is not supported.\" });\n return;\n }\n try {\n await fn.call(element);\n if (gen !== this._gen) return; // stale: dispose()/superseding call ran\n this._setError(null);\n this._applyActive();\n } catch (e: any) {\n if (gen !== this._gen) return; // stale\n this._setError(e);\n }\n }\n\n /**\n * Exit fullscreen. Silent no-op (§7) when nothing is currently fullscreen or\n * the API is unsupported — both are treated as \"already achieved the exit\n * intent\", not as errors, keeping repeated calls safe and never-throw.\n */\n async exitFullscreen(): Promise<void> {\n // no-op checks come BEFORE the generation bump: a call that does nothing\n // must not supersede an in-flight requestFullscreen() — bumping first\n // would make the pending request's settle handling stale and silently\n // swallow its error/active updates.\n if (this._fullscreenElement() === null) return; // already not fullscreen: silent no-op\n const fn = this._exitFullscreenFn();\n if (!fn) return; // unsupported: silent no-op (semantically already \"not fullscreen\")\n const gen = ++this._gen;\n try {\n await fn();\n if (gen !== this._gen) return; // stale\n this._setError(null);\n this._applyActive();\n } catch (e: any) {\n if (gen !== this._gen) return; // stale\n this._setError(e);\n }\n }\n\n // --- API resolution layer (§4): call-time, never cached. Lets tests\n // install/remove the standard/legacy APIs freely and lets an unsupported\n // environment be detected correctly on every call. ---\n\n private _requestFullscreenFn(el: Element): (() => Promise<void>) | undefined {\n return this._elementFullscreenFn(el, \"requestFullscreen\")\n ?? this._elementFullscreenFn(el, \"webkitRequestFullscreen\");\n }\n\n // Resolve a fullscreen method for `el` WITHOUT a naive `el[name]` lookup.\n // A plain lookup walks the whole prototype chain — and <wcs-fullscreen>\n // itself declares a `requestFullscreen()` *command* method, so when the\n // resolved target is the Shell element (target=\"self\", or target omitted\n // with no children), the naive lookup would find the Shell's own command\n // instead of the platform API and recurse infinitely (stack overflow).\n // Instead: check the element's own properties (how tests install stubs —\n // happy-dom has no Fullscreen API — and how a deliberate per-element\n // monkey-patch would appear), then jump straight to Element.prototype,\n // where the platform defines the real methods. Both the standard and the\n // legacy webkit name go through this same resolution for symmetry.\n private _elementFullscreenFn(el: Element, name: string): (() => Promise<void>) | undefined {\n if (Object.prototype.hasOwnProperty.call(el, name)) {\n return (el as any)[name];\n }\n return (Element.prototype as any)[name];\n }\n\n private _exitFullscreenFn(): (() => Promise<void>) | undefined {\n const d = document as any;\n return d.exitFullscreen?.bind(document) ?? d.webkitExitFullscreen?.bind(document);\n }\n\n private _fullscreenElement(): Element | null {\n const d = document as any;\n return d.fullscreenElement ?? d.webkitFullscreenElement ?? null;\n }\n\n private _fullscreenChangeEventName(): string {\n return \"onfullscreenchange\" in document ? \"fullscreenchange\" : \"webkitfullscreenchange\";\n }\n\n // --- Internal ---\n\n private _onFullscreenChange = (): void => {\n this._applyActive();\n };\n\n // Re-derive `active` by comparing document.fullscreenElement (incl. legacy\n // fallback) against *this instance's* resolved target (§2/§2.1/§5). A null\n // resolved target always yields active=false — there is nothing for this\n // instance to claim as \"mine\".\n private _applyActive(): void {\n const next = this._resolvedTarget !== null && this._fullscreenElement() === this._resolvedTarget;\n this._setActive(next);\n }\n\n private _setActive(v: boolean): void {\n if (this._active === v) return; // same-value guard (§3.3 MUST)\n this._active = v;\n this._target.dispatchEvent(new CustomEvent(\"wcs-fullscreen:change\", {\n detail: { active: v },\n bubbles: true,\n }));\n }\n\n private _setError(error: any): void {\n this._error = error;\n }\n}\n","import { IWcBindable } from \"../types.js\";\nimport { FullscreenCore } from \"../core/FullscreenCore.js\";\n\n/**\n * `<wcs-fullscreen target=\"...\">` — declarative Fullscreen API control.\n *\n * Like `intersection`/`resize`, this Shell operates on a *referenced* element,\n * not itself (docs/fullscreen-tag-design.md §0): `target` resolves which\n * element `requestFullscreen()`/`exitFullscreen()` are invoked on, using the\n * exact same 3-mode resolution as `<wcs-intersect>`\n * (docs/fullscreen-tag-design.md §1):\n *\n * | `target` | operates on | display | use case |\n * |-----------------|-------------------------|-------------|--------------------------|\n * | omitted | first element child | `contents` | wrap a gallery image/video |\n * | `\"#hero\"` / sel | the matched element | `none` | point at a distant node |\n * | `\"self\"` | the element itself | `block` | fullscreen the wrapper |\n *\n * `requestFullscreen()` requires an active user gesture — this element cannot\n * manufacture one. Invoke the command from within a real click handler\n * (typically via the command-token protocol: this element subscribes with\n * `command.requestFullscreen: $command.<token>`, and a button emits the\n * token from its own click handler, e.g. `onclick: $command.<token>`).\n */\nexport class WcsFullscreen extends HTMLElement {\n // SSR (§10): the fullscreenchange subscription is established synchronously\n // on connect, but the Shell still exposes connectedCallbackPromise so the\n // state binder can await it uniformly across all IO nodes before\n // snapshotting.\n static hasConnectedCallbackPromise = true;\n\n static observedAttributes = [\"target\"];\n\n static wcBindable: IWcBindable = {\n ...FullscreenCore.wcBindable,\n inputs: [{ name: \"target\", attribute: \"target\" }],\n // Core の commands をそのまま継承(単一情報源)。network/intersection と同型。\n commands: FullscreenCore.wcBindable.commands,\n };\n\n private _core: FullscreenCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new FullscreenCore(this);\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Attribute accessors ---\n\n get target(): string {\n return this.getAttribute(\"target\") ?? \"\";\n }\n\n set target(value: string) {\n this.setAttribute(\"target\", value);\n }\n\n // --- Core delegated getters ---\n\n get active(): boolean {\n return this._core.active;\n }\n\n get error(): any {\n return this._core.error;\n }\n\n // --- Commands ---\n\n /**\n * Resolve `target` and request fullscreen on it. never-throw: an\n * unresolvable target or an unsupported/rejected API call are both\n * surfaced via `error`, never thrown (docs/fullscreen-tag-design.md §3/§6).\n */\n async requestFullscreen(): Promise<void> {\n const { element } = this._resolveTarget();\n await this._core.requestFullscreen(element);\n }\n\n async exitFullscreen(): Promise<void> {\n await this._core.exitFullscreen();\n }\n\n // --- Internal ---\n\n // Copied verbatim from <wcs-intersect> (Intersect.ts _resolveTarget/_safeQuery,\n // docs/fullscreen-tag-design.md §1): identical 3-mode resolution, only the\n // \"what to do with the resolved element\" step differs.\n private _resolveTarget(): { element: Element | null; display: string } {\n const target = this.target;\n if (target === \"self\") {\n return { element: this, display: \"block\" };\n }\n if (target !== \"\") {\n const scope = this.getRootNode() as Document | ShadowRoot;\n return { element: this._safeQuery(scope, target), display: \"none\" };\n }\n const child = this.firstElementChild;\n if (child) {\n return { element: child, display: \"contents\" };\n }\n return { element: this, display: \"block\" };\n }\n\n private _safeQuery(scope: Document | ShadowRoot, selector: string): Element | null {\n try {\n return scope.querySelector(selector);\n } catch {\n return null;\n }\n }\n\n private _reresolve(): void {\n const { element, display } = this._resolveTarget();\n this.style.display = display;\n this._core.setTarget(element);\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this._reresolve();\n this._connectedCallbackPromise = this._core.observe();\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n\n attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n if (!this.isConnected) return;\n this._reresolve();\n }\n}\n","import { WcsFullscreen } from \"./components/Fullscreen.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.fullscreen)) {\n customElements.define(config.tagNames.fullscreen, WcsFullscreen);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapFullscreen(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e={tagNames:{fullscreen:"wcs-fullscreen"}};function t(e){if(null===e||"object"!=typeof e)return e;Object.freeze(e);for(const s of Object.keys(e))t(e[s]);return e}function s(e){if(null===e||"object"!=typeof e)return e;const t={};for(const r of Object.keys(e))t[r]=s(e[r]);return t}let r=null;const n=e;function l(){return r||(r=t(s(e))),r}class c extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"active",event:"wcs-fullscreen:change",getter:e=>e.detail.active}],commands:[{name:"requestFullscreen",async:!0},{name:"exitFullscreen",async:!0}]};_target;_active=!1;_error=null;_resolvedTarget=null;_gen=0;_subscribed=!1;_ready=Promise.resolve();constructor(e){super(),this._target=e??this}get ready(){return this._ready}get active(){return this._active}get error(){return this._error}setTarget(e){this._resolvedTarget=e,this._applyActive()}observe(){return this._subscribed||(this._subscribed=!0,document.addEventListener(this._fullscreenChangeEventName(),this._onFullscreenChange)),this._ready}dispose(){this._gen++,this._subscribed&&(this._subscribed=!1,document.removeEventListener(this._fullscreenChangeEventName(),this._onFullscreenChange))}async requestFullscreen(e){const t=++this._gen;if(this._resolvedTarget=e,!e)return void this._setError({message:"Fullscreen target could not be resolved."});const s=this._requestFullscreenFn(e);if(s)try{if(await s.call(e),t!==this._gen)return;this._setError(null),this._applyActive()}catch(e){if(t!==this._gen)return;this._setError(e)}else this._setError({message:"Fullscreen API is not supported."})}async exitFullscreen(){if(null===this._fullscreenElement())return;const e=this._exitFullscreenFn();if(!e)return;const t=++this._gen;try{if(await e(),t!==this._gen)return;this._setError(null),this._applyActive()}catch(e){if(t!==this._gen)return;this._setError(e)}}_requestFullscreenFn(e){return this._elementFullscreenFn(e,"requestFullscreen")??this._elementFullscreenFn(e,"webkitRequestFullscreen")}_elementFullscreenFn(e,t){return Object.prototype.hasOwnProperty.call(e,t)?e[t]:Element.prototype[t]}_exitFullscreenFn(){const e=document;return e.exitFullscreen?.bind(document)??e.webkitExitFullscreen?.bind(document)}_fullscreenElement(){const e=document;return e.fullscreenElement??e.webkitFullscreenElement??null}_fullscreenChangeEventName(){return"onfullscreenchange"in document?"fullscreenchange":"webkitfullscreenchange"}_onFullscreenChange=()=>{this._applyActive()};_applyActive(){const e=null!==this._resolvedTarget&&this._fullscreenElement()===this._resolvedTarget;this._setActive(e)}_setActive(e){this._active!==e&&(this._active=e,this._target.dispatchEvent(new CustomEvent("wcs-fullscreen:change",{detail:{active:e},bubbles:!0})))}_setError(e){this._error=e}}class i extends HTMLElement{static hasConnectedCallbackPromise=!0;static observedAttributes=["target"];static wcBindable={...c.wcBindable,inputs:[{name:"target",attribute:"target"}],commands:c.wcBindable.commands};_core;_connectedCallbackPromise=Promise.resolve();constructor(){super(),this._core=new c(this)}get connectedCallbackPromise(){return this._connectedCallbackPromise}get target(){return this.getAttribute("target")??""}set target(e){this.setAttribute("target",e)}get active(){return this._core.active}get error(){return this._core.error}async requestFullscreen(){const{element:e}=this._resolveTarget();await this._core.requestFullscreen(e)}async exitFullscreen(){await this._core.exitFullscreen()}_resolveTarget(){const e=this.target;if("self"===e)return{element:this,display:"block"};if(""!==e){const t=this.getRootNode();return{element:this._safeQuery(t,e),display:"none"}}const t=this.firstElementChild;return t?{element:t,display:"contents"}:{element:this,display:"block"}}_safeQuery(e,t){try{return e.querySelector(t)}catch{return null}}_reresolve(){const{element:e,display:t}=this._resolveTarget();this.style.display=t,this._core.setTarget(e)}connectedCallback(){this._reresolve(),this._connectedCallbackPromise=this._core.observe()}disconnectedCallback(){this._core.dispose()}attributeChangedCallback(e,t,s){t!==s&&this.isConnected&&this._reresolve()}}function a(t){var s;t&&((s=t).tagNames&&Object.assign(e.tagNames,s.tagNames),r=null),customElements.get(n.tagNames.fullscreen)||customElements.define(n.tagNames.fullscreen,i)}export{c as FullscreenCore,i as WcsFullscreen,a as bootstrapFullscreen,l as getConfig};
2
+ //# sourceMappingURL=index.esm.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/core/FullscreenCore.ts","../src/components/Fullscreen.ts","../src/bootstrapFullscreen.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n fullscreen: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n fullscreen: \"wcs-fullscreen\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable } from \"../types.js\";\n\n/**\n * Headless Fullscreen API primitive. Unlike most wcstack IO nodes, this Core\n * does not operate on itself: it drives `requestFullscreen()` /\n * `exitFullscreen()` on a *referenced* Element that the Shell resolves via its\n * `target` attribute (docs/fullscreen-tag-design.md §0). The Core only ever\n * receives already-resolved `Element`s from its callers — it has no opinion on\n * how `target` selectors are parsed.\n *\n * `document.fullscreenElement` is a single document-wide value, so this Core\n * always compares against the *last element it resolved* (via\n * `requestFullscreen()`/`setTarget()`), never against \"is the document\n * fullscreen at all\" — that comparison is what keeps multiple concurrent\n * `<wcs-fullscreen>` instances from all reporting the same `active` value\n * (docs/fullscreen-tag-design.md §2.1, MUST).\n */\nexport class FullscreenCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"active\", event: \"wcs-fullscreen:change\", getter: (e: Event) => (e as CustomEvent).detail.active },\n ],\n commands: [\n { name: \"requestFullscreen\", async: true },\n { name: \"exitFullscreen\", async: true },\n ],\n };\n\n private _target: EventTarget;\n private _active: boolean = false;\n // Single error slot (§8): null means \"no recent failure\". Fullscreen's\n // gesture-rejection failure is a one-shot event, not a persistent state\n // machine like permission's 4-value surface — active/error are two\n // orthogonal, independently-observable axes.\n private _error: any = null;\n\n // The last Element this Core resolved via requestFullscreen()/setTarget().\n // Compared against document.fullscreenElement on every fullscreenchange so\n // each instance judges only its own target (§2.1). null means \"no target\n // resolved yet\" — active must stay false in that case.\n private _resolvedTarget: Element | null = null;\n\n // Generation guard (§6): Core-scoped (one per Core, not per-target),\n // mirroring fetch/upload. document.fullscreenElement is a single\n // document-wide slot, so at most one in-flight request/exit is meaningful\n // per Core at a time.\n private _gen = 0;\n\n // True once observe() has attached the document-level fullscreenchange\n // listener. Guards observe() so a redundant call does not double-subscribe;\n // dispose() resets it so a later observe() resumes cleanly.\n private _subscribed = false;\n\n // SSR (§10): no asynchronous probe to await — observe() completes\n // synchronously, so readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get ready(): Promise<void> {\n return this._ready;\n }\n\n get active(): boolean {\n return this._active;\n }\n\n get error(): any {\n return this._error;\n }\n\n /**\n * Update the resolved target without issuing a fullscreen request (e.g. the\n * Shell re-resolves `target` on attribute change / connect). Re-evaluates\n * `active` against the current `document.fullscreenElement` so the state\n * stays correct even if the target changed while already fullscreen.\n */\n setTarget(element: Element | null): void {\n this._resolvedTarget = element;\n this._applyActive();\n }\n\n // Lifecycle (§10/§3.5). Idempotent: a second observe() while already\n // subscribed is a no-op (no double listener). Synchronous overall (no probe\n // to await), so the returned promise is only for API uniformity with other\n // IO nodes.\n observe(): Promise<void> {\n if (!this._subscribed) {\n this._subscribed = true;\n document.addEventListener(this._fullscreenChangeEventName(), this._onFullscreenChange);\n }\n return this._ready;\n }\n\n dispose(): void {\n this._gen++;\n if (this._subscribed) {\n this._subscribed = false;\n document.removeEventListener(this._fullscreenChangeEventName(), this._onFullscreenChange);\n }\n }\n\n /**\n * Request fullscreen on `element`. never-throw (§3/§6): a missing API or a\n * rejected promise (e.g. a `TypeError` from a call outside a user\n * gesture, per the WHATWG Fullscreen spec's transient-activation check) is\n * caught and surfaced via `error`, never thrown. The caller\n * (Shell) is responsible for resolving `target` and for ensuring this is\n * invoked from within an actual user gesture — this Core cannot manufacture\n * one (docs/fullscreen-tag-design.md §3).\n */\n async requestFullscreen(element: Element | null): Promise<void> {\n const gen = ++this._gen;\n this._resolvedTarget = element;\n if (!element) {\n // Distinct from \"API is not supported\" (below): the Shell's `target`\n // selector did not resolve to any element (missing/typo'd selector).\n // Conflating the two previously misled users into thinking Fullscreen\n // itself was unsupported when only their selector was wrong.\n this._setError({ message: \"Fullscreen target could not be resolved.\" });\n return;\n }\n const fn = this._requestFullscreenFn(element);\n if (!fn) {\n this._setError({ message: \"Fullscreen API is not supported.\" });\n return;\n }\n try {\n await fn.call(element);\n if (gen !== this._gen) return; // stale: dispose()/superseding call ran\n this._setError(null);\n this._applyActive();\n } catch (e: any) {\n if (gen !== this._gen) return; // stale\n this._setError(e);\n }\n }\n\n /**\n * Exit fullscreen. Silent no-op (§7) when nothing is currently fullscreen or\n * the API is unsupported — both are treated as \"already achieved the exit\n * intent\", not as errors, keeping repeated calls safe and never-throw.\n */\n async exitFullscreen(): Promise<void> {\n // no-op checks come BEFORE the generation bump: a call that does nothing\n // must not supersede an in-flight requestFullscreen() — bumping first\n // would make the pending request's settle handling stale and silently\n // swallow its error/active updates.\n if (this._fullscreenElement() === null) return; // already not fullscreen: silent no-op\n const fn = this._exitFullscreenFn();\n if (!fn) return; // unsupported: silent no-op (semantically already \"not fullscreen\")\n const gen = ++this._gen;\n try {\n await fn();\n if (gen !== this._gen) return; // stale\n this._setError(null);\n this._applyActive();\n } catch (e: any) {\n if (gen !== this._gen) return; // stale\n this._setError(e);\n }\n }\n\n // --- API resolution layer (§4): call-time, never cached. Lets tests\n // install/remove the standard/legacy APIs freely and lets an unsupported\n // environment be detected correctly on every call. ---\n\n private _requestFullscreenFn(el: Element): (() => Promise<void>) | undefined {\n return this._elementFullscreenFn(el, \"requestFullscreen\")\n ?? this._elementFullscreenFn(el, \"webkitRequestFullscreen\");\n }\n\n // Resolve a fullscreen method for `el` WITHOUT a naive `el[name]` lookup.\n // A plain lookup walks the whole prototype chain — and <wcs-fullscreen>\n // itself declares a `requestFullscreen()` *command* method, so when the\n // resolved target is the Shell element (target=\"self\", or target omitted\n // with no children), the naive lookup would find the Shell's own command\n // instead of the platform API and recurse infinitely (stack overflow).\n // Instead: check the element's own properties (how tests install stubs —\n // happy-dom has no Fullscreen API — and how a deliberate per-element\n // monkey-patch would appear), then jump straight to Element.prototype,\n // where the platform defines the real methods. Both the standard and the\n // legacy webkit name go through this same resolution for symmetry.\n private _elementFullscreenFn(el: Element, name: string): (() => Promise<void>) | undefined {\n if (Object.prototype.hasOwnProperty.call(el, name)) {\n return (el as any)[name];\n }\n return (Element.prototype as any)[name];\n }\n\n private _exitFullscreenFn(): (() => Promise<void>) | undefined {\n const d = document as any;\n return d.exitFullscreen?.bind(document) ?? d.webkitExitFullscreen?.bind(document);\n }\n\n private _fullscreenElement(): Element | null {\n const d = document as any;\n return d.fullscreenElement ?? d.webkitFullscreenElement ?? null;\n }\n\n private _fullscreenChangeEventName(): string {\n return \"onfullscreenchange\" in document ? \"fullscreenchange\" : \"webkitfullscreenchange\";\n }\n\n // --- Internal ---\n\n private _onFullscreenChange = (): void => {\n this._applyActive();\n };\n\n // Re-derive `active` by comparing document.fullscreenElement (incl. legacy\n // fallback) against *this instance's* resolved target (§2/§2.1/§5). A null\n // resolved target always yields active=false — there is nothing for this\n // instance to claim as \"mine\".\n private _applyActive(): void {\n const next = this._resolvedTarget !== null && this._fullscreenElement() === this._resolvedTarget;\n this._setActive(next);\n }\n\n private _setActive(v: boolean): void {\n if (this._active === v) return; // same-value guard (§3.3 MUST)\n this._active = v;\n this._target.dispatchEvent(new CustomEvent(\"wcs-fullscreen:change\", {\n detail: { active: v },\n bubbles: true,\n }));\n }\n\n private _setError(error: any): void {\n this._error = error;\n }\n}\n","import { IWcBindable } from \"../types.js\";\nimport { FullscreenCore } from \"../core/FullscreenCore.js\";\n\n/**\n * `<wcs-fullscreen target=\"...\">` — declarative Fullscreen API control.\n *\n * Like `intersection`/`resize`, this Shell operates on a *referenced* element,\n * not itself (docs/fullscreen-tag-design.md §0): `target` resolves which\n * element `requestFullscreen()`/`exitFullscreen()` are invoked on, using the\n * exact same 3-mode resolution as `<wcs-intersect>`\n * (docs/fullscreen-tag-design.md §1):\n *\n * | `target` | operates on | display | use case |\n * |-----------------|-------------------------|-------------|--------------------------|\n * | omitted | first element child | `contents` | wrap a gallery image/video |\n * | `\"#hero\"` / sel | the matched element | `none` | point at a distant node |\n * | `\"self\"` | the element itself | `block` | fullscreen the wrapper |\n *\n * `requestFullscreen()` requires an active user gesture — this element cannot\n * manufacture one. Invoke the command from within a real click handler\n * (typically via the command-token protocol: this element subscribes with\n * `command.requestFullscreen: $command.<token>`, and a button emits the\n * token from its own click handler, e.g. `onclick: $command.<token>`).\n */\nexport class WcsFullscreen extends HTMLElement {\n // SSR (§10): the fullscreenchange subscription is established synchronously\n // on connect, but the Shell still exposes connectedCallbackPromise so the\n // state binder can await it uniformly across all IO nodes before\n // snapshotting.\n static hasConnectedCallbackPromise = true;\n\n static observedAttributes = [\"target\"];\n\n static wcBindable: IWcBindable = {\n ...FullscreenCore.wcBindable,\n inputs: [{ name: \"target\", attribute: \"target\" }],\n // Core の commands をそのまま継承(単一情報源)。network/intersection と同型。\n commands: FullscreenCore.wcBindable.commands,\n };\n\n private _core: FullscreenCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new FullscreenCore(this);\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Attribute accessors ---\n\n get target(): string {\n return this.getAttribute(\"target\") ?? \"\";\n }\n\n set target(value: string) {\n this.setAttribute(\"target\", value);\n }\n\n // --- Core delegated getters ---\n\n get active(): boolean {\n return this._core.active;\n }\n\n get error(): any {\n return this._core.error;\n }\n\n // --- Commands ---\n\n /**\n * Resolve `target` and request fullscreen on it. never-throw: an\n * unresolvable target or an unsupported/rejected API call are both\n * surfaced via `error`, never thrown (docs/fullscreen-tag-design.md §3/§6).\n */\n async requestFullscreen(): Promise<void> {\n const { element } = this._resolveTarget();\n await this._core.requestFullscreen(element);\n }\n\n async exitFullscreen(): Promise<void> {\n await this._core.exitFullscreen();\n }\n\n // --- Internal ---\n\n // Copied verbatim from <wcs-intersect> (Intersect.ts _resolveTarget/_safeQuery,\n // docs/fullscreen-tag-design.md §1): identical 3-mode resolution, only the\n // \"what to do with the resolved element\" step differs.\n private _resolveTarget(): { element: Element | null; display: string } {\n const target = this.target;\n if (target === \"self\") {\n return { element: this, display: \"block\" };\n }\n if (target !== \"\") {\n const scope = this.getRootNode() as Document | ShadowRoot;\n return { element: this._safeQuery(scope, target), display: \"none\" };\n }\n const child = this.firstElementChild;\n if (child) {\n return { element: child, display: \"contents\" };\n }\n return { element: this, display: \"block\" };\n }\n\n private _safeQuery(scope: Document | ShadowRoot, selector: string): Element | null {\n try {\n return scope.querySelector(selector);\n } catch {\n return null;\n }\n }\n\n private _reresolve(): void {\n const { element, display } = this._resolveTarget();\n this.style.display = display;\n this._core.setTarget(element);\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this._reresolve();\n this._connectedCallbackPromise = this._core.observe();\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n\n attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return;\n if (!this.isConnected) return;\n this._reresolve();\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapFullscreen(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsFullscreen } from \"./components/Fullscreen.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.fullscreen)) {\n customElements.define(config.tagNames.fullscreen, WcsFullscreen);\n }\n}\n"],"names":["_config","tagNames","fullscreen","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","FullscreenCore","EventTarget","static","protocol","version","properties","name","event","getter","e","detail","active","commands","async","_target","_active","_error","_resolvedTarget","_gen","_subscribed","_ready","Promise","resolve","constructor","target","super","this","ready","error","setTarget","element","_applyActive","observe","document","addEventListener","_fullscreenChangeEventName","_onFullscreenChange","dispose","removeEventListener","requestFullscreen","gen","_setError","message","fn","_requestFullscreenFn","call","exitFullscreen","_fullscreenElement","_exitFullscreenFn","el","_elementFullscreenFn","prototype","hasOwnProperty","Element","d","bind","webkitExitFullscreen","fullscreenElement","webkitFullscreenElement","next","_setActive","v","dispatchEvent","CustomEvent","bubbles","WcsFullscreen","HTMLElement","wcBindable","inputs","attribute","_core","_connectedCallbackPromise","connectedCallbackPromise","getAttribute","value","setAttribute","_resolveTarget","display","scope","getRootNode","_safeQuery","child","firstElementChild","selector","querySelector","_reresolve","style","connectedCallback","disconnectedCallback","attributeChangedCallback","_name","oldValue","newValue","isConnected","bootstrapFullscreen","userConfig","partialConfig","assign","customElements","get","define"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@wcstack/fullscreen",
3
+ "version": "1.16.0",
4
+ "description": "Declarative Fullscreen API component for Web Components. Framework-agnostic requestFullscreen()/exitFullscreen() control via wc-bindable-protocol.",
5
+ "type": "module",
6
+ "main": "./dist/index.esm.js",
7
+ "module": "./dist/index.esm.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.esm.js"
13
+ },
14
+ "./auto": "./dist/auto.min.js"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "clean": "rimraf dist .tsc-out",
21
+ "build": "rimraf dist .tsc-out && tsc && rollup -c",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
24
+ "test:coverage": "vitest run --coverage",
25
+ "lint": "eslint src",
26
+ "version:patch": "npm version patch",
27
+ "version:minor": "npm version minor",
28
+ "version:major": "npm version major",
29
+ "prepublishOnly": "npm run build && npm run test:coverage"
30
+ },
31
+ "keywords": [
32
+ "web-components",
33
+ "fullscreen",
34
+ "requestfullscreen",
35
+ "custom-elements",
36
+ "wc-bindable",
37
+ "declarative",
38
+ "zero-dependencies",
39
+ "framework-agnostic"
40
+ ],
41
+ "author": "mogera551",
42
+ "homepage": "https://wcstack.github.io",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/wcstack/wcstack.git",
46
+ "directory": "packages/fullscreen"
47
+ },
48
+ "bugs": {
49
+ "url": "https://github.com/wcstack/wcstack/issues"
50
+ },
51
+ "license": "MIT",
52
+ "devDependencies": {
53
+ "@eslint/js": "^9.39.1",
54
+ "@rollup/plugin-terser": "^0.4.4",
55
+ "@rollup/plugin-typescript": "^11.1.6",
56
+ "@vitest/coverage-v8": "^4.0.15",
57
+ "@vitest/ui": "^4.0.15",
58
+ "eslint": "^9.39.1",
59
+ "globals": "^16.5.0",
60
+ "happy-dom": "^20.0.11",
61
+ "rimraf": "^6.0.1",
62
+ "rollup": "^4.22.4",
63
+ "rollup-plugin-dts": "^6.1.1",
64
+ "rollup-plugin-copy": "^3.5.0",
65
+ "tslib": "^2.8.1",
66
+ "typescript": "^5.9.3",
67
+ "typescript-eslint": "^8.49.0",
68
+ "vitest": "^4.0.15"
69
+ }
70
+ }