@wcstack/state 1.3.17 → 1.3.18

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 CHANGED
@@ -8,6 +8,28 @@
8
8
 
9
9
  CDNスクリプトはカスタム要素の定義を登録するだけ — ロード時にはそれ以外何も起きない。`<wcs-state>` 要素がDOMに接続されたとき、状態ソースを読み取り、兄弟要素の `data-wcs` バインディングを走査し、リアクティビティを構築する。すべての初期化は要素のライフサイクルが駆動する。あなたのコードではなく。
10
10
 
11
+ ## 4ステップで動く
12
+
13
+ ```html
14
+ <!-- 1. CDN を読み込む -->
15
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
16
+
17
+ <!-- 2. <wcs-state> タグを書く -->
18
+ <wcs-state>
19
+ <!-- 3. 状態オブジェクトを定義する -->
20
+ <script type="module">
21
+ export default {
22
+ message: "Hello, World!"
23
+ };
24
+ </script>
25
+ </wcs-state>
26
+
27
+ <!-- 4. data-wcs 属性でバインドする -->
28
+ <div data-wcs="textContent: message"></div>
29
+ ```
30
+
31
+ これだけ。ビルドなし、初期化コードなし、フレームワークなし。
32
+
11
33
  ## 特徴
12
34
 
13
35
  - **宣言的データバインディング** — `data-wcs` 属性によるプロパティ / テキスト / イベント / 構造バインディング
@@ -1077,12 +1099,21 @@ bootstrapState();
1077
1099
 
1078
1100
  ```
1079
1101
  bootstrapState()
1080
- ├── registerComponents() // <wcs-state> カスタム要素を登録
1081
- └── registerHandler() // DOMContentLoaded ハンドラ
1082
- ├── waitForStateInitialize() // 全 <wcs-state> の読み込み待機
1083
- ├── convertMustacheToComments() // {{ }} → コメントノードに変換
1084
- ├── collectStructuralFragments() // for/if テンプレートを収集
1085
- └── initializeBindings() // DOM 走査、data-wcs 解析、バインディング設定
1102
+ └── registerComponents() // <wcs-state> カスタム要素を登録
1103
+
1104
+ <wcs-state> connectedCallback
1105
+ ├── _initializeBindWebComponent() // bind-component: 親コンポーネントから状態を取得
1106
+ ├── _initialize() // 状態をロード (state属性 / src / json / script / API)
1107
+ └── setStateElementByName() // WeakMap<Node, Map<name, element>> に登録
1108
+ │ └── (rootNode への初回登録時)
1109
+ │ └── queueMicrotask → buildBindings()
1110
+ ├── _callStateConnectedCallback() // $connectedCallback が定義されていれば呼び出し
1111
+
1112
+ buildBindings(root)
1113
+ ├── waitForStateInitialize() // 全 <wcs-state> の initializePromise を待機
1114
+ ├── convertMustacheToComments() // {{ }} → コメントノードに変換
1115
+ ├── collectStructuralFragments() // for/if テンプレートを収集
1116
+ └── initializeBindings() // DOM 走査、data-wcs 解析、バインディング設定
1086
1117
  ```
1087
1118
 
1088
1119
  ### リアクティビティフロー
package/README.md CHANGED
@@ -8,6 +8,28 @@ That's what `<wcs-state>` and `data-wcs` explore. One CDN import, zero dependenc
8
8
 
9
9
  The CDN script only registers the custom element definition — nothing else happens at load time. When a `<wcs-state>` element connects to the DOM, it reads its state source, scans sibling elements for `data-wcs` bindings, and wires up reactivity. All initialization is driven by the element's lifecycle, not by your code.
10
10
 
11
+ ## 4 Steps to Reactive HTML
12
+
13
+ ```html
14
+ <!-- 1. Load the CDN -->
15
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
16
+
17
+ <!-- 2. Write a <wcs-state> tag -->
18
+ <wcs-state>
19
+ <!-- 3. Define your state object -->
20
+ <script type="module">
21
+ export default {
22
+ message: "Hello, World!"
23
+ };
24
+ </script>
25
+ </wcs-state>
26
+
27
+ <!-- 4. Bind with data-wcs attributes -->
28
+ <div data-wcs="textContent: message"></div>
29
+ ```
30
+
31
+ That's it. No build, no bootstrap code, no framework.
32
+
11
33
  ## Features
12
34
 
13
35
  - **Declarative data binding** — `data-wcs` attribute for property / text / event / structural binding
@@ -1077,12 +1099,21 @@ bootstrapState();
1077
1099
 
