sodialog 0.1.11 → 0.1.14

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 CHANGED
@@ -34,6 +34,18 @@ openOffcanvas({
34
34
  animation: 'slide',
35
35
  content: '<p>这是 Offcanvas</p>',
36
36
  })
37
+
38
+ // Toast
39
+ import { toast } from 'sodialog'
40
+
41
+ toast({
42
+ title: '保存成功',
43
+ content: '配置已更新',
44
+ placement: 'top-end',
45
+ variant: 'success',
46
+ duration: 2500,
47
+ maxVisible: 3,
48
+ })
37
49
  ```
38
50
 
39
51
  ## API
@@ -50,7 +62,7 @@ openOffcanvas({
50
62
  - `animation?: 'slide' | 'fade' | 'zoom'` (默认 `fade`)
51
63
  - `useModal?: boolean` (默认 `true`,`true` 使用 `showModal()`,`false` 使用 `show()`)
52
64
  - `draggable?: boolean` (默认 `false`)
53
- - `dragHandle?: 'header' | 'title' | 'body' | 'panel' | string` (默认 `header`,也可传 CSS 选择器)
65
+ - `dragHandle?: ('header' | 'title' | 'body' | 'panel' | string) | Array<'header' | 'title' | 'body' | 'panel' | string>` (默认 `header`,支持传数组开启多拖动区域,也可传 CSS 选择器)
54
66
  - `autoFitSize?: boolean` (默认 `true`,会根据 body 内容变化自动扩/缩尺寸,例如图片加载完成后)
55
67
  - `scrollMode?: 'body' | 'hybrid' | 'viewport' | 'none'` (默认 `body`)
56
68
  - `hybridSwitchRatio?: number` (默认 `1.35`,仅 `scrollMode: 'hybrid'` 时生效,最小值 `1`)
@@ -60,11 +72,30 @@ openOffcanvas({
60
72
  - `autoFitMinHeight?: number` (默认 `160`)
61
73
  - `confirmText?: string`
62
74
  - `cancelText?: string`
75
+ - `footerButtons?: SoDialogFooterButton[]`(自定义底部按钮。未传时默认“取消 + 确认”)
76
+ - `hideFooter?: boolean`(默认 `false`,可隐藏底部按钮区)
77
+ - `footerAlign?: 'start' | 'center' | 'end' | 'between'`(默认 `end`)
63
78
  - `confirmAction?: 'hide' | 'destroy'`(默认 `hide`;显式传入 `id` 时默认 `destroy`)
64
79
  - `closeOnBackdrop?: boolean` (默认 `true`)
65
80
  - `closeOnEsc?: boolean` (默认 `true`)
66
81
  - `onConfirm?: () => void`
67
82
  - `onCancel?: () => void`
83
+ - `onAction?: (context) => void`(监听所有 footer 按钮动作)
84
+ - `handle.setFooterButtons(buttons): void`(运行时整体替换 footer 按钮)
85
+ - `handle.updateFooterButton(id, updates): boolean`(按 id 更新某个 footer 按钮)
86
+ - `handle.onAction(listener): () => void`(追加动作监听,返回取消监听函数)
87
+
88
+ `SoDialogFooterButton` 字段:
89
+
90
+ - `id?: string`(动作标识,推荐传)
91
+ - `label: string | Node`
92
+ - `role?: 'confirm' | 'cancel' | 'custom'`
93
+ - `variant?: 'primary' | 'outline' | 'danger' | 'success' | 'ghost' | 'link'`
94
+ - `action?: 'none' | 'hide' | 'destroy'`
95
+ - `className?: string`
96
+ - `disabled?: boolean`
97
+ - `attrs?: Record<string, string>`
98
+ - `onClick?: (context) => void | boolean | Promise<void | boolean>`(返回 `false` 可阻止后续默认动作)
68
99
 
69
100
  说明:
70
101
 
@@ -72,6 +103,38 @@ openOffcanvas({
72
103
  - `取消/关闭` 语义是 `dialog.close()`(隐藏)
73
104
  - `confirmAction: 'destroy'` 语义是 `dialog.remove()`(销毁)
74
105
 
106
+ 示例(自定义 footer 按钮、动作监听、运行时更新):
107
+
108
+ ```ts
109
+ const handle = openModal({
110
+ title: '订单操作',
111
+ content: '<p>请选择一个动作</p>',
112
+ footerAlign: 'between',
113
+ footerButtons: [
114
+ { id: 'help', label: '帮助', variant: 'link', action: 'none' },
115
+ { id: 'cancel', label: '取消', role: 'cancel', variant: 'outline' },
116
+ { id: 'delete', label: '删除', variant: 'danger', action: 'destroy' },
117
+ ],
118
+ onAction: ({ action }) => {
119
+ console.log('global action:', action)
120
+ },
121
+ })
122
+
123
+ const off = handle.onAction(({ action, dialog }) => {
124
+ if (action === 'help') {
125
+ dialog.querySelector('.sod-body')?.insertAdjacentHTML('beforeend', '<p>帮助信息已展开</p>')
126
+ }
127
+ })
128
+
129
+ handle.updateFooterButton('delete', { label: '确认删除', disabled: false })
130
+ handle.setFooterButtons([
131
+ { id: 'close', label: '关闭', role: 'cancel', variant: 'ghost' },
132
+ { id: 'confirm', label: '提交', role: 'confirm', variant: 'primary' },
133
+ ])
134
+
135
+ off()
136
+ ```
137
+
75
138
  ### `openOffcanvas(options)`
76
139
 
77
140
  在 `openModal` 参数基础上新增:
@@ -92,17 +155,71 @@ openOffcanvas({ title: 'Bottom', placement: 'bottom', animation: 'zoom', content
92
155
 
93
156
  通用入口,`options.kind` 可为 `modal` 或 `offcanvas`。
94
157
 
158
+ ### `toast(options)` / `SoToast.show(options)`
159
+
160
+ - `content: string | Node`
161
+ - `title?: string`
162
+ - `placement?: 'top-start' | 'top-center' | 'top-end' | 'bottom-start' | 'bottom-center' | 'bottom-end'`(默认 `top-end`)
163
+ - `variant?: 'default' | 'info' | 'success' | 'warning' | 'danger'`(默认 `default`)
164
+ - `duration?: number | false`(默认 `3000`,`false` 表示不自动消失)
165
+ - `showProgress?: boolean`(默认 `true`,显示自动消失倒计时进度条)
166
+ - `maxVisible?: number`(默认 `3`,按同位置队列生效)
167
+ - `closable?: boolean`(默认 `true`)
168
+ - `pauseOnHover?: boolean`(默认 `true`)
169
+ - `pauseOnWindowBlur?: boolean`(默认 `false`,切换窗口时暂停倒计时)
170
+ - `duplicateStrategy?: 'update' | 'ignore' | 'restart-timer' | 'stack'`(默认 `update`)
171
+ - `onShown?: (handle) => void`
172
+ - `onClose?: (reason, handle) => void`
173
+
174
+ 队列与控制:
175
+
176
+ - 超过 `maxVisible` 的消息会进入队列,前面的 toast 关闭后自动补位
177
+ - 相同 `id` 再次调用会按 `duplicateStrategy` 处理
178
+ - `update`: 更新内容与配置;若 `placement` 改变会迁移到新位置
179
+ - `ignore`: 保留已有 toast,忽略本次重复调用
180
+ - `restart-timer`: 更新并强制重启倒计时
181
+ - `stack`: 自动生成新 id,保留已有 toast 并叠加新 toast
182
+ - 返回 `handle`,支持 `close / update / pause / resume`
183
+ - 可用 `SoToast.clear(placement?)` 清空指定位置或全部 toast
184
+ - 可用 `SoToast.closeAll()` 清空全部 toast
185
+ - 便捷方法:`SoToast.success/error/info/warning`
186
+
95
187
  ## 开发
96
188
 
97
189
  ```bash
98
190
  npm run dev
99
191
  npm run lint
100
192
  npm run build
193
+ npm run docs:changelog
101
194
  ```
102
195
 
196
+ ## GitHub Pages 首页
197
+
198
+ 本仓库已将文档主页入口放在 `index.html + src/main.ts`,用于展示开发文档与使用说明。
199
+
200
+ - 本地构建主页:`npm run build:demo`
201
+ - 产物目录:`dist-pages/`
202
+ - 线上部署:`.github/workflows/pages.yml`(推送到 `master` 自动部署)
203
+ - 仓库设置:`Settings -> Pages -> Source` 选择 `GitHub Actions`
204
+
205
+ 默认线上地址:`https://sohophp.github.io/sodialog/`
206
+
207
+ ## 文档体系
208
+
209
+ - `README.md`:使用方式、API、发布流程总览
210
+ - `CHANGELOG.md`:版本变更记录(按 git tag 自动生成)
211
+ - `RELEASE_CHECKLIST.md`:发布前人工检查清单
212
+
213
+ ### 文档自动更新
214
+
215
+ - 本地可执行:`npm run docs:changelog`
216
+ - 仓库内置工作流:`.github/workflows/docs-changelog.yml`
217
+ - 当推送版本 tag(`v*.*.*`)时,工作流会自动重建 `CHANGELOG.md` 并回推到 `master`
218
+
103
219
  Demo 中已包含:
104
220
 
105
221
  - 预设默认值:`center`、`zoom`、`medium`、`showModal`、不可拖动、自动适配
222
+ - 拖动区域支持多选(`header/title/body/panel` 可组合)
106
223
  - `Modal ID` 留空自动生成,输入后可复用唤醒同 ID
107
224
  - Modal 内切换不同尺寸图片,验证 `autoFitSize` 自动扩缩
108
225
  - 包含超大图(`xlarge`)、超长单词、超宽表格的溢出测试
@@ -110,6 +227,10 @@ Demo 中已包含:
110
227
  - 子窗口包含多种表单元素(input/select/checkbox/textarea)演示
111
228
  - 新增 Markdown 编辑器子窗口(工具栏插入、实时预览)
112
229
  - 编辑器支持“仅编辑 / 编辑 + 预览”模式切换
230
+ - Modal 新增“启用自定义 Footer API”开关(可切换默认按钮与自定义按钮)
231
+ - Footer 按钮支持样式变体演示(`link / outline / danger / success / ghost`)
232
+ - Footer 动作统一监听(`onAction`)与事件日志输出
233
+ - 运行时按钮更新演示(`handle.updateFooterButton` / `handle.setFooterButtons`)
113
234
 
114
235
  ## 发布到 NPM
115
236
 
@@ -129,16 +250,22 @@ Demo 中已包含:
129
250
 
130
251
  1. 在 npm 包页面启用 Trusted Publishing,并绑定 GitHub 仓库与该工作流
131
252
  2. 确保工作流权限包含 `id-token: write`(本仓库已配置)
253
+ 3. 本地先做版本校验(tag 必须和 `package.json` 一致)
254
+
255
+ ```bash
256
+ npm run release:check -- vX.Y.Z
257
+ ```
132
258
 
133
259
  说明:当前工作流优先使用 `NPM_AUTOMATION_TOKEN` 发布;未配置时回退到 Trusted Publishing(OIDC)。
134
260
 
135
- - 若使用 token 路径,`NPM_AUTOMATION_TOKEN` 必须是 npm Automation Token。
261
+ - 若使用 token 路径,`NPM_AUTOMATION_TOKEN` 请使用 npm Granular access token(Write 权限),并启用 `Bypass 2FA`,否则 CI 发布会报 `EOTP`。
136
262
  - 若使用 Trusted Publishing 路径,需先在 npm 包设置里完成仓库和 workflow 绑定,否则发布阶段可能出现 `E404`。
137
263
 
138
264
  推荐发布命令:
139
265
 
