Explorar o código

修改一些layer的使用方式

robert hai 6 horas
pai
achega
1e74c24b52
Modificáronse 9 ficheiros con 332 adicións e 175 borrados
  1. 14 15
      Guide.md
  2. 120 10
      animal.js
  3. 3 3
      doc/layer/list.html
  4. 157 114
      doc/layer/test_dom_steps.html
  5. 12 12
      doc/layer/test_static_fire.html
  6. 2 2
      doc/module.md
  7. 13 9
      layer.js
  8. 8 8
      test.html
  9. 3 2
      xjs.js

+ 14 - 15
Guide.md

@@ -69,21 +69,17 @@ go run main.go build path/to/xjs.js
 
 ### 3.1 全局导出(必须稳定)
 
-对外承诺以下全局变量(避免污染)
+对外承诺以下全局变量:
 
-- **`window.xjs`**:主入口(推荐统一使用)
-- **`window.animal`**:兼容别名(与 `xjs` 等价)
+- **`window.$`**:主入口(jQuery 风格选择器 + 链式能力)
+- **`window.xjs`**:兼容别名(与 `$` 等价)
+- **`window.animal`**:兼容别名(与 `$` 等价)
 - **`window.Layer`**:Layer 构造/工厂入口
 
-可选行为:
-
-- **不默认导出 `$`**
-- 只有当业务方在引入前显式设置 `window.XJS_GLOBAL_DOLLAR = true` 时,才会在 `$` 未被占用的前提下导出 `$ = xjs`
-
 ### 3.2 API 设计原则(统一风格)
 