1078
1100
  ```
1079
1101
  bootstrapState()
1080
- ├── registerComponents() // Register <wcs-state> custom element
1081
- └── registerHandler() // DOMContentLoaded handler
1082
- ├── waitForStateInitialize() // Wait for all <wcs-state> to load
1083
- ├── convertMustacheToComments() // {{ }} comment nodes
1084
- ├── collectStructuralFragments() // Collect for/if templates
1085
- └── initializeBindings() // Walk DOM, parse data-wcs, set up bindings
1102
+ └── registerComponents() // Register <wcs-state> custom element
1103
+
1104
+ <wcs-state> connectedCallback
1105
+ ├── _initializeBindWebComponent() // bind-component: get state from parent component
1106
+ ├── _initialize() // Load state (state attr / src / json / script / API)
1107
+ └── setStateElementByName() // Register to WeakMap<Node, Map<name, element>>
1108
+ │ └── (first registration per rootNode)
1109
+ │ └── queueMicrotask → buildBindings()
1110
+ ├── _callStateConnectedCallback() // Call $connectedCallback if defined
1111
+
1112
+ buildBindings(root)
1113
+ ├── waitForStateInitialize() // Wait for all <wcs-state> initializePromise
1114
+ ├── convertMustacheToComments() // {{ }} → comment nodes
1115
+ ├── collectStructuralFragments() // Collect for/if templates
1116
+ └── initializeBindings() // Walk DOM, parse data-wcs, set up bindings
1086
1117
  ```
1087
1118
 
1088
1119
  ### Reactivity Flow
package/dist/index.esm.js CHANGED
@@ -1910,28 +1910,31 @@ function clearAbsoluteStateAddressByBinding(binding) {
1910
1910
  }
1911
1911
 
1912
1912
  const cache = new WeakMap();
1913
- function isCustomElement(node) {
1914
- let value = cache.get(node);
1915
- if (value !== undefined) {
1916
- return value;
1913
+ function getCustomElement(node) {
1914
+ const cached = cache.get(node);
1915
+ if (cached !== undefined) {
1916
+ return cached;
1917
1917
  }
1918
+ let value = null;
1918
1919
  try {
1919
1920
  if (node.nodeType !== Node.ELEMENT_NODE) {
1920
- return value = false;
1921
+ return value;
1921
1922
  }
1922
1923
  const element = node;
1923
- if (element.tagName.includes("-")) {
1924
- return value = true;
1924
+ const tagName = element.tagName.toLowerCase();
1925
+ if (tagName.includes("-")) {
1926
+ return value = tagName;
1925
1927
  }
1926
1928
  if (element.hasAttribute("is")) {
1927
- if (element.getAttribute("is")?.includes("-")) {
1928
- return value = true;
1929
+ const is = element.getAttribute("is");
1930
+ if (is.includes("-")) {
1931
+ return value = is;
1929
1932
  }
1930
1933
  }
1931
- return value = false;
1934
+ return value;
1932
1935
  }
1933
1936
  finally {
1934
- cache.set(node, value ?? false);
1937
+ cache.set(node, value);
1935
1938
  }
1936
1939
  }
1937
1940
 
@@ -2967,9 +2970,9 @@ function applyChange(binding, context) {
2967
2970
  if (binding.bindingType === "event") {
2968
2971
  return;
2969
2972
  }
2970
- if (isCustomElement(binding.replaceNode)) {
2971
- const element = binding.replaceNode;
2972
- if (customElements.get(element.tagName.toLowerCase()) === undefined) {
2973
+ const customTag = getCustomElement(binding.replaceNode);
2974
+ if (customTag) {
2975
+ if (customElements.get(customTag) === undefined) {
2973
2976
  // cutomElement側の初期化を期待
2974
2977
  return;
2975
2978
  }
@@ -5494,7 +5497,8 @@ class State extends HTMLElement {
5494
5497
  : parentNode instanceof Element
5495
5498
  ? parentNode
5496
5499
  : null;
5497
- if (boundComponent === null || !isCustomElement(boundComponent)) {
5500
+ const customTagName = boundComponent ? getCustomElement(boundComponent) : null;
5501
+ if (boundComponent === null || customTagName === null) {
5498
5502
  raiseError(`"bind-component" requires <${config.tagNames.state}> to be a direct child of a custom element.`);
5499
5503
  }
5500
5504
  // LightDOMの場合、名前空間が上位スコープと共有されるためnameが必須
@@ -5502,7 +5506,7 @@ class State extends HTMLElement {
5502
5506
  raiseError(`"bind-component" in Light DOM requires a "name" attribute to avoid namespace conflicts with the parent scope.`);
5503
5507
  }
5504
5508
  const boundComponentStateProp = this.getAttribute("bind-component");
5505
- await customElements.whenDefined(boundComponent.tagName.toLowerCase());
5509
+ await customElements.whenDefined(customTagName.toLowerCase());
5506
5510
  // data-wcs属性がある場合は、上位の状態によりbinding情報の設定が完了するまで待機する
5507
5511
  if (boundComponent.hasAttribute(config.bindAttributeName)) {
5508
5512
  await waitInitializeBinding(boundComponent);