@mzzsfy/dsh-toast 0.1.2 → 0.2.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.md +4 -3
- package/package.json +1 -1
- package/src/client.js +17 -3
- package/test/mount.test.mjs +27 -1
- package/test/toast.test.mjs +12 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
DSH 全局浮出通知 Toast 库:多条并存栈式展示,自动消失与常驻确认两种生命周期。供各插件发送全局操作反馈与事件通知,替代各插件自写通知 UI。
|
|
4
4
|
|
|
5
|
-
**本包是普通 npm 依赖,不是 dsh 插件**:不声明 `dsh.bundle.patch
|
|
5
|
+
**本包是普通 npm 依赖,不是 dsh 插件**:不声明 `dsh.bundle.patch`,不自带 cordis.patch.yml,无需也不应 `dsh plugin add`,且**不进 profile 表层 manifest 依赖行**(插件市场已装列表因此不显示本包)。安装与装载链:消费插件 `dependencies` 声明本包 → pnpm(hoisted 布局)作为传递依赖实体安装到顶层 node_modules,dsh 启动 fallback 沿 bundles 依赖闭包补链兜底;开发态由 dev-link 的 junction 指向仓库工作副本保热更。消费插件在自身 `cordis.patch.yml` 中代挂本包宿主占位条目使 client 进入客户端模块表。机制推导与实证见 [MECHANISM.md](./MECHANISM.md)。
|
|
6
6
|
|
|
7
7
|
## 消费方接入
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ DSH 全局浮出通知 Toast 库:多条并存栈式展示,自动消失与常驻
|
|
|
10
10
|
|
|
11
11
|
```json
|
|
12
12
|
{
|
|
13
|
-
"dependencies": { "@mzzsfy/dsh-toast": "^0.1.
|
|
13
|
+
"dependencies": { "@mzzsfy/dsh-toast": "^0.1.2" },
|
|
14
14
|
"dsh": {
|
|
15
15
|
"client": {
|
|
16
16
|
"external": ["@mzzsfy/dsh-toast/client"]
|
|
@@ -40,7 +40,7 @@ toast('回合完成', { holdMs: 6 * 1000 }) // 自定义展示期
|
|
|
40
40
|
|
|
41
41
|
## API
|
|
42
42
|
|
|
43
|
-
- `show(text, opts)` → `id`:入栈一条通知。`opts.kind` 为 `'info' | 'ok' | 'error'`(非法归 `info`,默认深色 / 成功绿 / 错误红);`opts.sticky` 真值常驻不自动消失,渲染「知道了」按钮;`opts.holdMs` 有限正值自定义展示期(默认 4 秒,非有限值或超出 setTimeout 钳位上界回落默认)
|
|
43
|
+
- `show(text, opts)` → `id`:入栈一条通知。`opts.kind` 为 `'info' | 'ok' | 'error'`(非法归 `info`,默认深色 / 成功绿 / 错误红);`opts.sticky` 真值常驻不自动消失,渲染「知道了」按钮;`opts.holdMs` 有限正值自定义展示期(默认 4 秒,非有限值或超出 setTimeout 钳位上界回落默认);`opts.onClick` 函数使整卡可点(带 pointer 样式),点击即触发回调并消失,「知道了」按钮显式关闭不连带触发。`text` 非字符串或纯空白时忽略并返回 `null`
|
|
44
44
|
- `dismiss(id)`:移除指定条目并撤销其自动消失计时,幂等
|
|
45
45
|
- `mount()`:显式挂载渲染容器(一般无需调用,首次 `show` 惰性自举)
|
|
46
46
|
- `__test`:非公开 API,仅供本包测试驱动 store 消费,无兼容承诺,消费方禁用
|
|
@@ -51,6 +51,7 @@ toast('回合完成', { holdMs: 6 * 1000 }) // 自定义展示期
|
|
|
51
51
|
- Given 栈内已有 4 条,When 再入栈一条,Then 最旧条目立即移除(含 sticky,新通知优先),被裁条目的自动消失计时同步撤销
|
|
52
52
|
- Given `show(text, {sticky: true})`,Then 通知常驻,点「知道了」或 `dismiss(id)` 后消失
|
|
53
53
|
- Given `kind: 'error'`,Then 红色变体渲染
|
|
54
|
+
- Given `show(text, {onClick})`,Then 整卡可点,点击即消失并触发 `onClick`;Given 该条目另有 sticky,点「知道了」仅关闭不触发 `onClick`
|
|
54
55
|
- Given 首次 `show`,Then 渲染容器与样式惰性挂载(容器直挂 body,不受设置页全屏层 z-index 遮挡)
|
|
55
56
|
- Given HMR 重载产生同 id 旧容器,When 新代首次挂载,Then 旧容器移除、新容器就位;旧代闭包再调 `show`/`mount` 时发现在位容器属更新代际即退避,不拆新代容器
|
|
56
57
|
- Given 容器在场而样式节点被外部移除,When 再次 `show`,Then 样式补挂(容器与样式同级自愈)
|
package/package.json
CHANGED
package/src/client.js
CHANGED
|
@@ -36,6 +36,7 @@ window.__ModuleLoader__.load({
|
|
|
36
36
|
' animation:dsh-toast-in 0.18s ease-out; max-width:520px; max-height:60vh; overflow:auto; overflow-wrap:anywhere; display:flex; align-items:safe center; gap:10px; }',
|
|
37
37
|
'.dsh-toast--ok { background:var(--dsw-alias-state-success-primary, #1a7f37); color:var(--dsw-alias-label-primary-inverted, #fff); }',
|
|
38
38
|
'.dsh-toast--error { background:var(--dsw-alias-state-error-primary, #d93025); color:#fff; }',
|
|
39
|
+
'.dsh-toast--click { cursor:pointer; }',
|
|
39
40
|
'.dsh-toast__close { border:0; background:transparent; cursor:pointer; color:inherit;',
|
|
40
41
|
' font:var(--dsw-font-xs-strong-13, 13px system-ui, sans-serif); padding:0 2px; opacity:.8; }',
|
|
41
42
|
'.dsh-toast__close:hover { opacity:1; }',
|
|
@@ -91,7 +92,8 @@ window.__ModuleLoader__.load({
|
|
|
91
92
|
function show(text, opts) {
|
|
92
93
|
if (typeof text !== 'string' || text.trim() === '') return null
|
|
93
94
|
const sticky = Boolean(opts && opts.sticky)
|
|
94
|
-
const
|
|
95
|
+
const onClick = typeof (opts && opts.onClick) === 'function' ? opts.onClick : undefined
|
|
96
|
+
const entry = { id: ++seq, text, kind: normalizeKind(opts && opts.kind), sticky, onClick, timer: undefined }
|
|
95
97
|
const merged = items.concat(entry)
|
|
96
98
|
if (merged.length > TOAST_MAX) {
|
|
97
99
|
for (const dropped of merged.slice(0, merged.length - TOAST_MAX)) clearTimer(dropped)
|
|
@@ -179,10 +181,22 @@ window.__ModuleLoader__.load({
|
|
|
179
181
|
|
|
180
182
|
function ToastItem(props) {
|
|
181
183
|
const item = props.item
|
|
182
|
-
|
|
184
|
+
// 携带 onClick 的条目整卡可点:触发回调并消失;「知道了」按钮先阻断冒泡,
|
|
185
|
+
// 显式关闭不得连带触发直达动作
|
|
186
|
+
const activate = item.onClick === undefined
|
|
187
|
+
? undefined
|
|
188
|
+
: () => { dismiss(item.id); item.onClick() }
|
|
189
|
+
return h('div', {
|
|
190
|
+
className: 'dsh-toast dsh-toast--' + item.kind + (activate !== undefined ? ' dsh-toast--click' : ''),
|
|
191
|
+
role: 'alert',
|
|
192
|
+
onClick: activate,
|
|
193
|
+
},
|
|
183
194
|
h('span', null, item.text),
|
|
184
195
|
item.sticky
|
|
185
|
-
? h('button', {
|
|
196
|
+
? h('button', {
|
|
197
|
+
className: 'dsh-toast__close',
|
|
198
|
+
onClick: (event) => { event.stopPropagation(); dismiss(item.id) },
|
|
199
|
+
}, '知道了')
|
|
186
200
|
: null,
|
|
187
201
|
)
|
|
188
202
|
}
|
package/test/mount.test.mjs
CHANGED
|
@@ -212,6 +212,7 @@ test('渲染产物:栈容器类名、变体类、role 与 sticky 按钮接线',
|
|
|
212
212
|
const okTree = okItem.type(okItem.props)
|
|
213
213
|
assert.equal(okTree.props.className, 'dsh-toast dsh-toast--ok')
|
|
214
214
|
assert.equal(okTree.props.role, 'alert')
|
|
215
|
+
assert.equal(okTree.props.onClick, undefined, '无 onClick 条目整卡不可点')
|
|
215
216
|
assert.equal(okTree.children[1], null, '非 sticky 无按钮')
|
|
216
217
|
|
|
217
218
|
const stickyItem = items.find((vnode) => vnode.props.item.text === '常驻')
|
|
@@ -220,10 +221,35 @@ test('渲染产物:栈容器类名、变体类、role 与 sticky 按钮接线',
|
|
|
220
221
|
const button = stickyTree.children[1]
|
|
221
222
|
assert.equal(button.type, 'button')
|
|
222
223
|
assert.equal(button.props.className, 'dsh-toast__close')
|
|
223
|
-
button.props.onClick()
|
|
224
|
+
button.props.onClick({ stopPropagation() {} })
|
|
224
225
|
assert.equal(mod.__test.getItems().some((item) => item.id === stickyId), false, '按钮接线 dismiss 生效')
|
|
225
226
|
})
|
|
226
227
|
|
|
228
|
+
test('渲染产物:onClick 条目整卡可点,点击即消失并触发回调;按钮关闭不连带', () => {
|
|
229
|
+
const state = makeState()
|
|
230
|
+
const { mod, roots } = loadRenderModule(state)
|
|
231
|
+
let activated = false
|
|
232
|
+
mod.show('可点通知', { onClick: () => { activated = true } })
|
|
233
|
+
const root = roots[roots.length - 1]
|
|
234
|
+
const tree = root.vnode.type()
|
|
235
|
+
const vnode = tree.children.flat().find((entry) => entry.props.item.text === '可点通知')
|
|
236
|
+
const card = vnode.type(vnode.props)
|
|
237
|
+
assert.equal(card.props.className, 'dsh-toast dsh-toast--info dsh-toast--click', '可点条目带标记类')
|
|
238
|
+
card.props.onClick()
|
|
239
|
+
assert.equal(activated, true, '点击触发回调')
|
|
240
|
+
assert.equal(mod.__test.getItems().length, 0, '点击后条目消失')
|
|
241
|
+
// sticky + onClick:「知道了」只关闭,不触发直达
|
|
242
|
+
let stickyActivated = false
|
|
243
|
+
mod.show('常驻可点', { sticky: true, onClick: () => { stickyActivated = true } })
|
|
244
|
+
const tree2 = roots[roots.length - 1].vnode.type()
|
|
245
|
+
const vnode2 = tree2.children.flat().find((entry) => entry.props.item.text === '常驻可点')
|
|
246
|
+
const card2 = vnode2.type(vnode2.props)
|
|
247
|
+
const button = card2.children[1]
|
|
248
|
+
button.props.onClick({ stopPropagation() {} })
|
|
249
|
+
assert.equal(stickyActivated, false, '显式关闭不触发直达')
|
|
250
|
+
assert.equal(mod.__test.getItems().length, 0)
|
|
251
|
+
})
|
|
252
|
+
|
|
227
253
|
test('样式内容不一致原位替换:节点不重建,内容与写入计数还原', () => {
|
|
228
254
|
const state = makeState()
|
|
229
255
|
const { mod } = loadModule(state)
|
package/test/toast.test.mjs
CHANGED
|
@@ -154,6 +154,18 @@ test('kind: error 合法直通', () => {
|
|
|
154
154
|
assert.equal(mod.__test.getItems()[0].kind, 'error')
|
|
155
155
|
})
|
|
156
156
|
|
|
157
|
+
test('onClick:函数入栈携带,非函数剔除;activate 语义由消费方经快照 item 驱动', () => {
|
|
158
|
+
const mod = loadModule()
|
|
159
|
+
const hit = () => {}
|
|
160
|
+
mod.__test.show('可点', { onClick: hit })
|
|
161
|
+
mod.__test.show('不可点', { onClick: 'not-a-function' })
|
|
162
|
+
mod.__test.show('缺省')
|
|
163
|
+
const items = mod.__test.getItems()
|
|
164
|
+
assert.equal(items[0].onClick, hit)
|
|
165
|
+
assert.equal(items[1].onClick, undefined)
|
|
166
|
+
assert.equal(items[2].onClick, undefined)
|
|
167
|
+
})
|
|
168
|
+
|
|
157
169
|
test('纯空白 text 拒绝,非空白正常入栈', () => {
|
|
158
170
|
const mod = loadModule()
|
|
159
171
|
assert.equal(mod.__test.show(' '), null)
|