140
266
  ```bash
141
267
  npm version patch --no-git-tag-version
268
+ npm run release:check -- vX.Y.Z
142
269
  git add -A
143
270
  git commit -m "release: vX.Y.Z"
144
271
  git tag -a vX.Y.Z -m "vX.Y.Z"
Binary file
package/dist/logo.ico ADDED
Binary file
package/dist/sodialog.css CHANGED
@@ -1 +1 @@
1
- .sod-dialog{border:0;padding:0;margin:auto;background:transparent;max-width:none;max-height:none;overflow:visible}.sod-dialog::backdrop{background:#0000007f}.sod-panel{box-sizing:border-box;background:#fff;color:#212529;border-radius:.5rem;box-shadow:0 .5rem 1rem #00000040;min-width:min(520px,calc(100vw - 2rem));max-width:min(520px,calc(100vw - 2rem))}.sod-modal .sod-panel{position:relative;margin:0}.sod-modal[open]{width:100vw;height:100vh;display:flex;justify-content:center;align-items:center}.sod-modal[open].sod-modal-viewport-scroll{align-items:flex-start;overflow:auto;padding:1rem 0}.sod-modal .sod-panel.sod-modal-pos-center{align-self:center}.sod-modal .sod-panel.sod-modal-pos-top{align-self:flex-start;margin-top:1rem}.sod-modal .sod-panel.sod-modal-pos-bottom{align-self:flex-end;margin-bottom:1rem}.sod-modal .sod-panel.sod-modal-autofit{min-width:0;width:auto;height:auto;max-width:calc(100vw - 2rem);max-height:calc(100vh - 2rem);display:flex;flex-direction:column}.sod-modal .sod-panel.sod-modal-draggable{touch-action:none}.sod-modal .sod-panel.sod-is-dragging{animation:none!important;transition:none!important}.sod-modal .sod-panel .sod-drag-handle{cursor:move;-webkit-user-select:none;user-select:none}.sod-modal .sod-panel .sod-drag-handle.is-dragging{cursor:grabbing}.sod-modal .sod-panel.sod-modal-anim-fade{animation:sod-fade-in .18s ease-out}.sod-modal .sod-panel.sod-modal-anim-fade.is-closing{animation:sod-fade-out .18s ease-in forwards}.sod-modal .sod-panel.sod-modal-anim-zoom{animation:sod-modal-zoom-in .18s ease-out}.sod-modal .sod-panel.sod-modal-anim-zoom.is-closing{animation:sod-modal-zoom-out .18s ease-in forwards}.sod-modal .sod-panel.sod-modal-anim-slide{animation:sod-modal-slide-in .18s ease-out}.sod-modal .sod-panel.sod-modal-anim-slide.is-closing{animation:sod-modal-slide-out .18s ease-in forwards}.sod-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1rem .75rem;border-bottom:1px solid #dee2e6}.sod-title{margin:0;font-size:1.1rem}.sod-close{border:0;background:transparent;color:#6c757d;font-size:1.5rem;line-height:1;cursor:pointer}.sod-body{box-sizing:border-box;padding:1rem;overflow-wrap:anywhere;word-break:break-word}.sod-body>*{max-width:100%}.sod-body input,.sod-body select,.sod-body textarea,.sod-body button{max-width:100%;box-sizing:border-box}.sod-body img,.sod-body video,.sod-body canvas,.sod-body iframe,.sod-body table{max-width:100%}.sod-modal .sod-panel.sod-modal-autofit .sod-body{flex:1 1 auto;min-height:0}.sod-body p{margin:0 0 .5rem}.sod-body ul{margin:.5rem 0 0;padding-left:1.2rem}.sod-footer{padding:.75rem 1rem 1rem;border-top:1px solid #dee2e6;display:flex;justify-content:flex-end;gap:.5rem}.sod-btn{display:inline-flex;align-items:center;justify-content:center;border-radius:.375rem;border:1px solid transparent;padding:.5rem 1rem;font-size:.95rem;font-weight:500;cursor:pointer;transition:all .15s ease-in-out}.sod-btn:focus-visible{outline:2px solid #86b7fe;outline-offset:1px}.sod-btn-primary{background:#0d6efd;color:#fff;border-color:#0d6efd}.sod-btn-primary:hover{background:#0b5ed7;border-color:#0a58ca}.sod-btn-outline{background:transparent;color:#6c757d;border-color:#6c757d}.sod-btn-outline:hover{color:#fff;background:#6c757d}.sod-offcanvas[open]{width:100vw;height:100vh}.sod-offcanvas .sod-panel{position:fixed;margin:0;min-width:0;max-width:none;border-radius:0;width:min(360px,90vw);height:100vh}.sod-offcanvas .sod-panel.sod-placement-start{left:0;top:0}.sod-offcanvas .sod-panel.sod-placement-end{right:0;top:0}.sod-offcanvas .sod-panel.sod-placement-top{top:0;left:0;width:100vw;height:300px}.sod-offcanvas .sod-panel.sod-placement-bottom{bottom:0;left:0;width:100vw;height:300px}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-start{animation:sod-slide-in-start .18s ease-out}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-start.is-closing{animation:sod-slide-out-start .18s ease-in forwards}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-end{animation:sod-slide-in-end .18s ease-out}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-end.is-closing{animation:sod-slide-out-end .18s ease-in forwards}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-top{animation:sod-slide-in-top .18s ease-out}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-top.is-closing{animation:sod-slide-out-top .18s ease-in forwards}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-bottom{animation:sod-slide-in-bottom .18s ease-out}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-bottom.is-closing{animation:sod-slide-out-bottom .18s ease-in forwards}.sod-offcanvas .sod-panel.sod-anim-fade{animation:sod-fade-in .18s ease-out}.sod-offcanvas .sod-panel.sod-anim-fade.is-closing{animation:sod-fade-out .18s ease-in forwards}.sod-offcanvas .sod-panel.sod-anim-zoom{animation:sod-zoom-in .18s ease-out}.sod-offcanvas .sod-panel.sod-anim-zoom.is-closing{animation:sod-zoom-out .18s ease-in forwards}@keyframes sod-modal-zoom-in{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}@keyframes sod-modal-zoom-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.96)}}@keyframes sod-modal-slide-in{0%{opacity:0;transform:translateY(-16px)}to{opacity:1;transform:translateY(0)}}@keyframes sod-modal-slide-out{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-12px)}}@keyframes sod-slide-in-start{0%{transform:translate(-100%)}to{transform:translate(0)}}@keyframes sod-slide-out-start{0%{transform:translate(0)}to{transform:translate(-100%)}}@keyframes sod-slide-in-end{0%{transform:translate(100%)}to{transform:translate(0)}}@keyframes sod-slide-out-end{0%{transform:translate(0)}to{transform:translate(100%)}}@keyframes sod-slide-in-top{0%{transform:translateY(-100%)}to{transform:translateY(0)}}@keyframes sod-slide-out-top{0%{transform:translateY(0)}to{transform:translateY(-100%)}}@keyframes sod-slide-in-bottom{0%{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes sod-slide-out-bottom{0%{transform:translateY(0)}to{transform:translateY(100%)}}@keyframes sod-fade-in{0%{opacity:0}to{opacity:1}}@keyframes sod-fade-out{0%{opacity:1}to{opacity:0}}@keyframes sod-zoom-in{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}@keyframes sod-zoom-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.96)}}
1
+ .sod-dialog{border:0;padding:0;margin:auto;background:transparent;max-width:none;max-height:none;overflow:visible}.sod-dialog::backdrop{background:#0000007f}.sod-panel{box-sizing:border-box;background:#fff;color:#212529;border-radius:.5rem;box-shadow:0 .5rem 1rem #00000040;min-width:min(520px,calc(100vw - 2rem));max-width:min(520px,calc(100vw - 2rem))}.sod-modal .sod-panel{position:relative;margin:0}.sod-modal[open]{width:100vw;height:100vh;display:flex;justify-content:center;align-items:center}.sod-modal[open].sod-modal-viewport-scroll{align-items:flex-start;overflow:auto;padding:1rem 0}.sod-modal .sod-panel.sod-modal-pos-center{align-self:center}.sod-modal .sod-panel.sod-modal-pos-top{align-self:flex-start;margin-top:1rem}.sod-modal .sod-panel.sod-modal-pos-bottom{align-self:flex-end;margin-bottom:1rem}.sod-modal .sod-panel.sod-modal-autofit{min-width:0;width:auto;height:auto;max-width:calc(100vw - 2rem);max-height:calc(100vh - 2rem);display:flex;flex-direction:column}.sod-modal .sod-panel.sod-modal-draggable{touch-action:none}.sod-modal .sod-panel.sod-is-dragging{animation:none!important;transition:none!important}.sod-modal .sod-panel .sod-drag-handle{cursor:move;-webkit-user-select:none;user-select:none}.sod-modal .sod-panel .sod-drag-handle.is-dragging{cursor:grabbing}.sod-modal .sod-panel.sod-modal-anim-fade{animation:sod-fade-in .18s ease-out}.sod-modal .sod-panel.sod-modal-anim-fade.is-closing{animation:sod-fade-out .18s ease-in forwards}.sod-modal .sod-panel.sod-modal-anim-zoom{animation:sod-modal-zoom-in .18s ease-out}.sod-modal .sod-panel.sod-modal-anim-zoom.is-closing{animation:sod-modal-zoom-out .18s ease-in forwards}.sod-modal .sod-panel.sod-modal-anim-slide{animation:sod-modal-slide-in .18s ease-out}.sod-modal .sod-panel.sod-modal-anim-slide.is-closing{animation:sod-modal-slide-out .18s ease-in forwards}.sod-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1rem .75rem;border-bottom:1px solid #dee2e6}.sod-title{margin:0;font-size:1.1rem}.sod-close{border:0;background:transparent;color:#6c757d;font-size:1.5rem;line-height:1;cursor:pointer}.sod-body{box-sizing:border-box;padding:1rem;overflow-wrap:anywhere;word-break:break-word}.sod-body>*{max-width:100%}.sod-body input,.sod-body select,.sod-body textarea,.sod-body button{max-width:100%;box-sizing:border-box}.sod-body img,.sod-body video,.sod-body canvas,.sod-body iframe,.sod-body table{max-width:100%}.sod-modal .sod-panel.sod-modal-autofit .sod-body{flex:1 1 auto;min-height:0}.sod-body p{margin:0 0 .5rem}.sod-body ul{margin:.5rem 0 0;padding-left:1.2rem}.sod-footer{padding:.75rem 1rem 1rem;border-top:1px solid #dee2e6;display:flex;justify-content:flex-end;gap:.5rem}.sod-footer[data-align=start]{justify-content:flex-start}.sod-footer[data-align=center]{justify-content:center}.sod-footer[data-align=between]{justify-content:space-between}.sod-btn{display:inline-flex;align-items:center;justify-content:center;border-radius:.375rem;border:1px solid transparent;padding:.5rem 1rem;font-size:.95rem;font-weight:500;cursor:pointer;transition:all .15s ease-in-out}.sod-btn:focus-visible{outline:2px solid #86b7fe;outline-offset:1px}.sod-btn-primary{background:#0d6efd;color:#fff;border-color:#0d6efd}.sod-btn-primary:hover{background:#0b5ed7;border-color:#0a58ca}.sod-btn-outline{background:transparent;color:#6c757d;border-color:#6c757d}.sod-btn-outline:hover{color:#fff;background:#6c757d}.sod-btn-danger{background:#dc3545;color:#fff;border-color:#dc3545}.sod-btn-danger:hover{background:#bb2d3b;border-color:#b02a37}.sod-btn-success{background:#198754;color:#fff;border-color:#198754}.sod-btn-success:hover{background:#157347;border-color:#146c43}.sod-btn-ghost{background:transparent;color:#495057;border-color:transparent}.sod-btn-ghost:hover{background:#e9ecef}.sod-btn-link{background:transparent;color:#0d6efd;border-color:transparent;text-decoration:underline;text-underline-offset:2px}.sod-btn-link:hover{color:#0a58ca}.sod-offcanvas[open]{width:100vw;height:100vh}.sod-offcanvas .sod-panel{position:fixed;margin:0;min-width:0;max-width:none;border-radius:0;width:min(360px,90vw);height:100vh}.sod-offcanvas .sod-panel.sod-placement-start{left:0;top:0}.sod-offcanvas .sod-panel.sod-placement-end{right:0;top:0}.sod-offcanvas .sod-panel.sod-placement-top{top:0;left:0;width:100vw;height:300px}.sod-offcanvas .sod-panel.sod-placement-bottom{bottom:0;left:0;width:100vw;height:300px}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-start{animation:sod-slide-in-start .18s ease-out}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-start.is-closing{animation:sod-slide-out-start .18s ease-in forwards}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-end{animation:sod-slide-in-end .18s ease-out}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-end.is-closing{animation:sod-slide-out-end .18s ease-in forwards}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-top{animation:sod-slide-in-top .18s ease-out}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-top.is-closing{animation:sod-slide-out-top .18s ease-in forwards}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-bottom{animation:sod-slide-in-bottom .18s ease-out}.sod-offcanvas .sod-panel.sod-anim-slide.sod-placement-bottom.is-closing{animation:sod-slide-out-bottom .18s ease-in forwards}.sod-offcanvas .sod-panel.sod-anim-fade{animation:sod-fade-in .18s ease-out}.sod-offcanvas .sod-panel.sod-anim-fade.is-closing{animation:sod-fade-out .18s ease-in forwards}.sod-offcanvas .sod-panel.sod-anim-zoom{animation:sod-zoom-in .18s ease-out}.sod-offcanvas .sod-panel.sod-anim-zoom.is-closing{animation:sod-zoom-out .18s ease-in forwards}@keyframes sod-modal-zoom-in{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}@keyframes sod-modal-zoom-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.96)}}@keyframes sod-modal-slide-in{0%{opacity:0;transform:translateY(-16px)}to{opacity:1;transform:translateY(0)}}@keyframes sod-modal-slide-out{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-12px)}}@keyframes sod-slide-in-start{0%{transform:translate(-100%)}to{transform:translate(0)}}@keyframes sod-slide-out-start{0%{transform:translate(0)}to{transform:translate(-100%)}}@keyframes sod-slide-in-end{0%{transform:translate(100%)}to{transform:translate(0)}}@keyframes sod-slide-out-end{0%{transform:translate(0)}to{transform:translate(100%)}}@keyframes sod-slide-in-top{0%{transform:translateY(-100%)}to{transform:translateY(0)}}@keyframes sod-slide-out-top{0%{transform:translateY(0)}to{transform:translateY(-100%)}}@keyframes sod-slide-in-bottom{0%{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes sod-slide-out-bottom{0%{transform:translateY(0)}to{transform:translateY(100%)}}@keyframes sod-fade-in{0%{opacity:0}to{opacity:1}}@keyframes sod-fade-out{0%{opacity:1}to{opacity:0}}@keyframes sod-zoom-in{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}.sod-toast-layer{position:fixed;z-index:1200;display:flex;flex-direction:column;gap:.5rem;width:min(360px,calc(100vw - 1.5rem));pointer-events:none}.sod-toast-layer-top-start{top:.75rem;left:.75rem;align-items:flex-start}.sod-toast-layer-top-center{top:.75rem;left:50%;transform:translate(-50%);align-items:center}.sod-toast-layer-top-end{top:.75rem;right:.75rem;align-items:flex-end}.sod-toast-layer-bottom-start{bottom:.75rem;left:.75rem;align-items:flex-start}.sod-toast-layer-bottom-center{bottom:.75rem;left:50%;transform:translate(-50%);align-items:center}.sod-toast-layer-bottom-end{bottom:.75rem;right:.75rem;align-items:flex-end}.sod-toast{--sod-toast-accent: #6c757d;pointer-events:auto;width:100%;box-sizing:border-box;border-radius:.5rem;border:1px solid #dee2e6;background:#fff;color:#212529;box-shadow:0 .5rem 1rem #0000002e;overflow:hidden;animation:sod-toast-enter .18s ease-out}.sod-toast.is-closing{animation:sod-toast-exit .18s ease-in forwards}.sod-toast-header{display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.5rem .75rem 0}.sod-toast-title{font-size:.92rem;font-weight:700}.sod-toast-close{border:0;background:transparent;color:#6c757d;font-size:1.15rem;line-height:1;cursor:pointer;padding:0}.sod-toast-body{padding:.55rem .75rem .75rem;font-size:.92rem;line-height:1.45;overflow-wrap:anywhere;word-break:break-word}.sod-toast-progress{width:100%;height:3px;background:var(--sod-toast-accent);opacity:.9;transform-origin:left center;animation:sod-toast-progress-countdown 3s linear forwards}.sod-toast-default{--sod-toast-accent: #6c757d;border-left:4px solid #6c757d}.sod-toast-info{--sod-toast-accent: #0dcaf0;border-left:4px solid #0dcaf0}.sod-toast-success{--sod-toast-accent: #198754;border-left:4px solid #198754}.sod-toast-warning{--sod-toast-accent: #ffc107;border-left:4px solid #ffc107}.sod-toast-danger{--sod-toast-accent: #dc3545;border-left:4px solid #dc3545}@keyframes sod-toast-enter{0%{opacity:0;transform:translateY(-8px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes sod-toast-exit{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.96)}}@keyframes sod-toast-progress-countdown{0%{transform:scaleX(1)}to{transform:scaleX(0)}}@keyframes sod-zoom-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.96)}}
@@ -1,186 +1,299 @@
1
- function A(t, e) {
1
+ function W(n, e) {
2
2
  if (typeof e == "string") {
3
- t.innerHTML = e;
3
+ n.innerHTML = e;
4
4
  return;
5
5
  }
6
- t.append(e);
6
+ n.append(e);
7
7
  }
8
- function I(t, e = "hide") {
8
+ function B(n, e = "hide") {
9
9
  if (e === "destroy") {
10
- t.dataset.sodDestroy = "true", t.open && t.close(), t.remove();
10
+ n.dataset.sodDestroy = "true", n.open && n.close(), n.remove();
11
11
  return;
12
12
  }
13
- t.open && t.close();
13
+ n.open && n.close();
14
14
  }
15
- function N(t, e) {
16
- const i = e ?? "header";
17
- return i === "panel" ? t : i === "header" ? t.querySelector(".sod-header") : i === "title" ? t.querySelector(".sod-title") : i === "body" ? t.querySelector(".sod-body") : t.querySelector(i) ?? t.querySelector(".sod-header");
15
+ function $(n, e) {
16
+ return n.action ? n.action : n.role === "confirm" ? e : n.role === "cancel" ? "hide" : "none";
18
17
  }
19
- function F(t, e) {
20
- const i = N(t, e);
21
- if (!i)
18
+ function N(n) {
19
+ return n === "danger" ? "sod-btn-danger" : n === "success" ? "sod-btn-success" : n === "ghost" ? "sod-btn-ghost" : n === "link" ? "sod-btn-link" : n === "outline" ? "sod-btn-outline" : "sod-btn-primary";
20
+ }
21
+ function F(n, e) {
22
+ const t = Array.isArray(e) ? e.length > 0 ? e : ["header"] : [e ?? "header"], s = [], i = (o) => {
23
+ o && (s.includes(o) || s.push(o));
24
+ };
25
+ for (const o of t) {
26
+ if (o === "panel") {
27
+ i(n);
28
+ continue;
29
+ }
30
+ if (o === "header") {
31
+ i(n.querySelector(".sod-header"));
32
+ continue;
33
+ }
34
+ if (o === "title") {
35
+ i(n.querySelector(".sod-title"));
36
+ continue;
37
+ }
38
+ if (o === "body") {
39
+ i(n.querySelector(".sod-body"));
40
+ continue;
41
+ }
42
+ n.querySelectorAll(o).forEach((r) => {
43
+ i(r);
44
+ });
45
+ }
46
+ return s.length === 0 && i(n.querySelector(".sod-header")), s;
47
+ }
48
+ function k(n, e) {
49
+ const t = F(n, e);
50
+ if (t.length === 0)
22
51
  return;
23
- t.classList.add("sod-modal-draggable"), i.classList.add("sod-drag-handle");
24
- let s = !1, H = 0, v = 0, d = null, g = 0, L = 0, o = 0;
25
- const x = document.body.style.userSelect, c = (r, m) => {
26
- const a = t.getBoundingClientRect(), M = Math.max(0, window.innerWidth - a.width), w = Math.max(0, window.innerHeight - a.height), p = Math.min(0, window.innerWidth - a.width), l = Math.min(0, window.innerHeight - a.height);
52
+ n.classList.add("sod-modal-draggable"), t.forEach((m) => {
53
+ m.classList.add("sod-drag-handle");
54
+ });
55
+ let s = !1, i = 0, o = 0, r = null, d = null, h = 0, a = 0, f = 0;
56
+ const v = document.body.style.userSelect, E = (m, u) => {
57
+ const y = n.getBoundingClientRect(), p = Math.max(0, window.innerWidth - y.width), S = Math.max(0, window.innerHeight - y.height), A = Math.min(0, window.innerWidth - y.width), b = Math.min(0, window.innerHeight - y.height);
27
58
  return {
28
- left: Math.min(M, Math.max(p, r)),
29
- top: Math.min(w, Math.max(l, m))
59
+ left: Math.min(p, Math.max(A, m)),
60
+ top: Math.min(S, Math.max(b, u))
30
61
  };
31
- }, h = (r) => {
62
+ }, R = (m) => {
32
63
  if (!s)
33
64
  return;
34
- const m = c(r.clientX - H, r.clientY - v);
35
- L = m.left, o = m.top, g === 0 && (g = window.requestAnimationFrame(() => {
36
- g = 0, t.style.inset = "auto", t.style.left = `${L}px`, t.style.top = `${o}px`, t.style.right = "auto", t.style.bottom = "auto", t.style.margin = "0", t.style.transform = "none";
65
+ const u = E(m.clientX - i, m.clientY - o);
66
+ a = u.left, f = u.top, h === 0 && (h = window.requestAnimationFrame(() => {
67
+ h = 0, n.style.inset = "auto", n.style.left = `${a}px`, n.style.top = `${f}px`, n.style.right = "auto", n.style.bottom = "auto", n.style.margin = "0", n.style.transform = "none";
37
68
  }));
38
- }, u = () => {
39
- s = !1, i.classList.remove("is-dragging"), t.classList.remove("sod-is-dragging"), t.style.willChange = "", document.body.style.userSelect = x, g !== 0 && (window.cancelAnimationFrame(g), g = 0), d !== null && i.hasPointerCapture && i.hasPointerCapture(d) && i.releasePointerCapture(d), d = null, window.removeEventListener("pointermove", h), window.removeEventListener("pointerup", u), window.removeEventListener("pointercancel", u);
69
+ }, x = () => {
70
+ s = !1, t.forEach((m) => {
71
+ m.classList.remove("is-dragging");
72
+ }), n.classList.remove("sod-is-dragging"), n.style.willChange = "", document.body.style.userSelect = v, h !== 0 && (window.cancelAnimationFrame(h), h = 0), r !== null && d !== null && d.hasPointerCapture && d.hasPointerCapture(r) && d.releasePointerCapture(r), r = null, d = null, window.removeEventListener("pointermove", R), window.removeEventListener("pointerup", x), window.removeEventListener("pointercancel", x);
40
73
  };
41
- i.addEventListener("pointerdown", (r) => {
42
- if (r.button !== 0 || r.target?.closest("button, input, select, textarea, a"))
43
- return;
44
- r.preventDefault(), t.classList.add("sod-is-dragging"), t.style.animation = "none", t.style.transition = "none", t.style.transform = "none";
45
- const a = t.getBoundingClientRect();
46
- H = r.clientX - a.left, v = r.clientY - a.top, s = !0, d = r.pointerId, i.setPointerCapture(r.pointerId), t.style.position = "fixed", t.style.left = `${a.left}px`, t.style.top = `${a.top}px`, t.style.width = `${Math.round(a.width)}px`, t.style.height = `${Math.round(a.height)}px`, t.style.inset = "auto", t.style.right = "auto", t.style.bottom = "auto", t.style.margin = "0", t.style.willChange = "left, top", i.classList.add("is-dragging"), document.body.style.userSelect = "none", window.addEventListener("pointermove", h), window.addEventListener("pointerup", u), window.addEventListener("pointercancel", u);
74
+ t.forEach((m) => {
75
+ m.addEventListener("pointerdown", (u) => {
76
+ if (u.button !== 0 || u.target?.closest("button, input, select, textarea, a"))
77
+ return;
78
+ u.preventDefault(), n.classList.add("sod-is-dragging"), n.style.animation = "none", n.style.transition = "none", n.style.transform = "none";
79
+ const p = n.getBoundingClientRect();
80
+ i = u.clientX - p.left, o = u.clientY - p.top, s = !0, r = u.pointerId, d = m, m.setPointerCapture(u.pointerId), n.style.position = "fixed", n.style.left = `${p.left}px`, n.style.top = `${p.top}px`, n.style.width = `${Math.round(p.width)}px`, n.style.height = `${Math.round(p.height)}px`, n.style.inset = "auto", n.style.right = "auto", n.style.bottom = "auto", n.style.margin = "0", n.style.willChange = "left, top", m.classList.add("is-dragging"), document.body.style.userSelect = "none", window.addEventListener("pointermove", R), window.addEventListener("pointerup", x), window.addEventListener("pointercancel", x);
81
+ });
47
82
  });
48
83
  }
49
- function Y(t, e, i, s, H, v) {
50
- const g = Math.max(1, v?.hybridSwitchRatio ?? 1.35), L = v?.refitOnContentChange ?? !0, o = Math.max(120, v?.autoFitMinWidth ?? 280), x = Math.max(100, v?.autoFitMinHeight ?? 160), c = v?.scrollMode ? v.scrollMode : v?.autoFitUseScrollbar === !1 ? "viewport" : "body";
51
- let h = 0;
52
- const u = (n, f, E) => Math.min(E, Math.max(f, n)), r = () => {
53
- if (h = 0, !t.isConnected || !t.open)
84
+ function D(n, e, t, s, i, o) {
85
+ const d = Math.max(1, o?.hybridSwitchRatio ?? 1.35), h = o?.refitOnContentChange ?? !0, a = Math.max(120, o?.autoFitMinWidth ?? 280), f = Math.max(100, o?.autoFitMinHeight ?? 160), v = o?.scrollMode ? o.scrollMode : o?.autoFitUseScrollbar === !1 ? "viewport" : "body";
86
+ let E = 0;
87
+ const R = (b, M, L) => Math.min(L, Math.max(M, b)), x = () => {
88
+ if (E = 0, !n.isConnected || !n.open)
54
89
  return;
55
- const n = Math.max(o, window.innerWidth - 32), f = Math.max(x, window.innerHeight - 32);
90
+ const b = Math.max(a, window.innerWidth - 32), M = Math.max(f, window.innerHeight - 32);
56
91
  e.style.width = "auto", e.style.height = "auto", e.style.maxHeight = "none", s.style.maxHeight = "none", s.style.overflowY = "visible", s.style.overflowX = "visible";
57
- const E = Math.ceil(e.scrollWidth), $ = Math.ceil(e.scrollHeight), W = E > n, S = $ > f;
58
- if (c === "body") {
59
- const b = u(E, o, n), y = u($, x, f);
60
- e.style.width = `${b}px`, e.style.maxWidth = `${n}px`, e.style.height = `${y}px`, e.style.maxHeight = `${f}px`;
61
- const C = i.offsetHeight, R = H.offsetHeight, D = Math.max(64, y - C - R);
62
- S ? (s.style.maxHeight = `${D}px`, s.style.overflowY = "auto") : (s.style.maxHeight = "none", s.style.overflowY = "hidden"), s.style.overflowX = W ? "auto" : "hidden", t.classList.remove("sod-modal-viewport-scroll");
63
- } else if (c === "hybrid")
64
- if ($ > f * g) {
65
- const y = Math.max(E, o), C = Math.max($, x);
66
- e.style.width = `${y}px`, e.style.maxWidth = "none", e.style.height = `${C}px`, e.style.maxHeight = "none", s.style.maxHeight = "none", s.style.overflowY = "visible", s.style.overflowX = "visible", t.classList.add("sod-modal-viewport-scroll");
92
+ const L = Math.ceil(e.scrollWidth), l = Math.ceil(e.scrollHeight), c = L > b, g = l > M;
93
+ if (v === "body") {
94
+ const w = R(L, a, b), O = R(l, f, M);
95
+ e.style.width = `${w}px`, e.style.maxWidth = `${b}px`, e.style.height = `${O}px`, e.style.maxHeight = `${M}px`;
96
+ const H = t.offsetHeight, T = i.offsetHeight, I = Math.max(64, O - H - T);
97
+ g ? (s.style.maxHeight = `${I}px`, s.style.overflowY = "auto") : (s.style.maxHeight = "none", s.style.overflowY = "hidden"), s.style.overflowX = c ? "auto" : "hidden", n.classList.remove("sod-modal-viewport-scroll");
98
+ } else if (v === "hybrid")
99
+ if (l > M * d) {
100
+ const O = Math.max(L, a), H = Math.max(l, f);
101
+ e.style.width = `${O}px`, e.style.maxWidth = "none", e.style.height = `${H}px`, e.style.maxHeight = "none", s.style.maxHeight = "none", s.style.overflowY = "visible", s.style.overflowX = "visible", n.classList.add("sod-modal-viewport-scroll");
67
102
  } else {
68
- const y = u(E, o, n), C = u($, x, f);
69
- e.style.width = `${y}px`, e.style.maxWidth = `${n}px`, e.style.height = `${C}px`, e.style.maxHeight = `${f}px`;
70
- const R = i.offsetHeight, D = H.offsetHeight, P = Math.max(64, C - R - D);
71
- S ? (s.style.maxHeight = `${P}px`, s.style.overflowY = "auto") : (s.style.maxHeight = "none", s.style.overflowY = "hidden"), s.style.overflowX = W ? "auto" : "hidden", t.classList.remove("sod-modal-viewport-scroll");
103
+ const O = R(L, a, b), H = R(l, f, M);
104
+ e.style.width = `${O}px`, e.style.maxWidth = `${b}px`, e.style.height = `${H}px`, e.style.maxHeight = `${M}px`;
105
+ const T = t.offsetHeight, I = i.offsetHeight, C = Math.max(64, H - T - I);
106
+ g ? (s.style.maxHeight = `${C}px`, s.style.overflowY = "auto") : (s.style.maxHeight = "none", s.style.overflowY = "hidden"), s.style.overflowX = c ? "auto" : "hidden", n.classList.remove("sod-modal-viewport-scroll");
72
107
  }
73
- else if (c === "viewport") {
74
- const b = Math.max(E, o), y = Math.max($, x);
75
- e.style.width = `${b}px`, e.style.maxWidth = "none", e.style.height = `${y}px`, e.style.maxHeight = "none", s.style.maxHeight = "none", s.style.overflowY = "visible", s.style.overflowX = "visible", y > f ? t.classList.add("sod-modal-viewport-scroll") : t.classList.remove("sod-modal-viewport-scroll");
108
+ else if (v === "viewport") {
109
+ const w = Math.max(L, a), O = Math.max(l, f);
110
+ e.style.width = `${w}px`, e.style.maxWidth = "none", e.style.height = `${O}px`, e.style.maxHeight = "none", s.style.maxHeight = "none", s.style.overflowY = "visible", s.style.overflowX = "visible", O > M ? n.classList.add("sod-modal-viewport-scroll") : n.classList.remove("sod-modal-viewport-scroll");
76
111
  } else {
77
- const b = W ? Math.max(E, o) : u(E, o, n);
78
- if (W || S) {
79
- const y = Math.max($, x);
80
- e.style.width = `${b}px`, e.style.maxWidth = "none", e.style.height = `${y}px`, e.style.maxHeight = "none", s.style.maxHeight = "none", s.style.overflowY = "visible", s.style.overflowX = "visible", t.classList.add("sod-modal-viewport-scroll");
112
+ const w = c ? Math.max(L, a) : R(L, a, b);
113
+ if (c || g) {
114
+ const O = Math.max(l, f);
115
+ e.style.width = `${w}px`, e.style.maxWidth = "none", e.style.height = `${O}px`, e.style.maxHeight = "none", s.style.maxHeight = "none", s.style.overflowY = "visible", s.style.overflowX = "visible", n.classList.add("sod-modal-viewport-scroll");
81
116
  } else
82
- e.style.width = `${b}px`, e.style.maxWidth = `${n}px`, e.style.height = "auto", e.style.maxHeight = "none", s.style.maxHeight = "none", s.style.overflowY = "visible", s.style.overflowX = "visible", t.classList.remove("sod-modal-viewport-scroll");
117
+ e.style.width = `${w}px`, e.style.maxWidth = `${b}px`, e.style.height = "auto", e.style.maxHeight = "none", s.style.maxHeight = "none", s.style.overflowY = "visible", s.style.overflowX = "visible", n.classList.remove("sod-modal-viewport-scroll");
83
118
  }
84
119
  }, m = () => {
85
- h === 0 && (h = window.requestAnimationFrame(r));
86
- }, a = new ResizeObserver(() => {
120
+ E === 0 && (E = window.requestAnimationFrame(x));
121
+ }, u = new ResizeObserver(() => {
87
122
  m();
88
- }), M = new MutationObserver(() => {
123
+ }), y = new MutationObserver(() => {
89
124
  m();
90
- }), w = () => {
125
+ }), p = () => {
91
126
  m();
92
- }, p = () => {
127
+ }, S = () => {
93
128
  m();
94
129
  };
95
- t.addEventListener("sod:refit", p), a.observe(s), a.observe(i), a.observe(H), L && (M.observe(s, {
130
+ n.addEventListener("sod:refit", S), u.observe(s), u.observe(t), u.observe(i), h && (y.observe(s, {
96
131
  subtree: !0,
97
132
  childList: !0,
98
133
  characterData: !0,
99
134
  attributes: !0
100
- }), s.addEventListener("load", w, !0), s.addEventListener("error", w, !0));
101
- const l = () => {
135
+ }), s.addEventListener("load", p, !0), s.addEventListener("error", p, !0));
136
+ const A = () => {
102
137
  m();
103
138
  };
104
- return window.addEventListener("resize", l), m(), () => {
105
- h !== 0 && (window.cancelAnimationFrame(h), h = 0), a.disconnect(), M.disconnect(), window.removeEventListener("resize", l), t.removeEventListener("sod:refit", p), s.removeEventListener("load", w, !0), s.removeEventListener("error", w, !0), t.classList.remove("sod-modal-viewport-scroll"), s.style.maxHeight = "", s.style.overflowY = "", s.style.overflowX = "";
139
+ return window.addEventListener("resize", A), m(), () => {
140
+ E !== 0 && (window.cancelAnimationFrame(E), E = 0), u.disconnect(), y.disconnect(), window.removeEventListener("resize", A), n.removeEventListener("sod:refit", S), s.removeEventListener("load", p, !0), s.removeEventListener("error", p, !0), n.classList.remove("sod-modal-viewport-scroll"), s.style.maxHeight = "", s.style.overflowY = "", s.style.overflowX = "";
106
141
  };
107
142
  }
108
- class O {
143
+ class P {
109
144
  static modalRegistry = /* @__PURE__ */ new Map();
145
+ static handleRegistry = /* @__PURE__ */ new WeakMap();
110
146
  static modalIdSeed = 0;
111
147
  static createAutoModalId() {
112
148
  return this.modalIdSeed += 1, `sod-modal-${this.modalIdSeed}`;
113
149
  }
114
- static revealExisting(e, i) {
150
+ static revealExisting(e, t) {
115
151
  if (!e.isConnected)
116
- return;
152
+ return {
153
+ dialog: e,
154
+ close: () => B(e),
155
+ refit: () => e.dispatchEvent(new Event("sod:refit")),
156
+ setFooterButtons: () => {
157
+ },
158
+ updateFooterButton: () => !1,
159
+ onAction: () => () => {
160
+ },
161
+ id: e.dataset.sodId
162
+ };
117
163
  const s = e.querySelector(".sod-panel");
118
- s?.classList.remove("is-closing"), document.body.append(e), e.open || (i ? e.showModal() : e.show()), e.dispatchEvent(new Event("sod:refit")), s?.focus();
164
+ s?.classList.remove("is-closing"), document.body.append(e), e.open || (t ? e.showModal() : e.show()), e.dispatchEvent(new Event("sod:refit")), s?.focus();
165
+ const i = this.handleRegistry.get(e);
166
+ return i ? i() : {
167
+ dialog: e,
168
+ close: () => B(e),
169
+ refit: () => e.dispatchEvent(new Event("sod:refit")),
170
+ setFooterButtons: () => {
171
+ },
172
+ updateFooterButton: () => !1,
173
+ onAction: () => () => {
174
+ },
175
+ id: e.dataset.sodId
176
+ };
119
177
  }
120
178
  static open(e) {
121
- const i = e.kind ?? "modal", s = "useModal" in e ? e.useModal ?? !0 : !0, H = i === "modal" ? e : void 0, v = i === "modal" && ("autoFitSize" in e ? e.autoFitSize !== !1 : !0);
122
- let d, g = !1, L = e.confirmAction ?? "hide";
123
- if (i === "modal") {
179
+ const t = e.kind ?? "modal", s = "useModal" in e ? e.useModal ?? !0 : !0, i = t === "modal" ? e : void 0, o = t === "modal" && ("autoFitSize" in e ? e.autoFitSize !== !1 : !0);
180
+ let r, d = !1, h = e.confirmAction ?? "hide";
181
+ if (t === "modal") {
124
182
  const l = "id" in e && e.id?.trim() ? e.id.trim() : void 0;
125
- if (g = !!l, d = l ?? this.createAutoModalId(), e.confirmAction === void 0 && g && (L = "destroy"), l) {
126
- const n = this.modalRegistry.get(d);
127
- if (n && n.isConnected) {
128
- this.revealExisting(n, s);
129
- const f = {
130
- dialog: n,
131
- close: () => I(n),
132
- refit: () => n.dispatchEvent(new Event("sod:refit")),
133
- id: d
134
- };
135
- return "onReused" in e && e.onReused?.(f), f;
183
+ if (d = !!l, r = l ?? this.createAutoModalId(), e.confirmAction === void 0 && d && (h = "destroy"), l) {
184
+ const c = this.modalRegistry.get(r);
185
+ if (c && c.isConnected) {
186
+ const g = this.revealExisting(c, s);
187
+ if (e.onAction) {
188
+ const w = g.onAction(e.onAction);
189
+ c.addEventListener("close", w, { once: !0 });
190
+ }
191
+ return "onReused" in e && e.onReused?.(g), g;
136
192
  }
137
193
  }
138
- this.modalRegistry.delete(d);
139
- }
140
- const o = document.createElement("dialog");
141
- o.className = `sod-dialog sod-${i}`;
142
- const x = [];
143
- d && (o.dataset.sodId = d, g && (o.dataset.sodPersistent = "true"));
144
- const c = document.createElement("section");
145
- if (c.className = "sod-panel", c.tabIndex = -1, i === "offcanvas") {
146
- const l = "placement" in e ? e.placement ?? "end" : "end", n = "animation" in e ? e.animation ?? "slide" : "slide";
147
- c.classList.add(`sod-placement-${l}`), c.classList.add(`sod-anim-${n}`);
194
+ this.modalRegistry.delete(r);
195
+ }
196
+ const a = document.createElement("dialog");
197
+ a.className = `sod-dialog sod-${t}`;
198
+ const f = [];
199
+ r && (a.dataset.sodId = r, d && (a.dataset.sodPersistent = "true"));
200
+ const v = document.createElement("section");
201
+ if (v.className = "sod-panel", v.tabIndex = -1, t === "offcanvas") {
202
+ const l = "placement" in e ? e.placement ?? "end" : "end", c = "animation" in e ? e.animation ?? "slide" : "slide";
203
+ v.classList.add(`sod-placement-${l}`), v.classList.add(`sod-anim-${c}`);
148
204
  } else {
149
- const l = "position" in e ? e.position ?? "center" : "center", n = "animation" in e ? e.animation ?? "fade" : "fade";
150
- c.classList.add(`sod-modal-pos-${l}`), c.classList.add(`sod-modal-anim-${n}`), v && c.classList.add("sod-modal-autofit");
151
- }
152
- const h = document.createElement("header");
153
- h.className = "sod-header";
154
- const u = document.createElement("h2");
155
- u.className = "sod-title", u.textContent = e.title;
156
- const r = document.createElement("button");
157
- r.type = "button", r.className = "sod-close", r.setAttribute("aria-label", "Close"), r.textContent = "×", r.addEventListener("click", () => I(o)), h.append(u, r);
205
+ const l = "position" in e ? e.position ?? "center" : "center", c = "animation" in e ? e.animation ?? "fade" : "fade";
206
+ v.classList.add(`sod-modal-pos-${l}`), v.classList.add(`sod-modal-anim-${c}`), o && v.classList.add("sod-modal-autofit");
207
+ }
208
+ const E = document.createElement("header");
209
+ E.className = "sod-header";
210
+ const R = document.createElement("h2");
211
+ R.className = "sod-title", R.textContent = e.title;
212
+ const x = document.createElement("button");
213
+ x.type = "button", x.className = "sod-close", x.setAttribute("aria-label", "Close"), x.textContent = "×", x.addEventListener("click", () => B(a)), E.append(R, x);
158
214
  const m = document.createElement("div");
159
- m.className = "sod-body", A(m, e.content);
160
- const a = document.createElement("footer");
161
- a.className = "sod-footer";
162
- const M = document.createElement("button");
163
- M.type = "button", M.className = "sod-btn sod-btn-outline", M.textContent = e.cancelText ?? "取消", M.addEventListener("click", () => {
164
- e.onCancel?.(), I(o);
215
+ m.className = "sod-body", W(m, e.content);
216
+ const u = document.createElement("footer");
217
+ u.className = "sod-footer", u.dataset.align = e.footerAlign ?? "end";
218
+ const y = /* @__PURE__ */ new Set();
219
+ e.onAction && y.add(e.onAction);
220
+ const p = (l) => (y.add(l), () => {
221
+ y.delete(l);
165
222
  });
166
- const w = document.createElement("button");
167
- w.type = "button", w.className = "sod-btn sod-btn-primary", w.textContent = e.confirmText ?? "确认", w.addEventListener("click", () => {
168
- e.onConfirm?.(), I(o, L);
169
- }), a.append(M, w), c.append(h, m, a), v && x.push(Y(o, c, h, m, a, H)), i === "modal" && "draggable" in e && e.draggable && F(c, e.dragHandle), o.append(c), (e.closeOnBackdrop ?? !0) && o.addEventListener("click", (l) => {
170
- l.target === o && I(o);
171
- }), (e.closeOnEsc ?? !0) || o.addEventListener("cancel", (l) => {
172
- l.preventDefault();
173
- }), o.addEventListener("close", () => {
174
- const l = o.dataset.sodPersistent === "true", n = o.dataset.sodDestroy === "true";
175
- (!l || n) && (x.forEach((f) => f()), o.remove(), d && this.modalRegistry.delete(d), delete o.dataset.sodDestroy);
176
- }), document.body.append(o), s ? o.showModal() : o.show(), o.dispatchEvent(new Event("sod:refit")), d && this.modalRegistry.set(d, o);
177
- const p = {
178
- dialog: o,
179
- close: () => I(o),
180
- refit: () => o.dispatchEvent(new Event("sod:refit")),
181
- id: d
223
+ let S = e.footerButtons ? [...e.footerButtons] : [
224
+ {
225
+ id: "cancel",
226
+ label: e.cancelText ?? "取消",
227
+ role: "cancel",
228
+ variant: "outline"
229
+ },
230
+ {
231
+ id: "confirm",
232
+ label: e.confirmText ?? "确认",
233
+ role: "confirm",
234
+ variant: "primary"
235
+ }
236
+ ];
237
+ const A = () => ({
238
+ dialog: a,
239
+ close: () => B(a),
240
+ refit: () => a.dispatchEvent(new Event("sod:refit")),
241
+ setFooterButtons: (l) => {
242
+ S = [...l], M(), a.dispatchEvent(new Event("sod:refit"));
243
+ },
244
+ updateFooterButton: (l, c) => {
245
+ const g = S.findIndex((w) => w.id === l);
246
+ return g < 0 ? !1 : (S[g] = { ...S[g], ...c }, M(), a.dispatchEvent(new Event("sod:refit")), !0);
247
+ },
248
+ onAction: (l) => p(l),
249
+ id: r
250
+ }), b = (l, c) => {
251
+ if (typeof c == "string") {
252
+ l.textContent = c;
253
+ return;
254
+ }
255
+ l.append(c);
256
+ }, M = () => {
257
+ if (u.replaceChildren(), e.hideFooter || S.length === 0) {
258
+ u.hidden = !0;
259
+ return;
260
+ }
261
+ u.hidden = !1;
262
+ for (const l of S) {
263
+ const c = document.createElement("button");
264
+ c.type = "button", c.className = `sod-btn ${N(l.variant)}`, l.className?.trim() && (c.className = `${c.className} ${l.className.trim()}`), l.id?.trim() && (c.dataset.action = l.id.trim()), l.disabled && (c.disabled = !0), l.attrs && Object.entries(l.attrs).forEach(([g, w]) => {
265
+ c.setAttribute(g, w);
266
+ }), b(c, l.label), c.addEventListener("click", (g) => {
267
+ (async () => {
268
+ const w = A(), H = {
269
+ action: l.id ?? l.role ?? "custom",
270
+ button: l,
271
+ buttonElement: c,
272
+ dialog: a,
273
+ event: g,
274
+ handle: w
275
+ };
276
+ if (await l.onClick?.(H) === !1)
277
+ return;
278
+ l.role === "cancel" && e.onCancel?.(), l.role === "confirm" && e.onConfirm?.(), y.forEach((C) => {
279
+ C(H);
280
+ });
281
+ const I = $(l, h);
282
+ I === "hide" && B(a, "hide"), I === "destroy" && B(a, "destroy");
283
+ })();
284
+ }), u.append(c);
285
+ }
182
286
  };
183
- return i === "modal" && "onCreated" in e && e.onCreated?.(p), p;
287
+ M(), v.append(E, m, u), o && f.push(D(a, v, E, m, u, i)), t === "modal" && "draggable" in e && e.draggable && k(v, e.dragHandle), a.append(v), (e.closeOnBackdrop ?? !0) && a.addEventListener("click", (l) => {
288
+ l.target === a && B(a);
289
+ }), (e.closeOnEsc ?? !0) || a.addEventListener("cancel", (l) => {
290
+ l.preventDefault();
291
+ }), a.addEventListener("close", () => {
292
+ const l = a.dataset.sodPersistent === "true", c = a.dataset.sodDestroy === "true";
293
+ (!l || c) && (f.forEach((g) => g()), y.clear(), a.remove(), r && this.modalRegistry.delete(r), this.handleRegistry.delete(a), delete a.dataset.sodDestroy);
294
+ }), document.body.append(a), s ? a.showModal() : a.show(), a.dispatchEvent(new Event("sod:refit")), r && this.modalRegistry.set(r, a), this.handleRegistry.set(a, A);
295
+ const L = A();
296
+ return t === "modal" && "onCreated" in e && e.onCreated?.(L), L;
184
297
  }
185
298
  static openModal(e) {
186
299
  return this.open({ ...e, kind: "modal" });
@@ -189,14 +302,334 @@ class O {
189
302
  return this.open({ ...e, kind: "offcanvas" });
190
303
  }
191
304
  }
192
- function k(t) {
193
- return O.openModal(t);
305
+ class V {
306
+ static placementState = /* @__PURE__ */ new Map();
307
+ static recordById = /* @__PURE__ */ new Map();
308
+ static idSeed = 0;
309
+ static defaults = {
310
+ placement: "top-end",
311
+ variant: "default",
312
+ duration: 3e3,
313
+ showProgress: !0,
314
+ closable: !0,
315
+ pauseOnHover: !0,
316
+ pauseOnWindowBlur: !1,
317
+ duplicateStrategy: "update",
318
+ newestOnTop: !0,
319
+ maxVisible: 3
320
+ };
321
+ static normalizeOptions(e) {
322
+ const t = e.duration === !1 ? !1 : Math.max(0, e.duration ?? this.defaults.duration);
323
+ return {
324
+ placement: e.placement ?? this.defaults.placement,
325
+ variant: e.variant ?? this.defaults.variant,
326
+ duration: t,
327
+ showProgress: e.showProgress ?? this.defaults.showProgress,
328
+ closable: e.closable ?? this.defaults.closable,
329
+ pauseOnHover: e.pauseOnHover ?? this.defaults.pauseOnHover,
330
+ pauseOnWindowBlur: e.pauseOnWindowBlur ?? this.defaults.pauseOnWindowBlur,
331
+ duplicateStrategy: e.duplicateStrategy ?? this.defaults.duplicateStrategy,
332
+ newestOnTop: e.newestOnTop ?? this.defaults.newestOnTop,
333
+ maxVisible: Math.max(1, e.maxVisible ?? this.defaults.maxVisible),
334
+ title: e.title,
335
+ content: e.content,
336
+ className: e.className,
337
+ attrs: e.attrs,
338
+ onShown: e.onShown,
339
+ onClose: e.onClose
340
+ };
341
+ }
342
+ static createAutoToastId() {
343
+ return this.idSeed += 1, `sot-toast-${this.idSeed}`;
344
+ }
345
+ static createStackedToastId(e) {
346
+ let t = 1, s = `${e}-${t}`;
347
+ for (; this.recordById.has(s); )
348
+ t += 1, s = `${e}-${t}`;
349
+ return s;
350
+ }
351
+ static resolveRole(e) {
352
+ return e === "danger" ? "alert" : "status";
353
+ }
354
+ static getPlacementState(e) {
355
+ const t = this.placementState.get(e);
356
+ if (t)
357
+ return t;
358
+ const s = document.createElement("div");
359
+ s.className = `sod-toast-layer sod-toast-layer-${e}`, s.setAttribute("aria-live", "polite"), s.setAttribute("aria-atomic", "false"), document.body.append(s);
360
+ const i = {
361
+ container: s,
362
+ active: [],
363
+ pending: []
364
+ };
365
+ return this.placementState.set(e, i), i;
366
+ }
367
+ static createToastElement(e) {
368
+ const { options: t } = e, s = document.createElement("article");
369
+ s.className = `sod-toast sod-toast-${t.variant}`, t.className?.trim() && (s.className = `${s.className} ${t.className.trim()}`), s.dataset.toastId = e.id, s.setAttribute("role", this.resolveRole(t.variant)), t.attrs && Object.entries(t.attrs).forEach(([a, f]) => {
370
+ s.setAttribute(a, f);
371
+ });
372
+ const i = document.createElement("header");
373
+ i.className = "sod-toast-header";
374
+ let o = null;
375
+ t.title?.trim() && (o = document.createElement("strong"), o.className = "sod-toast-title", o.textContent = t.title, i.append(o));
376
+ let r = null;
377
+ t.closable && (r = document.createElement("button"), r.type = "button", r.className = "sod-toast-close", r.setAttribute("aria-label", "Close"), r.textContent = "×", r.addEventListener("click", () => {
378
+ this.closeRecord(e, "close-button");
379
+ }), i.append(r)), i.childElementCount > 0 && s.append(i);
380
+ const d = document.createElement("div");
381
+ d.className = "sod-toast-body", W(d, t.content), s.append(d);
382
+ let h = null;
383
+ if (t.showProgress && (h = document.createElement("div"), h.className = "sod-toast-progress", s.append(h)), t.pauseOnHover) {
384
+ const a = () => this.pauseRecord(e), f = () => this.resumeRecord(e);
385
+ s.addEventListener("mouseenter", a), s.addEventListener("mouseleave", f), e.cleanupListeners.push(() => {
386
+ s.removeEventListener("mouseenter", a), s.removeEventListener("mouseleave", f);
387
+ });
388
+ }
389
+ if (t.pauseOnWindowBlur) {
390
+ const a = () => {
391
+ e.paused || e.status !== "active" || (e.pausedByWindowBlur = !0, this.pauseRecord(e));
392
+ }, f = () => {
393
+ e.pausedByWindowBlur && (e.pausedByWindowBlur = !1, this.resumeRecord(e));
394
+ };
395
+ window.addEventListener("blur", a), window.addEventListener("focus", f), e.cleanupListeners.push(() => {
396
+ window.removeEventListener("blur", a), window.removeEventListener("focus", f);
397
+ });
398
+ }
399
+ e.element = s, e.bodyElement = d, e.titleElement = o, e.closeButton = r, e.progressElement = h;
400
+ }
401
+ static resetProgressAnimation(e, t) {
402
+ !e.progressElement || !e.options.showProgress || (e.progressElement.style.display = "block", e.progressElement.style.animation = "none", e.progressElement.style.animationPlayState = "running", e.progressElement.offsetWidth, e.progressElement.style.animation = `sod-toast-progress-countdown ${Math.max(1, t)}ms linear forwards`);
403
+ }
404
+ static hideProgress(e) {
405
+ e.progressElement && (e.progressElement.style.display = "none", e.progressElement.style.animation = "none");
406
+ }
407
+ static isRecordActive(e, t) {
408
+ return e.active.includes(t);
409
+ }
410
+ static isRecordPending(e, t) {
411
+ return e.pending.includes(t);
412
+ }
413
+ static mountRecord(e, t) {
414
+ t.status = "active", t.options.newestOnTop ? e.container.prepend(t.element) : e.container.append(t.element), e.active.push(t), this.startRecordTimer(t), t.options.onShown?.(t.handle);
415
+ }
416
+ static queueRecord(e, t) {
417
+ t.status = "pending", e.pending.push(t);
418
+ }
419
+ static startRecordTimer(e) {
420
+ if (this.clearRecordTimer(e), e.options.duration === !1) {
421
+ e.remainingMs = !1, e.startedAt = null, this.hideProgress(e);
422
+ return;
423
+ }
424
+ const t = Math.max(0, e.remainingMs === !1 ? e.options.duration : e.remainingMs);
425
+ if (t === 0) {
426
+ this.closeRecord(e, "timeout");
427
+ return;
428
+ }
429
+ e.startedAt = Date.now(), e.remainingMs = t, this.resetProgressAnimation(e, t), e.timerId = window.setTimeout(() => {
430
+ e.timerId = null, this.closeRecord(e, "timeout");
431
+ }, t);
432
+ }
433
+ static clearRecordTimer(e) {
434
+ e.timerId !== null && (window.clearTimeout(e.timerId), e.timerId = null);
435
+ }
436
+ static pauseRecord(e) {
437
+ if (e.paused || e.status !== "active" || e.options.duration === !1)
438
+ return;
439
+ const t = e.startedAt ?? Date.now(), s = Math.max(0, Date.now() - t), i = e.remainingMs === !1 ? e.options.duration : e.remainingMs;
440
+ e.remainingMs = Math.max(0, i - s), e.startedAt = null, e.paused = !0, e.progressElement && (e.progressElement.style.animationPlayState = "paused"), this.clearRecordTimer(e);
441
+ }
442
+ static resumeRecord(e) {
443
+ !e.paused || e.status !== "active" || (e.paused = !1, e.progressElement && (e.progressElement.style.animationPlayState = "running"), this.startRecordTimer(e));
444
+ }
445
+ static rebuildRecordElement(e) {
446
+ const t = e.element, s = t.parentElement;
447
+ e.cleanupListeners.forEach((i) => i()), e.cleanupListeners = [], t.remove(), this.createToastElement(e), s && e.status === "active" && (e.options.newestOnTop ? s.prepend(e.element) : s.append(e.element), this.startRecordTimer(e));
448
+ }
449
+ static moveRecordToPlacement(e, t) {
450
+ const s = e.options.placement;
451
+ if (s === t)
452
+ return;
453
+ const i = this.placementState.get(s), o = this.getPlacementState(t), r = !!(i && this.removeFromArray(i.active, e)), d = !!(i && this.removeFromArray(i.pending, e));
454
+ if (e.options.placement = t, this.clearRecordTimer(e), d && !r) {
455
+ this.queueRecord(o, e), this.drainQueue(o, t), i && this.drainQueue(i, s);
456
+ return;
457
+ }
458
+ o.active.length < e.options.maxVisible ? this.mountRecord(o, e) : this.queueRecord(o, e), i && this.drainQueue(i, s);
459
+ }
460
+ static updateRecord(e, t) {
461
+ if (t.title !== void 0) {
462
+ const i = t.title.trim();
463
+ if (e.options.title = i.length > 0 ? i : void 0, e.options.title)
464
+ if (e.titleElement)
465
+ e.titleElement.textContent = e.options.title;
466
+ else {
467
+ const o = e.element.querySelector(".sod-toast-header");
468
+ if (o) {
469
+ const r = document.createElement("strong");
470
+ r.className = "sod-toast-title", r.textContent = e.options.title, o.prepend(r), e.titleElement = r;
471
+ }
472
+ }
473
+ else e.titleElement && (e.titleElement.remove(), e.titleElement = null);
474
+ }
475
+ t.content !== void 0 && (e.options.content = t.content, e.bodyElement.replaceChildren(), W(e.bodyElement, t.content)), t.variant !== void 0 && (e.options.variant = t.variant, e.element.classList.remove("sod-toast-default", "sod-toast-info", "sod-toast-success", "sod-toast-warning", "sod-toast-danger"), e.element.classList.add(`sod-toast-${t.variant}`), e.element.setAttribute("role", this.resolveRole(t.variant))), t.duration !== void 0 && (e.options.duration = t.duration === !1 ? !1 : Math.max(0, t.duration), e.remainingMs = e.options.duration === !1 ? !1 : e.options.duration, e.paused = !1, e.status === "active" && this.startRecordTimer(e)), t.maxVisible !== void 0 && (e.options.maxVisible = Math.max(1, t.maxVisible)), t.newestOnTop !== void 0 && (e.options.newestOnTop = t.newestOnTop);
476
+ let s = !1;
477
+ t.closable !== void 0 && t.closable !== e.options.closable && (e.options.closable = t.closable, s = !0), t.pauseOnHover !== void 0 && t.pauseOnHover !== e.options.pauseOnHover && (e.options.pauseOnHover = t.pauseOnHover, s = !0), t.pauseOnWindowBlur !== void 0 && t.pauseOnWindowBlur !== e.options.pauseOnWindowBlur && (e.options.pauseOnWindowBlur = t.pauseOnWindowBlur, s = !0), t.duplicateStrategy !== void 0 && (e.options.duplicateStrategy = t.duplicateStrategy), t.showProgress !== void 0 && t.showProgress !== e.options.showProgress && (e.options.showProgress = t.showProgress, s = !0), t.className !== void 0 && (e.options.className = t.className, s = !0), t.attrs !== void 0 && (e.options.attrs = t.attrs, s = !0), s && this.rebuildRecordElement(e), t.placement && t.placement !== e.options.placement && this.moveRecordToPlacement(e, t.placement);
478
+ }
479
+ static removeStateContainerIfEmpty(e) {
480
+ const t = this.placementState.get(e);
481
+ t && (t.active.length > 0 || t.pending.length > 0 || (t.container.remove(), this.placementState.delete(e)));
482
+ }
483
+ static drainQueue(e, t) {
484
+ let s = !0;
485
+ for (; s && e.pending.length > 0; ) {
486
+ const i = e.pending[0];
487
+ if (e.active.length >= i.options.maxVisible) {
488
+ s = !1;
489
+ continue;
490
+ }
491
+ e.pending.shift(), this.mountRecord(e, i);
492
+ }
493
+ this.removeStateContainerIfEmpty(t);
494
+ }
495
+ static removeFromArray(e, t) {
496
+ const s = e.indexOf(t);
497
+ return s < 0 ? !1 : (e.splice(s, 1), !0);
498
+ }
499
+ static finalizeClose(e, t) {
500
+ if (e.status === "closed")
501
+ return;
502
+ const s = e.options.placement, i = this.placementState.get(s);
503
+ this.clearRecordTimer(e), e.cleanupListeners.forEach((o) => o()), e.cleanupListeners = [], e.element.remove(), e.status = "closed", i && (this.removeFromArray(i.active, e), this.removeFromArray(i.pending, e)), this.recordById.delete(e.id), e.options.onClose?.(t, e.handle), i ? this.drainQueue(i, s) : this.removeStateContainerIfEmpty(s);
504
+ }
505
+ static closeRecord(e, t) {
506
+ if (e.status === "closed" || e.status === "closing")
507
+ return;
508
+ const s = e.options.placement, i = this.placementState.get(s);
509
+ if (!!(i && this.isRecordPending(i, e) && !this.isRecordActive(i, e))) {
510
+ this.finalizeClose(e, t);
511
+ return;
512
+ }
513
+ e.status = "closing", this.clearRecordTimer(e), e.element.classList.add("is-closing");
514
+ let r = !1;
515
+ const d = () => {
516
+ r || (r = !0, this.finalizeClose(e, t));
517
+ }, h = (a) => {
518
+ a.target === e.element && (e.element.removeEventListener("animationend", h), d());
519
+ };
520
+ e.element.addEventListener("animationend", h), window.setTimeout(() => {
521
+ e.element.removeEventListener("animationend", h), d();
522
+ }, 220);
523
+ }
524
+ static createHandle(e) {
525
+ return {
526
+ id: e.id,
527
+ get element() {
528
+ return e.element;
529
+ },
530
+ close: (t = "programmatic") => {
531
+ this.closeRecord(e, t);
532
+ },
533
+ update: (t) => {
534
+ this.updateRecord(e, t);
535
+ },
536
+ pause: () => {
537
+ this.pauseRecord(e);
538
+ },
539
+ resume: () => {
540
+ this.resumeRecord(e);
541
+ }
542
+ };
543
+ }
544
+ static show(e) {
545
+ const s = e.id?.trim() || this.createAutoToastId(), i = this.normalizeOptions(e), o = this.recordById.get(s);
546
+ if (o) {
547
+ const a = i.duplicateStrategy;
548
+ if (a === "ignore")
549
+ return o.handle;
550
+ if (a === "stack") {
551
+ const f = this.createStackedToastId(s);
552
+ return this.show({ ...e, id: f, duplicateStrategy: "update" });
553
+ }
554
+ return this.updateRecord(o, {
555
+ title: e.title,
556
+ content: e.content,
557
+ variant: e.variant,
558
+ duration: a === "restart-timer" ? e.duration === void 0 ? o.options.duration : i.duration : e.duration === void 0 ? void 0 : i.duration,
559
+ placement: e.placement,
560
+ closable: e.closable,
561
+ pauseOnHover: e.pauseOnHover,
562
+ pauseOnWindowBlur: e.pauseOnWindowBlur,
563
+ duplicateStrategy: e.duplicateStrategy,
564
+ newestOnTop: e.newestOnTop,
565
+ maxVisible: e.maxVisible,
566
+ showProgress: e.showProgress,
567
+ className: e.className,
568
+ attrs: e.attrs
569
+ }), o.options.onClose = i.onClose, o.options.onShown = i.onShown, o.handle;
570
+ }
571
+ const d = {
572
+ id: s,
573
+ options: i,
574
+ element: document.createElement("article"),
575
+ status: "pending",
576
+ timerId: null,
577
+ remainingMs: i.duration,
578
+ startedAt: null,
579
+ paused: !1,
580
+ pausedByWindowBlur: !1,
581
+ closeButton: null,
582
+ progressElement: null,
583
+ bodyElement: document.createElement("div"),
584
+ titleElement: null,
585
+ cleanupListeners: [],
586
+ handle: void 0
587
+ };
588
+ d.handle = this.createHandle(d), this.createToastElement(d), this.recordById.set(s, d);
589
+ const h = this.getPlacementState(i.placement);
590
+ return h.active.length < i.maxVisible ? this.mountRecord(h, d) : this.queueRecord(h, d), d.handle;
591
+ }
592
+ static clear(e) {
593
+ (e ? [e] : Array.from(this.placementState.keys())).forEach((s) => {
594
+ const i = this.placementState.get(s);
595
+ if (!i)
596
+ return;
597
+ const o = [...i.active];
598
+ [...i.pending].forEach((d) => this.closeRecord(d, "container-clear")), o.forEach((d) => this.closeRecord(d, "container-clear"));
599
+ });
600
+ }
601
+ static configure(e) {
602
+ e.placement && (this.defaults.placement = e.placement), e.variant && (this.defaults.variant = e.variant), e.duration !== void 0 && (this.defaults.duration = e.duration === !1 ? 3e3 : Math.max(0, e.duration)), e.showProgress !== void 0 && (this.defaults.showProgress = e.showProgress), e.closable !== void 0 && (this.defaults.closable = e.closable), e.pauseOnHover !== void 0 && (this.defaults.pauseOnHover = e.pauseOnHover), e.pauseOnWindowBlur !== void 0 && (this.defaults.pauseOnWindowBlur = e.pauseOnWindowBlur), e.duplicateStrategy !== void 0 && (this.defaults.duplicateStrategy = e.duplicateStrategy), e.newestOnTop !== void 0 && (this.defaults.newestOnTop = e.newestOnTop), e.maxVisible !== void 0 && (this.defaults.maxVisible = Math.max(1, e.maxVisible));
603
+ }
604
+ static success(e, t = {}) {
605
+ return this.show({ ...t, content: e, variant: "success" });
606
+ }
607
+ static error(e, t = {}) {
608
+ return this.show({ ...t, content: e, variant: "danger" });
609
+ }
610
+ static info(e, t = {}) {
611
+ return this.show({ ...t, content: e, variant: "info" });
612
+ }
613
+ static warning(e, t = {}) {
614
+ return this.show({ ...t, content: e, variant: "warning" });
615
+ }
616
+ static closeAll() {
617
+ this.clear();
618
+ }
619
+ }
620
+ function q(n) {
621
+ return P.openModal(n);
622
+ }
623
+ function z(n) {
624
+ return P.openOffcanvas(n);
194
625
  }
195
- function T(t) {
196
- return O.openOffcanvas(t);
626
+ function Y(n) {
627
+ return V.show(n);
197
628
  }
198
629
  export {
199
- O as SoDialog,
200
- k as openModal,
201
- T as openOffcanvas
630
+ P as SoDialog,
631
+ V as SoToast,
632
+ q as openModal,
633
+ z as openOffcanvas,
634
+ Y as toast
202
635
  };
@@ -1 +1 @@
1
- (function(H,W){typeof exports=="object"&&typeof module<"u"?W(exports):typeof define=="function"&&define.amd?define(["exports"],W):(H=typeof globalThis<"u"?globalThis:H||self,W(H.SoDialog={}))})(this,(function(H){"use strict";function W(t,e){if(typeof e=="string"){t.innerHTML=e;return}t.append(e)}function I(t,e="hide"){if(e==="destroy"){t.dataset.sodDestroy="true",t.open&&t.close(),t.remove();return}t.open&&t.close()}function N(t,e){const i=e??"header";return i==="panel"?t:i==="header"?t.querySelector(".sod-header"):i==="title"?t.querySelector(".sod-title"):i==="body"?t.querySelector(".sod-body"):t.querySelector(i)??t.querySelector(".sod-header")}function T(t,e){const i=N(t,e);if(!i)return;t.classList.add("sod-modal-draggable"),i.classList.add("sod-drag-handle");let s=!1,L=0,v=0,d=null,g=0,p=0,o=0;const y=document.body.style.userSelect,c=(r,m)=>{const a=t.getBoundingClientRect(),M=Math.max(0,window.innerWidth-a.width),w=Math.max(0,window.innerHeight-a.height),$=Math.min(0,window.innerWidth-a.width),l=Math.min(0,window.innerHeight-a.height);return{left:Math.min(M,Math.max($,r)),top:Math.min(w,Math.max(l,m))}},h=r=>{if(!s)return;const m=c(r.clientX-L,r.clientY-v);p=m.left,o=m.top,g===0&&(g=window.requestAnimationFrame(()=>{g=0,t.style.inset="auto",t.style.left=`${p}px`,t.style.top=`${o}px`,t.style.right="auto",t.style.bottom="auto",t.style.margin="0",t.style.transform="none"}))},f=()=>{s=!1,i.classList.remove("is-dragging"),t.classList.remove("sod-is-dragging"),t.style.willChange="",document.body.style.userSelect=y,g!==0&&(window.cancelAnimationFrame(g),g=0),d!==null&&i.hasPointerCapture&&i.hasPointerCapture(d)&&i.releasePointerCapture(d),d=null,window.removeEventListener("pointermove",h),window.removeEventListener("pointerup",f),window.removeEventListener("pointercancel",f)};i.addEventListener("pointerdown",r=>{if(r.button!==0||r.target?.closest("button, input, select, textarea, a"))return;r.preventDefault(),t.classList.add("sod-is-dragging"),t.style.animation="none",t.style.transition="none",t.style.transform="none";const a=t.getBoundingClientRect();L=r.clientX-a.left,v=r.clientY-a.top,s=!0,d=r.pointerId,i.setPointerCapture(r.pointerId),t.style.position="fixed",t.style.left=`${a.left}px`,t.style.top=`${a.top}px`,t.style.width=`${Math.round(a.width)}px`,t.style.height=`${Math.round(a.height)}px`,t.style.inset="auto",t.style.right="auto",t.style.bottom="auto",t.style.margin="0",t.style.willChange="left, top",i.classList.add("is-dragging"),document.body.style.userSelect="none",window.addEventListener("pointermove",h),window.addEventListener("pointerup",f),window.addEventListener("pointercancel",f)})}function F(t,e,i,s,L,v){const g=Math.max(1,v?.hybridSwitchRatio??1.35),p=v?.refitOnContentChange??!0,o=Math.max(120,v?.autoFitMinWidth??280),y=Math.max(100,v?.autoFitMinHeight??160),c=v?.scrollMode?v.scrollMode:v?.autoFitUseScrollbar===!1?"viewport":"body";let h=0;const f=(n,u,E)=>Math.min(E,Math.max(u,n)),r=()=>{if(h=0,!t.isConnected||!t.open)return;const n=Math.max(o,window.innerWidth-32),u=Math.max(y,window.innerHeight-32);e.style.width="auto",e.style.height="auto",e.style.maxHeight="none",s.style.maxHeight="none",s.style.overflowY="visible",s.style.overflowX="visible";const E=Math.ceil(e.scrollWidth),b=Math.ceil(e.scrollHeight),D=E>n,R=b>u;if(c==="body"){const C=f(E,o,n),x=f(b,y,u);e.style.width=`${C}px`,e.style.maxWidth=`${n}px`,e.style.height=`${x}px`,e.style.maxHeight=`${u}px`;const S=i.offsetHeight,P=L.offsetHeight,A=Math.max(64,x-S-P);R?(s.style.maxHeight=`${A}px`,s.style.overflowY="auto"):(s.style.maxHeight="none",s.style.overflowY="hidden"),s.style.overflowX=D?"auto":"hidden",t.classList.remove("sod-modal-viewport-scroll")}else if(c==="hybrid")if(b>u*g){const x=Math.max(E,o),S=Math.max(b,y);e.style.width=`${x}px`,e.style.maxWidth="none",e.style.height=`${S}px`,e.style.maxHeight="none",s.style.maxHeight="none",s.style.overflowY="visible",s.style.overflowX="visible",t.classList.add("sod-modal-viewport-scroll")}else{const x=f(E,o,n),S=f(b,y,u);e.style.width=`${x}px`,e.style.maxWidth=`${n}px`,e.style.height=`${S}px`,e.style.maxHeight=`${u}px`;const P=i.offsetHeight,A=L.offsetHeight,X=Math.max(64,S-P-A);R?(s.style.maxHeight=`${X}px`,s.style.overflowY="auto"):(s.style.maxHeight="none",s.style.overflowY="hidden"),s.style.overflowX=D?"auto":"hidden",t.classList.remove("sod-modal-viewport-scroll")}else if(c==="viewport"){const C=Math.max(E,o),x=Math.max(b,y);e.style.width=`${C}px`,e.style.maxWidth="none",e.style.height=`${x}px`,e.style.maxHeight="none",s.style.maxHeight="none",s.style.overflowY="visible",s.style.overflowX="visible",x>u?t.classList.add("sod-modal-viewport-scroll"):t.classList.remove("sod-modal-viewport-scroll")}else{const C=D?Math.max(E,o):f(E,o,n);if(D||R){const x=Math.max(b,y);e.style.width=`${C}px`,e.style.maxWidth="none",e.style.height=`${x}px`,e.style.maxHeight="none",s.style.maxHeight="none",s.style.overflowY="visible",s.style.overflowX="visible",t.classList.add("sod-modal-viewport-scroll")}else e.style.width=`${C}px`,e.style.maxWidth=`${n}px`,e.style.height="auto",e.style.maxHeight="none",s.style.maxHeight="none",s.style.overflowY="visible",s.style.overflowX="visible",t.classList.remove("sod-modal-viewport-scroll")}},m=()=>{h===0&&(h=window.requestAnimationFrame(r))},a=new ResizeObserver(()=>{m()}),M=new MutationObserver(()=>{m()}),w=()=>{m()},$=()=>{m()};t.addEventListener("sod:refit",$),a.observe(s),a.observe(i),a.observe(L),p&&(M.observe(s,{subtree:!0,childList:!0,characterData:!0,attributes:!0}),s.addEventListener("load",w,!0),s.addEventListener("error",w,!0));const l=()=>{m()};return window.addEventListener("resize",l),m(),()=>{h!==0&&(window.cancelAnimationFrame(h),h=0),a.disconnect(),M.disconnect(),window.removeEventListener("resize",l),t.removeEventListener("sod:refit",$),s.removeEventListener("load",w,!0),s.removeEventListener("error",w,!0),t.classList.remove("sod-modal-viewport-scroll"),s.style.maxHeight="",s.style.overflowY="",s.style.overflowX=""}}class O{static modalRegistry=new Map;static modalIdSeed=0;static createAutoModalId(){return this.modalIdSeed+=1,`sod-modal-${this.modalIdSeed}`}static revealExisting(e,i){if(!e.isConnected)return;const s=e.querySelector(".sod-panel");s?.classList.remove("is-closing"),document.body.append(e),e.open||(i?e.showModal():e.show()),e.dispatchEvent(new Event("sod:refit")),s?.focus()}static open(e){const i=e.kind??"modal",s="useModal"in e?e.useModal??!0:!0,L=i==="modal"?e:void 0,v=i==="modal"&&("autoFitSize"in e?e.autoFitSize!==!1:!0);let d,g=!1,p=e.confirmAction??"hide";if(i==="modal"){const l="id"in e&&e.id?.trim()?e.id.trim():void 0;if(g=!!l,d=l??this.createAutoModalId(),e.confirmAction===void 0&&g&&(p="destroy"),l){const n=this.modalRegistry.get(d);if(n&&n.isConnected){this.revealExisting(n,s);const u={dialog:n,close:()=>I(n),refit:()=>n.dispatchEvent(new Event("sod:refit")),id:d};return"onReused"in e&&e.onReused?.(u),u}}this.modalRegistry.delete(d)}const o=document.createElement("dialog");o.className=`sod-dialog sod-${i}`;const y=[];d&&(o.dataset.sodId=d,g&&(o.dataset.sodPersistent="true"));const c=document.createElement("section");if(c.className="sod-panel",c.tabIndex=-1,i==="offcanvas"){const l="placement"in e?e.placement??"end":"end",n="animation"in e?e.animation??"slide":"slide";c.classList.add(`sod-placement-${l}`),c.classList.add(`sod-anim-${n}`)}else{const l="position"in e?e.position??"center":"center",n="animation"in e?e.animation??"fade":"fade";c.classList.add(`sod-modal-pos-${l}`),c.classList.add(`sod-modal-anim-${n}`),v&&c.classList.add("sod-modal-autofit")}const h=document.createElement("header");h.className="sod-header";const f=document.createElement("h2");f.className="sod-title",f.textContent=e.title;const r=document.createElement("button");r.type="button",r.className="sod-close",r.setAttribute("aria-label","Close"),r.textContent="×",r.addEventListener("click",()=>I(o)),h.append(f,r);const m=document.createElement("div");m.className="sod-body",W(m,e.content);const a=document.createElement("footer");a.className="sod-footer";const M=document.createElement("button");M.type="button",M.className="sod-btn sod-btn-outline",M.textContent=e.cancelText??"取消",M.addEventListener("click",()=>{e.onCancel?.(),I(o)});const w=document.createElement("button");w.type="button",w.className="sod-btn sod-btn-primary",w.textContent=e.confirmText??"确认",w.addEventListener("click",()=>{e.onConfirm?.(),I(o,p)}),a.append(M,w),c.append(h,m,a),v&&y.push(F(o,c,h,m,a,L)),i==="modal"&&"draggable"in e&&e.draggable&&T(c,e.dragHandle),o.append(c),(e.closeOnBackdrop??!0)&&o.addEventListener("click",l=>{l.target===o&&I(o)}),(e.closeOnEsc??!0)||o.addEventListener("cancel",l=>{l.preventDefault()}),o.addEventListener("close",()=>{const l=o.dataset.sodPersistent==="true",n=o.dataset.sodDestroy==="true";(!l||n)&&(y.forEach(u=>u()),o.remove(),d&&this.modalRegistry.delete(d),delete o.dataset.sodDestroy)}),document.body.append(o),s?o.showModal():o.show(),o.dispatchEvent(new Event("sod:refit")),d&&this.modalRegistry.set(d,o);const $={dialog:o,close:()=>I(o),refit:()=>o.dispatchEvent(new Event("sod:refit")),id:d};return i==="modal"&&"onCreated"in e&&e.onCreated?.($),$}static openModal(e){return this.open({...e,kind:"modal"})}static openOffcanvas(e){return this.open({...e,kind:"offcanvas"})}}function Y(t){return O.openModal(t)}function k(t){return O.openOffcanvas(t)}H.SoDialog=O,H.openModal=Y,H.openOffcanvas=k,Object.defineProperty(H,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(H,B){typeof exports=="object"&&typeof module<"u"?B(exports):typeof define=="function"&&define.amd?define(["exports"],B):(H=typeof globalThis<"u"?globalThis:H||self,B(H.SoDialog={}))})(this,(function(H){"use strict";function B(n,e){if(typeof e=="string"){n.innerHTML=e;return}n.append(e)}function I(n,e="hide"){if(e==="destroy"){n.dataset.sodDestroy="true",n.open&&n.close(),n.remove();return}n.open&&n.close()}function F(n,e){return n.action?n.action:n.role==="confirm"?e:n.role==="cancel"?"hide":"none"}function k(n){return n==="danger"?"sod-btn-danger":n==="success"?"sod-btn-success":n==="ghost"?"sod-btn-ghost":n==="link"?"sod-btn-link":n==="outline"?"sod-btn-outline":"sod-btn-primary"}function D(n,e){const t=Array.isArray(e)?e.length>0?e:["header"]:[e??"header"],s=[],i=o=>{o&&(s.includes(o)||s.push(o))};for(const o of t){if(o==="panel"){i(n);continue}if(o==="header"){i(n.querySelector(".sod-header"));continue}if(o==="title"){i(n.querySelector(".sod-title"));continue}if(o==="body"){i(n.querySelector(".sod-body"));continue}n.querySelectorAll(o).forEach(r=>{i(r)})}return s.length===0&&i(n.querySelector(".sod-header")),s}function V(n,e){const t=D(n,e);if(t.length===0)return;n.classList.add("sod-modal-draggable"),t.forEach(m=>{m.classList.add("sod-drag-handle")});let s=!1,i=0,o=0,r=null,d=null,h=0,a=0,f=0;const v=document.body.style.userSelect,E=(m,u)=>{const y=n.getBoundingClientRect(),p=Math.max(0,window.innerWidth-y.width),O=Math.max(0,window.innerHeight-y.height),T=Math.min(0,window.innerWidth-y.width),b=Math.min(0,window.innerHeight-y.height);return{left:Math.min(p,Math.max(T,m)),top:Math.min(O,Math.max(b,u))}},R=m=>{if(!s)return;const u=E(m.clientX-i,m.clientY-o);a=u.left,f=u.top,h===0&&(h=window.requestAnimationFrame(()=>{h=0,n.style.inset="auto",n.style.left=`${a}px`,n.style.top=`${f}px`,n.style.right="auto",n.style.bottom="auto",n.style.margin="0",n.style.transform="none"}))},x=()=>{s=!1,t.forEach(m=>{m.classList.remove("is-dragging")}),n.classList.remove("sod-is-dragging"),n.style.willChange="",document.body.style.userSelect=v,h!==0&&(window.cancelAnimationFrame(h),h=0),r!==null&&d!==null&&d.hasPointerCapture&&d.hasPointerCapture(r)&&d.releasePointerCapture(r),r=null,d=null,window.removeEventListener("pointermove",R),window.removeEventListener("pointerup",x),window.removeEventListener("pointercancel",x)};t.forEach(m=>{m.addEventListener("pointerdown",u=>{if(u.button!==0||u.target?.closest("button, input, select, textarea, a"))return;u.preventDefault(),n.classList.add("sod-is-dragging"),n.style.animation="none",n.style.transition="none",n.style.transform="none";const p=n.getBoundingClientRect();i=u.clientX-p.left,o=u.clientY-p.top,s=!0,r=u.pointerId,d=m,m.setPointerCapture(u.pointerId),n.style.position="fixed",n.style.left=`${p.left}px`,n.style.top=`${p.top}px`,n.style.width=`${Math.round(p.width)}px`,n.style.height=`${Math.round(p.height)}px`,n.style.inset="auto",n.style.right="auto",n.style.bottom="auto",n.style.margin="0",n.style.willChange="left, top",m.classList.add("is-dragging"),document.body.style.userSelect="none",window.addEventListener("pointermove",R),window.addEventListener("pointerup",x),window.addEventListener("pointercancel",x)})})}function q(n,e,t,s,i,o){const d=Math.max(1,o?.hybridSwitchRatio??1.35),h=o?.refitOnContentChange??!0,a=Math.max(120,o?.autoFitMinWidth??280),f=Math.max(100,o?.autoFitMinHeight??160),v=o?.scrollMode?o.scrollMode:o?.autoFitUseScrollbar===!1?"viewport":"body";let E=0;const R=(b,M,L)=>Math.min(L,Math.max(M,b)),x=()=>{if(E=0,!n.isConnected||!n.open)return;const b=Math.max(a,window.innerWidth-32),M=Math.max(f,window.innerHeight-32);e.style.width="auto",e.style.height="auto",e.style.maxHeight="none",s.style.maxHeight="none",s.style.overflowY="visible",s.style.overflowX="visible";const L=Math.ceil(e.scrollWidth),l=Math.ceil(e.scrollHeight),c=L>b,g=l>M;if(v==="body"){const w=R(L,a,b),S=R(l,f,M);e.style.width=`${w}px`,e.style.maxWidth=`${b}px`,e.style.height=`${S}px`,e.style.maxHeight=`${M}px`;const A=t.offsetHeight,P=i.offsetHeight,C=Math.max(64,S-A-P);g?(s.style.maxHeight=`${C}px`,s.style.overflowY="auto"):(s.style.maxHeight="none",s.style.overflowY="hidden"),s.style.overflowX=c?"auto":"hidden",n.classList.remove("sod-modal-viewport-scroll")}else if(v==="hybrid")if(l>M*d){const S=Math.max(L,a),A=Math.max(l,f);e.style.width=`${S}px`,e.style.maxWidth="none",e.style.height=`${A}px`,e.style.maxHeight="none",s.style.maxHeight="none",s.style.overflowY="visible",s.style.overflowX="visible",n.classList.add("sod-modal-viewport-scroll")}else{const S=R(L,a,b),A=R(l,f,M);e.style.width=`${S}px`,e.style.maxWidth=`${b}px`,e.style.height=`${A}px`,e.style.maxHeight=`${M}px`;const P=t.offsetHeight,C=i.offsetHeight,$=Math.max(64,A-P-C);g?(s.style.maxHeight=`${$}px`,s.style.overflowY="auto"):(s.style.maxHeight="none",s.style.overflowY="hidden"),s.style.overflowX=c?"auto":"hidden",n.classList.remove("sod-modal-viewport-scroll")}else if(v==="viewport"){const w=Math.max(L,a),S=Math.max(l,f);e.style.width=`${w}px`,e.style.maxWidth="none",e.style.height=`${S}px`,e.style.maxHeight="none",s.style.maxHeight="none",s.style.overflowY="visible",s.style.overflowX="visible",S>M?n.classList.add("sod-modal-viewport-scroll"):n.classList.remove("sod-modal-viewport-scroll")}else{const w=c?Math.max(L,a):R(L,a,b);if(c||g){const S=Math.max(l,f);e.style.width=`${w}px`,e.style.maxWidth="none",e.style.height=`${S}px`,e.style.maxHeight="none",s.style.maxHeight="none",s.style.overflowY="visible",s.style.overflowX="visible",n.classList.add("sod-modal-viewport-scroll")}else e.style.width=`${w}px`,e.style.maxWidth=`${b}px`,e.style.height="auto",e.style.maxHeight="none",s.style.maxHeight="none",s.style.overflowY="visible",s.style.overflowX="visible",n.classList.remove("sod-modal-viewport-scroll")}},m=()=>{E===0&&(E=window.requestAnimationFrame(x))},u=new ResizeObserver(()=>{m()}),y=new MutationObserver(()=>{m()}),p=()=>{m()},O=()=>{m()};n.addEventListener("sod:refit",O),u.observe(s),u.observe(t),u.observe(i),h&&(y.observe(s,{subtree:!0,childList:!0,characterData:!0,attributes:!0}),s.addEventListener("load",p,!0),s.addEventListener("error",p,!0));const T=()=>{m()};return window.addEventListener("resize",T),m(),()=>{E!==0&&(window.cancelAnimationFrame(E),E=0),u.disconnect(),y.disconnect(),window.removeEventListener("resize",T),n.removeEventListener("sod:refit",O),s.removeEventListener("load",p,!0),s.removeEventListener("error",p,!0),n.classList.remove("sod-modal-viewport-scroll"),s.style.maxHeight="",s.style.overflowY="",s.style.overflowX=""}}class W{static modalRegistry=new Map;static handleRegistry=new WeakMap;static modalIdSeed=0;static createAutoModalId(){return this.modalIdSeed+=1,`sod-modal-${this.modalIdSeed}`}static revealExisting(e,t){if(!e.isConnected)return{dialog:e,close:()=>I(e),refit:()=>e.dispatchEvent(new Event("sod:refit")),setFooterButtons:()=>{},updateFooterButton:()=>!1,onAction:()=>()=>{},id:e.dataset.sodId};const s=e.querySelector(".sod-panel");s?.classList.remove("is-closing"),document.body.append(e),e.open||(t?e.showModal():e.show()),e.dispatchEvent(new Event("sod:refit")),s?.focus();const i=this.handleRegistry.get(e);return i?i():{dialog:e,close:()=>I(e),refit:()=>e.dispatchEvent(new Event("sod:refit")),setFooterButtons:()=>{},updateFooterButton:()=>!1,onAction:()=>()=>{},id:e.dataset.sodId}}static open(e){const t=e.kind??"modal",s="useModal"in e?e.useModal??!0:!0,i=t==="modal"?e:void 0,o=t==="modal"&&("autoFitSize"in e?e.autoFitSize!==!1:!0);let r,d=!1,h=e.confirmAction??"hide";if(t==="modal"){const l="id"in e&&e.id?.trim()?e.id.trim():void 0;if(d=!!l,r=l??this.createAutoModalId(),e.confirmAction===void 0&&d&&(h="destroy"),l){const c=this.modalRegistry.get(r);if(c&&c.isConnected){const g=this.revealExisting(c,s);if(e.onAction){const w=g.onAction(e.onAction);c.addEventListener("close",w,{once:!0})}return"onReused"in e&&e.onReused?.(g),g}}this.modalRegistry.delete(r)}const a=document.createElement("dialog");a.className=`sod-dialog sod-${t}`;const f=[];r&&(a.dataset.sodId=r,d&&(a.dataset.sodPersistent="true"));const v=document.createElement("section");if(v.className="sod-panel",v.tabIndex=-1,t==="offcanvas"){const l="placement"in e?e.placement??"end":"end",c="animation"in e?e.animation??"slide":"slide";v.classList.add(`sod-placement-${l}`),v.classList.add(`sod-anim-${c}`)}else{const l="position"in e?e.position??"center":"center",c="animation"in e?e.animation??"fade":"fade";v.classList.add(`sod-modal-pos-${l}`),v.classList.add(`sod-modal-anim-${c}`),o&&v.classList.add("sod-modal-autofit")}const E=document.createElement("header");E.className="sod-header";const R=document.createElement("h2");R.className="sod-title",R.textContent=e.title;const x=document.createElement("button");x.type="button",x.className="sod-close",x.setAttribute("aria-label","Close"),x.textContent="×",x.addEventListener("click",()=>I(a)),E.append(R,x);const m=document.createElement("div");m.className="sod-body",B(m,e.content);const u=document.createElement("footer");u.className="sod-footer",u.dataset.align=e.footerAlign??"end";const y=new Set;e.onAction&&y.add(e.onAction);const p=l=>(y.add(l),()=>{y.delete(l)});let O=e.footerButtons?[...e.footerButtons]:[{id:"cancel",label:e.cancelText??"取消",role:"cancel",variant:"outline"},{id:"confirm",label:e.confirmText??"确认",role:"confirm",variant:"primary"}];const T=()=>({dialog:a,close:()=>I(a),refit:()=>a.dispatchEvent(new Event("sod:refit")),setFooterButtons:l=>{O=[...l],M(),a.dispatchEvent(new Event("sod:refit"))},updateFooterButton:(l,c)=>{const g=O.findIndex(w=>w.id===l);return g<0?!1:(O[g]={...O[g],...c},M(),a.dispatchEvent(new Event("sod:refit")),!0)},onAction:l=>p(l),id:r}),b=(l,c)=>{if(typeof c=="string"){l.textContent=c;return}l.append(c)},M=()=>{if(u.replaceChildren(),e.hideFooter||O.length===0){u.hidden=!0;return}u.hidden=!1;for(const l of O){const c=document.createElement("button");c.type="button",c.className=`sod-btn ${k(l.variant)}`,l.className?.trim()&&(c.className=`${c.className} ${l.className.trim()}`),l.id?.trim()&&(c.dataset.action=l.id.trim()),l.disabled&&(c.disabled=!0),l.attrs&&Object.entries(l.attrs).forEach(([g,w])=>{c.setAttribute(g,w)}),b(c,l.label),c.addEventListener("click",g=>{(async()=>{const w=T(),A={action:l.id??l.role??"custom",button:l,buttonElement:c,dialog:a,event:g,handle:w};if(await l.onClick?.(A)===!1)return;l.role==="cancel"&&e.onCancel?.(),l.role==="confirm"&&e.onConfirm?.(),y.forEach($=>{$(A)});const C=F(l,h);C==="hide"&&I(a,"hide"),C==="destroy"&&I(a,"destroy")})()}),u.append(c)}};M(),v.append(E,m,u),o&&f.push(q(a,v,E,m,u,i)),t==="modal"&&"draggable"in e&&e.draggable&&V(v,e.dragHandle),a.append(v),(e.closeOnBackdrop??!0)&&a.addEventListener("click",l=>{l.target===a&&I(a)}),(e.closeOnEsc??!0)||a.addEventListener("cancel",l=>{l.preventDefault()}),a.addEventListener("close",()=>{const l=a.dataset.sodPersistent==="true",c=a.dataset.sodDestroy==="true";(!l||c)&&(f.forEach(g=>g()),y.clear(),a.remove(),r&&this.modalRegistry.delete(r),this.handleRegistry.delete(a),delete a.dataset.sodDestroy)}),document.body.append(a),s?a.showModal():a.show(),a.dispatchEvent(new Event("sod:refit")),r&&this.modalRegistry.set(r,a),this.handleRegistry.set(a,T);const L=T();return t==="modal"&&"onCreated"in e&&e.onCreated?.(L),L}static openModal(e){return this.open({...e,kind:"modal"})}static openOffcanvas(e){return this.open({...e,kind:"offcanvas"})}}class N{static placementState=new Map;static recordById=new Map;static idSeed=0;static defaults={placement:"top-end",variant:"default",duration:3e3,showProgress:!0,closable:!0,pauseOnHover:!0,pauseOnWindowBlur:!1,duplicateStrategy:"update",newestOnTop:!0,maxVisible:3};static normalizeOptions(e){const t=e.duration===!1?!1:Math.max(0,e.duration??this.defaults.duration);return{placement:e.placement??this.defaults.placement,variant:e.variant??this.defaults.variant,duration:t,showProgress:e.showProgress??this.defaults.showProgress,closable:e.closable??this.defaults.closable,pauseOnHover:e.pauseOnHover??this.defaults.pauseOnHover,pauseOnWindowBlur:e.pauseOnWindowBlur??this.defaults.pauseOnWindowBlur,duplicateStrategy:e.duplicateStrategy??this.defaults.duplicateStrategy,newestOnTop:e.newestOnTop??this.defaults.newestOnTop,maxVisible:Math.max(1,e.maxVisible??this.defaults.maxVisible),title:e.title,content:e.content,className:e.className,attrs:e.attrs,onShown:e.onShown,onClose:e.onClose}}static createAutoToastId(){return this.idSeed+=1,`sot-toast-${this.idSeed}`}static createStackedToastId(e){let t=1,s=`${e}-${t}`;for(;this.recordById.has(s);)t+=1,s=`${e}-${t}`;return s}static resolveRole(e){return e==="danger"?"alert":"status"}static getPlacementState(e){const t=this.placementState.get(e);if(t)return t;const s=document.createElement("div");s.className=`sod-toast-layer sod-toast-layer-${e}`,s.setAttribute("aria-live","polite"),s.setAttribute("aria-atomic","false"),document.body.append(s);const i={container:s,active:[],pending:[]};return this.placementState.set(e,i),i}static createToastElement(e){const{options:t}=e,s=document.createElement("article");s.className=`sod-toast sod-toast-${t.variant}`,t.className?.trim()&&(s.className=`${s.className} ${t.className.trim()}`),s.dataset.toastId=e.id,s.setAttribute("role",this.resolveRole(t.variant)),t.attrs&&Object.entries(t.attrs).forEach(([a,f])=>{s.setAttribute(a,f)});const i=document.createElement("header");i.className="sod-toast-header";let o=null;t.title?.trim()&&(o=document.createElement("strong"),o.className="sod-toast-title",o.textContent=t.title,i.append(o));let r=null;t.closable&&(r=document.createElement("button"),r.type="button",r.className="sod-toast-close",r.setAttribute("aria-label","Close"),r.textContent="×",r.addEventListener("click",()=>{this.closeRecord(e,"close-button")}),i.append(r)),i.childElementCount>0&&s.append(i);const d=document.createElement("div");d.className="sod-toast-body",B(d,t.content),s.append(d);let h=null;if(t.showProgress&&(h=document.createElement("div"),h.className="sod-toast-progress",s.append(h)),t.pauseOnHover){const a=()=>this.pauseRecord(e),f=()=>this.resumeRecord(e);s.addEventListener("mouseenter",a),s.addEventListener("mouseleave",f),e.cleanupListeners.push(()=>{s.removeEventListener("mouseenter",a),s.removeEventListener("mouseleave",f)})}if(t.pauseOnWindowBlur){const a=()=>{e.paused||e.status!=="active"||(e.pausedByWindowBlur=!0,this.pauseRecord(e))},f=()=>{e.pausedByWindowBlur&&(e.pausedByWindowBlur=!1,this.resumeRecord(e))};window.addEventListener("blur",a),window.addEventListener("focus",f),e.cleanupListeners.push(()=>{window.removeEventListener("blur",a),window.removeEventListener("focus",f)})}e.element=s,e.bodyElement=d,e.titleElement=o,e.closeButton=r,e.progressElement=h}static resetProgressAnimation(e,t){!e.progressElement||!e.options.showProgress||(e.progressElement.style.display="block",e.progressElement.style.animation="none",e.progressElement.style.animationPlayState="running",e.progressElement.offsetWidth,e.progressElement.style.animation=`sod-toast-progress-countdown ${Math.max(1,t)}ms linear forwards`)}static hideProgress(e){e.progressElement&&(e.progressElement.style.display="none",e.progressElement.style.animation="none")}static isRecordActive(e,t){return e.active.includes(t)}static isRecordPending(e,t){return e.pending.includes(t)}static mountRecord(e,t){t.status="active",t.options.newestOnTop?e.container.prepend(t.element):e.container.append(t.element),e.active.push(t),this.startRecordTimer(t),t.options.onShown?.(t.handle)}static queueRecord(e,t){t.status="pending",e.pending.push(t)}static startRecordTimer(e){if(this.clearRecordTimer(e),e.options.duration===!1){e.remainingMs=!1,e.startedAt=null,this.hideProgress(e);return}const t=Math.max(0,e.remainingMs===!1?e.options.duration:e.remainingMs);if(t===0){this.closeRecord(e,"timeout");return}e.startedAt=Date.now(),e.remainingMs=t,this.resetProgressAnimation(e,t),e.timerId=window.setTimeout(()=>{e.timerId=null,this.closeRecord(e,"timeout")},t)}static clearRecordTimer(e){e.timerId!==null&&(window.clearTimeout(e.timerId),e.timerId=null)}static pauseRecord(e){if(e.paused||e.status!=="active"||e.options.duration===!1)return;const t=e.startedAt??Date.now(),s=Math.max(0,Date.now()-t),i=e.remainingMs===!1?e.options.duration:e.remainingMs;e.remainingMs=Math.max(0,i-s),e.startedAt=null,e.paused=!0,e.progressElement&&(e.progressElement.style.animationPlayState="paused"),this.clearRecordTimer(e)}static resumeRecord(e){!e.paused||e.status!=="active"||(e.paused=!1,e.progressElement&&(e.progressElement.style.animationPlayState="running"),this.startRecordTimer(e))}static rebuildRecordElement(e){const t=e.element,s=t.parentElement;e.cleanupListeners.forEach(i=>i()),e.cleanupListeners=[],t.remove(),this.createToastElement(e),s&&e.status==="active"&&(e.options.newestOnTop?s.prepend(e.element):s.append(e.element),this.startRecordTimer(e))}static moveRecordToPlacement(e,t){const s=e.options.placement;if(s===t)return;const i=this.placementState.get(s),o=this.getPlacementState(t),r=!!(i&&this.removeFromArray(i.active,e)),d=!!(i&&this.removeFromArray(i.pending,e));if(e.options.placement=t,this.clearRecordTimer(e),d&&!r){this.queueRecord(o,e),this.drainQueue(o,t),i&&this.drainQueue(i,s);return}o.active.length<e.options.maxVisible?this.mountRecord(o,e):this.queueRecord(o,e),i&&this.drainQueue(i,s)}static updateRecord(e,t){if(t.title!==void 0){const i=t.title.trim();if(e.options.title=i.length>0?i:void 0,e.options.title)if(e.titleElement)e.titleElement.textContent=e.options.title;else{const o=e.element.querySelector(".sod-toast-header");if(o){const r=document.createElement("strong");r.className="sod-toast-title",r.textContent=e.options.title,o.prepend(r),e.titleElement=r}}else e.titleElement&&(e.titleElement.remove(),e.titleElement=null)}t.content!==void 0&&(e.options.content=t.content,e.bodyElement.replaceChildren(),B(e.bodyElement,t.content)),t.variant!==void 0&&(e.options.variant=t.variant,e.element.classList.remove("sod-toast-default","sod-toast-info","sod-toast-success","sod-toast-warning","sod-toast-danger"),e.element.classList.add(`sod-toast-${t.variant}`),e.element.setAttribute("role",this.resolveRole(t.variant))),t.duration!==void 0&&(e.options.duration=t.duration===!1?!1:Math.max(0,t.duration),e.remainingMs=e.options.duration===!1?!1:e.options.duration,e.paused=!1,e.status==="active"&&this.startRecordTimer(e)),t.maxVisible!==void 0&&(e.options.maxVisible=Math.max(1,t.maxVisible)),t.newestOnTop!==void 0&&(e.options.newestOnTop=t.newestOnTop);let s=!1;t.closable!==void 0&&t.closable!==e.options.closable&&(e.options.closable=t.closable,s=!0),t.pauseOnHover!==void 0&&t.pauseOnHover!==e.options.pauseOnHover&&(e.options.pauseOnHover=t.pauseOnHover,s=!0),t.pauseOnWindowBlur!==void 0&&t.pauseOnWindowBlur!==e.options.pauseOnWindowBlur&&(e.options.pauseOnWindowBlur=t.pauseOnWindowBlur,s=!0),t.duplicateStrategy!==void 0&&(e.options.duplicateStrategy=t.duplicateStrategy),t.showProgress!==void 0&&t.showProgress!==e.options.showProgress&&(e.options.showProgress=t.showProgress,s=!0),t.className!==void 0&&(e.options.className=t.className,s=!0),t.attrs!==void 0&&(e.options.attrs=t.attrs,s=!0),s&&this.rebuildRecordElement(e),t.placement&&t.placement!==e.options.placement&&this.moveRecordToPlacement(e,t.placement)}static removeStateContainerIfEmpty(e){const t=this.placementState.get(e);t&&(t.active.length>0||t.pending.length>0||(t.container.remove(),this.placementState.delete(e)))}static drainQueue(e,t){let s=!0;for(;s&&e.pending.length>0;){const i=e.pending[0];if(e.active.length>=i.options.maxVisible){s=!1;continue}e.pending.shift(),this.mountRecord(e,i)}this.removeStateContainerIfEmpty(t)}static removeFromArray(e,t){const s=e.indexOf(t);return s<0?!1:(e.splice(s,1),!0)}static finalizeClose(e,t){if(e.status==="closed")return;const s=e.options.placement,i=this.placementState.get(s);this.clearRecordTimer(e),e.cleanupListeners.forEach(o=>o()),e.cleanupListeners=[],e.element.remove(),e.status="closed",i&&(this.removeFromArray(i.active,e),this.removeFromArray(i.pending,e)),this.recordById.delete(e.id),e.options.onClose?.(t,e.handle),i?this.drainQueue(i,s):this.removeStateContainerIfEmpty(s)}static closeRecord(e,t){if(e.status==="closed"||e.status==="closing")return;const s=e.options.placement,i=this.placementState.get(s);if(!!(i&&this.isRecordPending(i,e)&&!this.isRecordActive(i,e))){this.finalizeClose(e,t);return}e.status="closing",this.clearRecordTimer(e),e.element.classList.add("is-closing");let r=!1;const d=()=>{r||(r=!0,this.finalizeClose(e,t))},h=a=>{a.target===e.element&&(e.element.removeEventListener("animationend",h),d())};e.element.addEventListener("animationend",h),window.setTimeout(()=>{e.element.removeEventListener("animationend",h),d()},220)}static createHandle(e){return{id:e.id,get element(){return e.element},close:(t="programmatic")=>{this.closeRecord(e,t)},update:t=>{this.updateRecord(e,t)},pause:()=>{this.pauseRecord(e)},resume:()=>{this.resumeRecord(e)}}}static show(e){const s=e.id?.trim()||this.createAutoToastId(),i=this.normalizeOptions(e),o=this.recordById.get(s);if(o){const a=i.duplicateStrategy;if(a==="ignore")return o.handle;if(a==="stack"){const f=this.createStackedToastId(s);return this.show({...e,id:f,duplicateStrategy:"update"})}return this.updateRecord(o,{title:e.title,content:e.content,variant:e.variant,duration:a==="restart-timer"?e.duration===void 0?o.options.duration:i.duration:e.duration===void 0?void 0:i.duration,placement:e.placement,closable:e.closable,pauseOnHover:e.pauseOnHover,pauseOnWindowBlur:e.pauseOnWindowBlur,duplicateStrategy:e.duplicateStrategy,newestOnTop:e.newestOnTop,maxVisible:e.maxVisible,showProgress:e.showProgress,className:e.className,attrs:e.attrs}),o.options.onClose=i.onClose,o.options.onShown=i.onShown,o.handle}const d={id:s,options:i,element:document.createElement("article"),status:"pending",timerId:null,remainingMs:i.duration,startedAt:null,paused:!1,pausedByWindowBlur:!1,closeButton:null,progressElement:null,bodyElement:document.createElement("div"),titleElement:null,cleanupListeners:[],handle:void 0};d.handle=this.createHandle(d),this.createToastElement(d),this.recordById.set(s,d);const h=this.getPlacementState(i.placement);return h.active.length<i.maxVisible?this.mountRecord(h,d):this.queueRecord(h,d),d.handle}static clear(e){(e?[e]:Array.from(this.placementState.keys())).forEach(s=>{const i=this.placementState.get(s);if(!i)return;const o=[...i.active];[...i.pending].forEach(d=>this.closeRecord(d,"container-clear")),o.forEach(d=>this.closeRecord(d,"container-clear"))})}static configure(e){e.placement&&(this.defaults.placement=e.placement),e.variant&&(this.defaults.variant=e.variant),e.duration!==void 0&&(this.defaults.duration=e.duration===!1?3e3:Math.max(0,e.duration)),e.showProgress!==void 0&&(this.defaults.showProgress=e.showProgress),e.closable!==void 0&&(this.defaults.closable=e.closable),e.pauseOnHover!==void 0&&(this.defaults.pauseOnHover=e.pauseOnHover),e.pauseOnWindowBlur!==void 0&&(this.defaults.pauseOnWindowBlur=e.pauseOnWindowBlur),e.duplicateStrategy!==void 0&&(this.defaults.duplicateStrategy=e.duplicateStrategy),e.newestOnTop!==void 0&&(this.defaults.newestOnTop=e.newestOnTop),e.maxVisible!==void 0&&(this.defaults.maxVisible=Math.max(1,e.maxVisible))}static success(e,t={}){return this.show({...t,content:e,variant:"success"})}static error(e,t={}){return this.show({...t,content:e,variant:"danger"})}static info(e,t={}){return this.show({...t,content:e,variant:"info"})}static warning(e,t={}){return this.show({...t,content:e,variant:"warning"})}static closeAll(){this.clear()}}function z(n){return W.openModal(n)}function Y(n){return W.openOffcanvas(n)}function X(n){return N.show(n)}H.SoDialog=W,H.SoToast=N,H.openModal=z,H.openOffcanvas=Y,H.toast=X,Object.defineProperty(H,Symbol.toStringTag,{value:"Module"})}));
@@ -4,8 +4,27 @@ export type SoOffcanvasPlacement = 'start' | 'end' | 'top' | 'bottom';
4
4
  export type SoOffcanvasAnimation = 'slide' | 'fade' | 'zoom';
5
5
  export type SoModalPosition = 'center' | 'top' | 'bottom';
6
6
  export type SoModalAnimation = 'slide' | 'fade' | 'zoom';
7
- export type SoModalDragHandle = 'header' | 'title' | 'body' | 'panel' | string;
7
+ export type SoModalDragHandleTarget = 'header' | 'title' | 'body' | 'panel' | string;
8
+ export type SoModalDragHandle = SoModalDragHandleTarget | SoModalDragHandleTarget[];
8
9
  export type SoModalScrollMode = 'body' | 'viewport' | 'none' | 'hybrid';
10
+ export type SoFooterButtonVariant = 'primary' | 'outline' | 'danger' | 'success' | 'ghost' | 'link';
11
+ export type SoFooterAlign = 'start' | 'center' | 'end' | 'between';
12
+ export type SoFooterButtonAction = 'none' | 'hide' | 'destroy';
13
+ export type SoToastPlacement = 'top-start' | 'top-center' | 'top-end' | 'bottom-start' | 'bottom-center' | 'bottom-end';
14
+ export type SoToastVariant = 'default' | 'info' | 'success' | 'warning' | 'danger';
15
+ export type SoToastCloseReason = 'timeout' | 'manual' | 'close-button' | 'container-clear' | 'programmatic';
16
+ export type SoToastDuplicateStrategy = 'update' | 'ignore' | 'restart-timer' | 'stack';
17
+ export interface SoDialogFooterButton {
18
+ id?: string;
19
+ label: string | Node;
20
+ role?: 'confirm' | 'cancel' | 'custom';
21
+ variant?: SoFooterButtonVariant;
22
+ className?: string;
23
+ action?: SoFooterButtonAction;
24
+ disabled?: boolean;
25
+ attrs?: Record<string, string>;
26
+ onClick?: (context: SoDialogFooterActionContext) => void | boolean | Promise<void | boolean>;
27
+ }
9
28
  export interface SoDialogBaseOptions {
10
29
  title: string;
11
30
  content: string | Node;
@@ -16,6 +35,10 @@ export interface SoDialogBaseOptions {
16
35
  closeOnEsc?: boolean;
17
36
  onConfirm?: () => void;
18
37
  onCancel?: () => void;
38
+ footerButtons?: SoDialogFooterButton[];
39
+ hideFooter?: boolean;
40
+ footerAlign?: SoFooterAlign;
41
+ onAction?: SoDialogActionListener;
19
42
  }
20
43
  export interface SoDialogModalOptions extends SoDialogBaseOptions {
21
44
  id?: string;
@@ -45,10 +68,50 @@ export interface SoDialogHandle {
45
68
  dialog: HTMLDialogElement;
46
69
  close: () => void;
47
70
  refit: () => void;
71
+ setFooterButtons: (buttons: SoDialogFooterButton[]) => void;
72
+ updateFooterButton: (id: string, updates: Partial<SoDialogFooterButton>) => boolean;
73
+ onAction: (listener: SoDialogActionListener) => () => void;
48
74
  id?: string;
49
75
  }
76
+ export interface SoDialogFooterActionContext {
77
+ action: string;
78
+ button: SoDialogFooterButton;
79
+ buttonElement: HTMLButtonElement;
80
+ dialog: HTMLDialogElement;
81
+ event: MouseEvent;
82
+ handle: SoDialogHandle;
83
+ }
84
+ export type SoDialogActionListener = (context: SoDialogFooterActionContext) => void;
85
+ export interface SoToastOptions {
86
+ id?: string;
87
+ title?: string;
88
+ content: string | Node;
89
+ placement?: SoToastPlacement;
90
+ variant?: SoToastVariant;
91
+ duration?: number | false;
92
+ showProgress?: boolean;
93
+ closable?: boolean;
94
+ pauseOnHover?: boolean;
95
+ pauseOnWindowBlur?: boolean;
96
+ duplicateStrategy?: SoToastDuplicateStrategy;
97
+ newestOnTop?: boolean;
98
+ maxVisible?: number;
99
+ className?: string;
100
+ attrs?: Record<string, string>;
101
+ onShown?: (handle: SoToastHandle) => void;
102
+ onClose?: (reason: SoToastCloseReason, handle: SoToastHandle) => void;
103
+ }
104
+ export interface SoToastHandle {
105
+ id: string;
106
+ element: HTMLElement;
107
+ close: (reason?: Exclude<SoToastCloseReason, 'timeout' | 'close-button'>) => void;
108
+ update: (patch: Partial<Pick<SoToastOptions, 'title' | 'content' | 'variant' | 'duration'>>) => void;
109
+ pause: () => void;
110
+ resume: () => void;
111
+ }
50
112
  export declare class SoDialog {
51
113
  private static modalRegistry;
114
+ private static handleRegistry;
52
115
  private static modalIdSeed;
53
116
  private static createAutoModalId;
54
117
  private static revealExisting;
@@ -56,5 +119,45 @@ export declare class SoDialog {
56
119
  static openModal(options: SoDialogModalOptions): SoDialogHandle;
57
120
  static openOffcanvas(options: Omit<SoDialogOffcanvasOptions, 'kind'>): SoDialogHandle;
58
121
  }
122
+ export declare class SoToast {
123
+ private static placementState;
124
+ private static recordById;
125
+ private static idSeed;
126
+ private static defaults;
127
+ private static normalizeOptions;
128
+ private static createAutoToastId;
129
+ private static createStackedToastId;
130
+ private static resolveRole;
131
+ private static getPlacementState;
132
+ private static createToastElement;
133
+ private static resetProgressAnimation;
134
+ private static hideProgress;
135
+ private static isRecordActive;
136
+ private static isRecordPending;
137
+ private static mountRecord;
138
+ private static queueRecord;
139
+ private static startRecordTimer;
140
+ private static clearRecordTimer;
141
+ private static pauseRecord;
142
+ private static resumeRecord;
143
+ private static rebuildRecordElement;
144
+ private static moveRecordToPlacement;
145
+ private static updateRecord;
146
+ private static removeStateContainerIfEmpty;
147
+ private static drainQueue;
148
+ private static removeFromArray;
149
+ private static finalizeClose;
150
+ private static closeRecord;
151
+ private static createHandle;
152
+ static show(options: SoToastOptions): SoToastHandle;
153
+ static clear(placement?: SoToastPlacement): void;
154
+ static configure(defaults: Partial<SoToastOptions>): void;
155
+ static success(content: string | Node, options?: Omit<SoToastOptions, 'content' | 'variant'>): SoToastHandle;
156
+ static error(content: string | Node, options?: Omit<SoToastOptions, 'content' | 'variant'>): SoToastHandle;
157
+ static info(content: string | Node, options?: Omit<SoToastOptions, 'content' | 'variant'>): SoToastHandle;
158
+ static warning(content: string | Node, options?: Omit<SoToastOptions, 'content' | 'variant'>): SoToastHandle;
159
+ static closeAll(): void;
160
+ }
59
161
  export declare function openModal(options: SoDialogModalOptions): SoDialogHandle;
60
162
  export declare function openOffcanvas(options: Omit<SoDialogOffcanvasOptions, 'kind'>): SoDialogHandle;
163
+ export declare function toast(options: SoToastOptions): SoToastHandle;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sodialog",
3
- "version": "0.1.11",
3
+ "version": "0.1.14",
4
4
  "description": "A reusable HTML5 dialog-based modal and offcanvas library.",
5
5
  "keywords": [
6
6
  "dialog",
@@ -38,6 +38,8 @@
38
38
  "build": "vite build && npm run build:types",
39
39
  "build:types": "tsc -p tsconfig.build.json",
40
40
  "build:demo": "vite build --mode demo",
41
+ "release:check": "node scripts/check-release-version.mjs",
42
+ "docs:changelog": "node scripts/generate-changelog.mjs",
41
43
  "preview": "vite preview",
42
44
  "lint": "eslint .",
43
45
  "prepublishOnly": "npm run lint && npm run build"