-- **单入口**:所有能力尽量从 `xjs(selector)` 返回的 `Selection` 链式对象进入
-- **强约束、少魔法**:避免“隐式全局配置”;如需全局配置,集中在 `xjs.config`(未来可扩展
+- **单入口**:所有能力尽量从 `$(selector)` 返回的 `Selection` 链式对象进入
+- **强约束、少魔法**:避免“隐式全局配置”;如需全局配置,集中在 `$.config`(`xjs/animal` 为别名)
 - **兼容性优先级**:能用 WAAPI 就用 WAAPI;不支持时优雅降级(必要时 fallback 到 requestAnimationFrame)
 - **返回值规范**:
   - 异步行为返回 `Promise`(例如 `animate().then(...)`)
@@ -109,7 +105,7 @@ go run main.go build path/to/xjs.js
 
 ### 4.3 选择器与 Selection 约定
 
-- `xjs(selector)` 返回 `Selection`(继承 Array 的可链式集合)
+- `$(selector)` 返回 `Selection`(继承 Array 的可链式集合)
 - Selection 实例方法命名尽量用动词:`animate / draw / inViewAnimate / layer / unlayer`
 - 新增能力优先做成 Selection 方法,而不是新增全局函数
 
@@ -186,6 +182,9 @@ go run main.go build path/to/xjs.js
 - **示例页面结构**:
   - **演示区块在上**、代码区块在下(tabs + code),便于直接看效果
   - 演示区块应包含最小可操作按钮/交互
+- **代码展示必须一致**:
+  - HTML/CSS/JS 的代码块必须与实际页面/示例一致
+  - 禁止使用 `...`、伪代码或省略写法(如隐藏 DOM 块也要完整展示)
 - **必须显式声明引用的库(页面内)**:
   - 样式:`doc/demo.css`(示例通用样式)、`xjs.css`(组件运行时样式)
   - 脚本:`doc/highlight_css.js`(代码高亮)
@@ -206,7 +205,7 @@ go run main.go build path/to/xjs.js
 
 - 新增 Selection 方法:`Selection.prototype.xxx = function (...) { ... }`
 - 或在 `Selection` 类体内增加方法(更推荐,保持结构统一)
-- 如需新增静态能力:挂在 `xjs.xxx = ...`(需要同时考虑 `animal` 别名)
+- 如需新增静态能力:挂在 `$.xxx = ...`(`xjs/animal` 为别名)
 
 ### 7.2 新增 UI 组件(独立文件 + CSS )
 
@@ -276,7 +275,7 @@ Layer 现在支持把“页面里已有的 DOM(可默认隐藏)”挂载到
 // 页面中一个默认隐藏的 DOM
 // <div id="my_form" style="display:none">...</div>
 
-xjs.layer({
+$.layer({
   title: 'Edit profile',
   dom: '#my_form',
   showCancelButton: true,
@@ -293,7 +292,7 @@ Layer 支持“同一弹窗内”的步骤流:点击确认按钮不关闭,
 推荐写法(链式):
 
 ```js
-xjs.Layer.$({
+$.layer({
   icon: 'info',
   closeOnClickOutside: false,
   closeOnEsc: false
@@ -317,7 +316,7 @@ xjs.Layer.$({
     return { plan: popup.querySelector('select[name="plan"]').value };
   }
 })
-.fire()
+.run()
 .then((res) => {
   if (res.isConfirmed) {
     // res.value 是每一步 preConfirm 的返回值数组

+ 120 - 10
animal.js

@@ -1,7 +1,16 @@
 (function (global, factory) {
-  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
-    typeof define === 'function' && define.amd ? define(factory) :
-      (global.animal = factory());
+  if (typeof exports === 'object' && typeof module !== 'undefined') {
+    module.exports = factory();
+  } else if (typeof define === 'function' && define.amd) {
+    define(factory);
+  } else {
+    const api = factory();
+    // Main global: $ (jQuery-like selector + utilities)
+    global.$ = api;
+    // Backward-compatible aliases
+    global.xjs = api;
+    global.animal = api;
+  }
 }(this, (function () {
   'use strict';
 
@@ -73,7 +82,11 @@
           }
           
           // Prefer builder API if present, fallback to static fire.
-          if (LayerCtor.$ && isFunc(LayerCtor.$)) return LayerCtor.$(opts).fire();
+          if (LayerCtor.$ && isFunc(LayerCtor.$)) {
+            const inst = LayerCtor.$(opts);
+            return (inst.run ? inst.run() : inst.fire());
+          }
+          if (LayerCtor.run && isFunc(LayerCtor.run)) return LayerCtor.run(opts);
           if (LayerCtor.fire && isFunc(LayerCtor.fire)) return LayerCtor.fire(opts);
         };
         
@@ -95,6 +108,59 @@
       });
       return this;
     }
+
+    // jQuery-style click handler
+    // - click(fn): bind handler with `this` as element
+    // - click(layerInstance): bind Layer instance to click (auto fire)
+    // - click(options): open Layer with options on click
+    click(handler) {
+      const LayerCtor =
+        (typeof window !== 'undefined' && window.Layer) ? window.Layer :
+        (typeof globalThis !== 'undefined' && globalThis.Layer) ? globalThis.Layer :
+        (typeof Layer !== 'undefined' ? Layer : null);
+
+      this.forEach((el) => {
+        if (!isEl(el)) return;
+        let cb = handler;
+
+        if (handler && (typeof handler.run === 'function' || typeof handler.fire === 'function')) {
+          // Layer instance: defer auto-fire and trigger on click
+          try { handler._deferAuto = true; } catch {}
+          cb = (e) => {
+            try { handler._triggerEl = el; handler._triggerEvent = e; } catch {}
+            try { return handler.run ? handler.run() : handler.fire(); } catch {}
+          };
+        } else if (!isFunc(handler)) {
+          // Options object: open Layer on click
+          const opts = handler;
+          cb = (e) => {
+            if (!LayerCtor) {
+              console.warn('Layer module not loaded.');
+              return;
+            }
+            let finalOpts = opts;
+            if (isFunc(opts)) {
+              finalOpts = opts.call(el, e);
+            }
+            if (finalOpts && typeof finalOpts === 'object' && !('triggerEl' in finalOpts)) {
+              finalOpts.triggerEl = el;
+            }
+            if (LayerCtor.$ && isFunc(LayerCtor.$)) {
+              const inst = LayerCtor.$(finalOpts);
+              return (inst.run ? inst.run() : inst.fire());
+            }
+            if (LayerCtor.run && isFunc(LayerCtor.run)) return LayerCtor.run(finalOpts);
+            if (LayerCtor.fire && isFunc(LayerCtor.fire)) return LayerCtor.fire(finalOpts);
+          };
+        } else {
+          cb = (e) => handler.call(el, e);
+        }
+
+        el.addEventListener('click', cb);
+      });
+
+      return this;
+    }
   }
   
   const $ = (targets) => Selection.from(toArray(targets));
@@ -1369,16 +1435,60 @@
     }
   });
 
-  // Shortcut for Layer.fire or new Layer()
-  // Allows $.fire({ title: 'Hi' }) or $.layer({ title: 'Hi' })
-  animal.fire = (options) => {
+  // Shortcut for Layer.run or new Layer()
+  // Allows $.run({ title: 'Hi' })
+  animal.run = (options) => {
     const L = animal.Layer;
-    if (L) return (L.fire ? L.fire(options) : new L(options).fire());
+    if (L) return (L.run ? L.run(options) : (L.fire ? L.fire(options) : new L(options).run()));
     console.warn('Layer module not loaded.');
     return Promise.reject('Layer module not loaded');
   };
-  // 'layer' alias for static usage
-  animal.layer = animal.fire;
+  // Backward-compatible alias
+  animal.fire = (options) => animal.run(options);
+  // $.layer(): returns a Layer instance with auto-fire for simple popups
+  animal.layer = (options) => {
+    const L = animal.Layer;
+    if (!L) {
+      console.warn('Layer module not loaded.');
+      return Promise.reject('Layer module not loaded');
+    }
+    const inst = (L.$ && isFunc(L.$)) ? L.$(options) : (new L()).config(options);
+    const rawRun = inst.run ? inst.run.bind(inst) : (inst.fire ? inst.fire.bind(inst) : null);
+    if (rawRun) {
+      inst.run = (opts) => {
+        const p = rawRun(opts);
+        inst._autoPromise = p;
+        return p;
+      };
+    }
+    inst._autoPromise = null;
+    // Auto-fire on next tick if no steps are added (unless deferred/bound)
+    setTimeout(() => {
+      if (!inst || inst._autoPromise) return;
+      if (inst._deferAuto) return;
+      if (inst._flowSteps && inst._flowSteps.length) return;
+      if (rawRun) inst.run();
+    }, 0);
+    // Promise-like convenience for $.layer(...).then(...)
+    inst.then = (...args) => {
+      if (!inst._autoPromise) {
+        if (inst._flowSteps && inst._flowSteps.length) {
+          if (rawRun) inst.run();
+        } else {
+          if (rawRun) inst.run();
+        }
+      }
+      return inst._autoPromise ? inst._autoPromise.then(...args) : Promise.resolve().then(...args);
+    };
+    inst.catch = (...args) => inst.then(null, ...args);
+    inst.finally = (...args) => {
+      if (!inst._autoPromise && rawRun) inst.run();
+      return inst._autoPromise ? inst._autoPromise.finally(...args) : Promise.resolve().finally(...args);
+    };
+    // Backward-compatible alias
+    if (!inst.fire && inst.run) inst.fire = inst.run.bind(inst);
+    return inst;
+  };
 
   return animal;
 

+ 3 - 3
doc/layer/list.html

@@ -101,21 +101,21 @@
 
       <div class="card" data-content="layer/test_confirm_flow.html" onclick="selectItem('test_confirm_flow.html', this)">
         <div class="card-preview">
-          <div class="preview-pill">xjs.layer(...).then(res =&gt; ...)</div>
+          <div class="preview-pill">$.layer(...).then(res =&gt; ...)</div>
         </div>
         <div class="card-footer">Confirm flow (success / error)</div>
       </div>
 
       <div class="card" data-content="layer/test_selection_layer_dataset.html" onclick="selectItem('test_selection_layer_dataset.html', this)">
         <div class="card-preview">
-          <div class="preview-pill">xjs('.btn').layer() + data-layer-*</div>
+          <div class="preview-pill">$('.btn').layer() + data-layer-*</div>
         </div>
         <div class="card-footer">Selection.layer() + dataset</div>
       </div>
 
       <div class="card" data-content="layer/test_static_fire.html" onclick="selectItem('test_static_fire.html', this)">
         <div class="card-preview">
-          <div class="preview-pill">Layer.fire(...) / Layer.$(...).fire()</div>
+          <div class="preview-pill">Layer.run(...) / Layer.$(...).run()</div>
         </div>
         <div class="card-footer">Static API / Builder API</div>
       </div>

+ 157 - 114
doc/layer/test_dom_steps.html

@@ -85,8 +85,8 @@
     <div class="box-container">
       <div class="demo-visual">
         <div class="row">
-          <button class="btn primary" onclick="openDom()">Open DOM popup</button>
-          <button class="btn success" onclick="openWizard()">Open step wizard</button>
+          <button id="openDom" class="btn primary">Open DOM popup</button>
+          <button id="openWizard" class="btn success">Open step wizard</button>
         </div>
         <div class="hint">
           Tip: step wizard disables backdrop/ESC close to avoid accidental dismiss.
@@ -103,69 +103,105 @@
       </div>
 
       <pre id="js-code" class="code-view active">// 1) Single popup: show a hidden DOM block inside Layer
-xjs.layer({
-  title: 'DOM content',
-  dom: '#demo_dom_block',
-  showCancelButton: true,
-  confirmButtonText: 'OK',
-  cancelButtonText: 'Close'
+$('#openDom').click(function () {
+  $.layer({
+    title: 'DOM content',
+    dom: '#demo_dom_block',
+    showCancelButton: true,
+    confirmButtonText: 'OK',
+    cancelButtonText: 'Close'
+  });
 });
 
 // 2) Step flow (wizard) - no icon during steps; final summary is allowed
-xjs.Layer.$({
-  closeOnClickOutside: false,
-  closeOnEsc: false,
-  popupAnimation: false,
-  confirmButtonColor: '#3085d6',
-  cancelButtonColor: '#444'
-})
-.step({
-  title: 'Step 1: Basic info',
-  dom: '#wizard_step_1',
-  showCancelButton: true,
-  cancelButtonText: 'Cancel',
-  preConfirm(popup) {
-    const name = popup.querySelector('input[name="name"]').value.trim();
-    const err = popup.querySelector('[data-error]');
-    if (!name) { err.textContent = 'Please enter your name.'; return false; }
-    err.textContent = '';
-    return { name };
-  }
-})
-.step({
-  title: 'Step 2: Preferences',
-  dom: '#wizard_step_2',
-  preConfirm(popup) {
-    const plan = popup.querySelector('select[name="plan"]').value;
-    return { plan };
-  }
-})
-.step({
-  title: 'Summary',
-  icon: 'success',
-  isSummary: true,
-  html: '&lt;div class="hint"&gt;Click OK to finish.&lt;/div&gt;'
-})
-.fire()
-.then((res) =&gt; {
-  if (res.isConfirmed) {
-    xjs.layer({
-      title: 'Done',
-      icon: 'success',
-      popupAnimation: false,
-      replace: true,
-      html: '&lt;pre&gt;' + JSON.stringify(res.value, null, 2) + '&lt;/pre&gt;'
-    });
-  }
+$('#openWizard').click(function () {
+  $.layer({
+    closeOnClickOutside: false,
+    closeOnEsc: false,
+    popupAnimation: false,
+    confirmButtonColor: '#3085d6',
+    cancelButtonColor: '#444'
+  })
+  .step({
+    title: 'Step 1: Basic info',
+    dom: '#wizard_step_1',
+    showCancelButton: true,
+    cancelButtonText: 'Cancel',
+    preConfirm(popup) {
+      const name = popup.querySelector('input[name="name"]').value.trim();
+      const err = popup.querySelector('[data-error]');
+      if (!name) { err.textContent = 'Please enter your name.'; return false; }
+      err.textContent = '';
+      return { name };
+    }
+  })
+  .step({
+    title: 'Step 2: Preferences',
+    dom: '#wizard_step_2',
+    preConfirm(popup) {
+      const plan = popup.querySelector('select[name="plan"]').value;
+      return { plan };
+    }
+  })
+  .step({
+    title: 'Summary',
+    icon: 'success',
+    isSummary: true,
+    html: '&lt;div class="hint"&gt;Click OK to finish.&lt;/div&gt;'
+  })
+  .run()
+  .then((res) =&gt; {
+    if (res.isConfirmed) {
+      $.layer({
+        title: 'Done',
+        icon: 'success',
+        popupAnimation: false,
+        replace: true,
+        html: '&lt;pre&gt;' + JSON.stringify(res.value, null, 2) + '&lt;/pre&gt;'
+      });
+    }
+  });
 });</pre>
 
       <pre id="html-code" class="html-view">&lt;button class="btn primary" onclick="openDom()"&gt;Open DOM popup&lt;/button&gt;
 &lt;button class="btn success" onclick="openWizard()"&gt;Open step wizard&lt;/button&gt;
 
 &lt;!-- Hidden DOM blocks (default display:none) --&gt;
-&lt;div id="demo_dom_block" class="hidden-dom"&gt;...&lt;/div&gt;
-&lt;div id="wizard_step_1" class="hidden-dom"&gt;...&lt;/div&gt;
-&lt;div id="wizard_step_2" class="hidden-dom"&gt;...&lt;/div&gt;</pre>
+&lt;div id="demo_dom_block" class="hidden-dom"&gt;
+  &lt;div class="form"&gt;
+    &lt;div class="field"&gt;
+      &lt;label&gt;Quick note&lt;/label&gt;
+      &lt;input placeholder="This input lives in a hidden DOM block" value="Hello from DOM!"&gt;
+    &lt;/div&gt;
+    &lt;div class="hint" style="margin-top: -4px;"&gt;
+      This DOM node is moved into the popup and restored on close.
+    &lt;/div&gt;
+  &lt;/div&gt;
+&lt;/div&gt;
+
+&lt;div id="wizard_step_1" class="hidden-dom"&gt;
+  &lt;div class="form"&gt;
+    &lt;div class="field"&gt;
+      &lt;label&gt;Name&lt;/label&gt;
+      &lt;input name="name" placeholder="Your name"&gt;
+    &lt;/div&gt;
+    &lt;div class="error" data-error&gt;&lt;/div&gt;
+  &lt;/div&gt;
+&lt;/div&gt;
+
+&lt;div id="wizard_step_2" class="hidden-dom"&gt;
+  &lt;div class="form"&gt;
+    &lt;div class="field"&gt;
+      &lt;label&gt;Plan&lt;/label&gt;
+      &lt;select name="plan"&gt;
+        &lt;option value="free"&gt;Free&lt;/option&gt;
+        &lt;option value="pro"&gt;Pro&lt;/option&gt;
+        &lt;option value="team"&gt;Team&lt;/option&gt;
+      &lt;/select&gt;
+    &lt;/div&gt;
+    &lt;div class="hint"&gt;Confirm will finish the wizard.&lt;/div&gt;
+  &lt;/div&gt;
+&lt;/div&gt;</pre>
 
       <pre id="css-code" class="css-view">/* This page hides DOM blocks by default:
 .hidden-dom { display: none; }
@@ -264,65 +300,68 @@ then restore it back (and restore display/hidden state) on close. */</pre>
       }
     }
 
-    function openDom() {
-      xjs.layer({
-        title: 'DOM content',
-        dom: '#demo_dom_block',
-        popupAnimation: true,
-        showCancelButton: true,
-        confirmButtonText: 'OK',
-        cancelButtonText: 'Close'
+    function bindDemoHandlers() {
+      if (typeof $ !== 'function') return;
+      $('#openDom').click(function () {
+        $.layer({
+          title: 'DOM content',
+          dom: '#demo_dom_block',
+          popupAnimation: true,
+          showCancelButton: true,
+          confirmButtonText: 'OK',
+          cancelButtonText: 'Close'
+        });
       });
-    }
 
-    function openWizard() {
-      xjs.Layer.$({
-        closeOnClickOutside: false,
-        closeOnEsc: false,
-        popupAnimation: false,
-        confirmButtonColor: '#3085d6',
-        cancelButtonColor: '#444'
-      })
-      .step({
-        title: 'Step 1: Basic info',
-        dom: '#wizard_step_1',
-        showCancelButton: true,
-        cancelButtonText: 'Cancel',
-        preConfirm(popup) {
-          const name = popup.querySelector('input[name="name"]').value.trim();
-          const err = popup.querySelector('[data-error]');
-          if (!name) { err.textContent = 'Please enter your name.'; return false; }
-          err.textContent = '';
-          return { name };
-        }
-      })
-      .step({
-        title: 'Step 2: Preferences',
-        dom: '#wizard_step_2',
-        preConfirm(popup) {
-          const plan = popup.querySelector('select[name="plan"]').value;
-          return { plan };
-        }
-      })
-      .step({
-        title: 'Summary',
-        icon: 'success',
-        isSummary: true,
-        html: '<div class="hint">Click OK to finish.</div>'
-      })
-      .fire()
-      .then((res) => {
-        if (res.isConfirmed) {
-          xjs.layer({
-            title: 'Done',
-            icon: 'success',
-            popupAnimation: false,
-            replace: true,
-            html: '<pre>' + JSON.stringify(res.value, null, 2) + '</pre>'
-          });
-        } else {
-          xjs.layer({ title: 'Dismissed', icon: 'info', popupAnimation: false, replace: true, text: 'Flow cancelled' });
-        }
+      $('#openWizard').click(function () {
+        $.layer({
+          closeOnClickOutside: false,
+          closeOnEsc: false,
+          popupAnimation: false,
+          confirmButtonColor: '#3085d6',
+          cancelButtonColor: '#444'
+        })
+        .step({
+          title: 'Step 1: Basic info',
+          dom: '#wizard_step_1',
+          showCancelButton: true,
+          cancelButtonText: 'Cancel',
+          preConfirm(popup) {
+            const name = popup.querySelector('input[name="name"]').value.trim();
+            const err = popup.querySelector('[data-error]');
+            if (!name) { err.textContent = 'Please enter your name.'; return false; }
+            err.textContent = '';
+            return { name };
+          }
+        })
+        .step({
+          title: 'Step 2: Preferences',
+          dom: '#wizard_step_2',
+          preConfirm(popup) {
+            const plan = popup.querySelector('select[name="plan"]').value;
+            return { plan };
+          }
+        })
+        .step({
+          title: 'Summary',
+          icon: 'success',
+          isSummary: true,
+          html: '<div class="hint">Click OK to finish.</div>'
+        })
+      .run()
+        .then((res) => {
+          if (res.isConfirmed) {
+            $.layer({
+              title: 'Done',
+              icon: 'success',
+              popupAnimation: false,
+              replace: true,
+              html: '<pre>' + JSON.stringify(res.value, null, 2) + '</pre>'
+            });
+          } else {
+            $.layer({ title: 'Dismissed', icon: 'info', popupAnimation: false, replace: true, text: 'Flow cancelled' });
+          }
+        });
       });
     }
 
@@ -355,8 +394,12 @@ then restore it back (and restore display/hidden state) on close. */</pre>
       //   (without cache-busting) and may overwrite the fresh one after our load finishes.
       // - Load sources directly with cache-bust to guarantee we use the current workspace code.
       await loadScript(base + 'animal.js?_=' + DEV_CACHE_BUST);
-      try { if (window.animal && !window.xjs) window.xjs = window.animal; } catch {}
+      try {
+        if (window.animal && !window.xjs) window.xjs = window.animal;
+        if (window.xjs && !window.$) window.$ = window.xjs;
+      } catch {}
       await loadScript(base + 'layer.js?_=' + DEV_CACHE_BUST);
+      bindDemoHandlers();
     })().catch((e) => {
       try { console.error(e); } catch {}
     });

+ 12 - 12
doc/layer/test_static_fire.html

@@ -41,14 +41,14 @@
 
     <h1>Static API / Builder API</h1>
     <p class="description">
-      Layer supports <code class="inline">Layer.fire()</code> and <code class="inline">Layer.$(...).fire()</code> (builder style), and returns a Promise result.
+      Layer supports <code class="inline">Layer.run()</code> and <code class="inline">Layer.$(...).run()</code> (builder style), and returns a Promise result.
     </p>
 
     <div class="box-container">
       <div class="demo-visual">
         <div class="row">
-          <button class="btn primary" onclick="openFire()">Layer.fire()</button>
-          <button class="btn" onclick="openBuilder()">Builder fire()</button>
+          <button class="btn primary" onclick="openFire()">Layer.run()</button>
+          <button class="btn" onclick="openBuilder()">Builder run()</button>
         </div>
         <div class="hint">
           Promise result is printed to <span class="mono">console</span>.
@@ -64,17 +64,17 @@
         </div>
       </div>
 
-      <pre id="js-code" class="code-view active">// 1) Static fire
-Layer.fire({ title: 'Hello', text: 'Layer.fire(...)', icon: 'info' });
+      <pre id="js-code" class="code-view active">// 1) Static run
+Layer.run({ title: 'Hello', text: 'Layer.run(...)', icon: 'info' });
 
 // 2) Builder style
 Layer.$()
   .config({ title: 'Confirm?', icon: 'warning', showCancelButton: true })
-  .fire()
+  .run()
   .then((res) => console.log(res));</pre>
 
-      <pre id="html-code" class="html-view">&lt;button class="btn primary"&gt;Layer.fire()&lt;/button&gt;
-&lt;button class="btn"&gt;Layer.$().config().fire()&lt;/button&gt;</pre>
+      <pre id="html-code" class="html-view">&lt;button class="btn primary"&gt;Layer.run()&lt;/button&gt;
+&lt;button class="btn"&gt;Layer.$().config().run()&lt;/button&gt;</pre>
 
       <pre id="css-code" class="css-view">/* Local styles for buttons only */</pre>
 
@@ -125,8 +125,8 @@ Layer.$()
     }
 
     function openFire() {
-      Layer.fire({ title: 'Hello', text: 'Layer.fire(...)', icon: 'info', popupAnimation: true })
-        .then((res) => console.log('[Layer.fire] result:', res));
+      Layer.run({ title: 'Hello', text: 'Layer.run(...)', icon: 'info', popupAnimation: true })
+        .then((res) => console.log('[Layer.run] result:', res));
     }
 
     function openBuilder() {
@@ -138,10 +138,10 @@ Layer.$()
           popupAnimation: true,
           showCancelButton: true
         })
-        .fire()
+        .run()
         .then((res) => {
           console.log('[Layer.builder] result:', res);
-          if (res.isConfirmed) xjs.layer({ title: 'Confirmed', icon: 'success', popupAnimation: true });
+          if (res.isConfirmed) $.layer({ title: 'Confirmed', icon: 'success', popupAnimation: true });
         });
     }
 

+ 2 - 2
doc/module.md

@@ -238,7 +238,7 @@ Prev/Next 规则:
 
 ### I. Layer 集成(联动展示库特性)
 
-> `Selection.layer()` / `Selection.unlayer()` + `xjs.fire()` / `xjs.layer()`
+> `Selection.layer()` / `Selection.unlayer()` + `$.run()` / `$.layer()`
 
 - ✅ `examples/layer.html`(已有示例,但后续会补充“更像产品”的玩法)
 - ✅ `layer/list.html`(第二列:Layer 分组卡片导航)
@@ -246,7 +246,7 @@ Prev/Next 规则:
 - ✅ `layer/test_icons_svg_animation.html`(SVG icon:ring + mark 描边动画,体验接近 SweetAlert)
 - ✅ `layer/test_confirm_flow.html`(确认流程:warning → success/info 串联)
 - ✅ `layer/test_selection_layer_dataset.html`(Selection.layer + data-layer-*)
-- ✅ `layer/test_static_fire.html`(Layer.fire / Layer.$ builder)
+- ✅ `layer/test_static_fire.html`(Layer.run / Layer.$ builder)
 
 > 说明:Layer 的成功/失败等 icon 已切换为 **内联 SVG**,并使用 `animal.js` 的 `draw()` 技术(strokeDashoffset)做描边动画,同时用 spring easing 做弹性进入。
 

+ 13 - 9
layer.js

@@ -139,22 +139,24 @@
     static get isProxy() { return true; }
     
     // Static entry point
-    static fire(options) {
+    static run(options) {
       const instance = new Layer();
       return instance._fire(options);
     }
+    // Backward-compatible alias
+    static fire(options) { return Layer.run(options); }
     
     // Chainable entry point (builder-style)
     // Example:
-    //   Layer.$({ title: 'Hi' }).fire().then(...)
-    //   Layer.$().config({ title: 'Hi' }).fire()
+    //   Layer.$({ title: 'Hi' }).run().then(...)
+    //   Layer.$().config({ title: 'Hi' }).run()
     static $(options) {
       const instance = new Layer();
       if (options !== undefined) instance.config(options);
       return instance;
     }
     
-    // Chainable config helper (does not render until `.fire()` is called)
+    // Chainable config helper (does not render until `.run()` is called)
     config(options = {}) {
       // Support the same shorthand as Layer.fire(title, text, icon)
       options = normalizeOptions(options, arguments[1], arguments[2]);
@@ -164,7 +166,7 @@
 
     // Add a single step (chainable)
     // Usage:
-    //   Layer.$().step({ title:'A', dom:'#step1' }).step({ title:'B', dom:'#step2' }).fire()
+    //   Layer.$().step({ title:'A', dom:'#step1' }).step({ title:'B', dom:'#step2' }).run()
     step(options = {}) {
       if (!this._flowSteps) this._flowSteps = [];
       this._flowSteps.push(normalizeOptions(options, arguments[1], arguments[2]));
@@ -181,11 +183,11 @@
 
     // Convenience static helper: Layer.flow([steps], baseOptions?)
     static flow(steps = [], baseOptions = {}) {
-      return Layer.$(baseOptions).steps(steps).fire();
+      return Layer.$(baseOptions).steps(steps).run();
     }
     
     // Instance entry point (chainable)
-    fire(options) {
+    run(options) {
       // Flow mode: if configured via .step()/.steps(), ignore per-call options and use steps
       if (this._flowSteps && this._flowSteps.length) {
         if (options !== undefined && options !== null) {
@@ -198,6 +200,8 @@
       const merged = (options === undefined) ? (this.params || {}) : options;
       return this._fire(merged);
     }
+    // Backward-compatible alias
+    fire(options) { return this.run(options); }
 
     _fire(options = {}) {
       options = normalizeOptions(options, arguments[1], arguments[2]);
@@ -264,10 +268,10 @@
     }
 
     static _getXjs() {
-      // Prefer xjs, fallback to animal (compat)
+      // Prefer $ (main), fallback to xjs/animal (compat)
       const g = (typeof window !== 'undefined') ? window : (typeof globalThis !== 'undefined' ? globalThis : null);
       if (!g) return null;
-      const x = g.xjs || g.animal;
+      const x = g.$ || g.xjs || g.animal;
       return (typeof x === 'function') ? x : null;
     }
 

+ 8 - 8
test.html

@@ -62,15 +62,15 @@
         }
 
         function testBasic() {
-            Layer.fire('Hello World');
+            Layer.run('Hello World');
         }
 
         function testSuccess() {
-            Layer.fire('Good job!', 'You clicked the button!', 'success');
+            Layer.run('Good job!', 'You clicked the button!', 'success');
         }
 
         function testError() {
-            Layer.fire({
+            Layer.run({
                 icon: 'error',
                 title: 'Oops...',
                 text: 'Something went wrong!',
@@ -78,7 +78,7 @@
         }
 
         function testWarning() {
-            Layer.fire({
+            Layer.run({
                 title: 'Are you sure?',
                 text: "You won't be able to revert this!",
                 icon: 'warning',
@@ -88,7 +88,7 @@
                 confirmButtonText: 'Yes, delete it!'
             }).then((result) => {
                 if (result.isConfirmed) {
-                    Layer.fire(
+                    Layer.run(
                         'Deleted!',
                         'Your file has been deleted.',
                         'success'
@@ -98,7 +98,7 @@
         }
 
         function testConfirm() {
-            Layer.fire({
+            Layer.run({
                 title: 'Do you want to save the changes?',
                 showCancelButton: true,
                 confirmButtonText: 'Save',
@@ -112,13 +112,13 @@
         }
 
         function testChaining() {
-            Layer.fire({
+            Layer.run({
                 title: 'Step 1',
                 text: 'Proceed to step 2?',
                 showCancelButton: true
             }).then((result) => {
                 if (result.isConfirmed) {
-                    return Layer.fire({
+                    return Layer.run({
                         title: 'Step 2',
                         text: 'Last step!',
                         icon: 'success'

+ 3 - 2
xjs.js

@@ -33,8 +33,9 @@
   const ensureGlobals = () => {
     try {
       if (g.animal && !g.xjs) g.xjs = g.animal;
-      // Optional: export `$` only when explicitly requested
-      if (g.XJS_GLOBAL_DOLLAR && !g.$ && g.xjs) g.$ = g.xjs;
+      if (g.xjs && !g.$) g.$ = g.xjs;
+      if (g.$ && !g.xjs) g.xjs = g.$;
+      if (g.$ && !g.animal) g.animal = g.$;
     } catch {}
   };