best-dialog 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,282 @@
1
+ # best-dialog
2
+
3
+ 轻量、精美、丝滑的 Vue3 对话框组件,原生支持 Nuxt 3。
4
+
5
+ ## 特性
6
+
7
+ - **三种调用方式** — 模板组件 / 组合式(链式 API)/ 函数式
8
+ - **高级方法** — `alert` / `confirm` / `prompt` 开箱即用
9
+ - **精美动画** — zoom / slide / fade 三种过渡效果,GPU 加速
10
+ - **灵活定位** — 9 种位置(center / top / bottom / left / right / 四角)
11
+ - **丰富内容** — 文本 / HTML / iframe / Vue 组件 / 插槽
12
+ - **actions 插槽** — 完全自定义底部按钮区域
13
+ - **事件信息** — `onClose(e)` 返回触发事件详情(按钮 index / btn 配置)
14
+ - **暗色模式** — 自动跟随系统 `prefers-color-scheme`
15
+ - **响应式** — 移动端自适应布局
16
+ - **无障碍** — ARIA 属性 + ESC 关闭
17
+ - **TypeScript** — 完整类型推导
18
+
19
+ ## 安装
20
+
21
+ ### Nuxt 3
22
+
23
+ ```bash
24
+ npm install best-dialog
25
+ ```
26
+
27
+ ```ts
28
+ // nuxt.config.ts
29
+ export default defineNuxtConfig({
30
+ modules: ['best-dialog'],
31
+ })
32
+ ```
33
+
34
+ 安装后即可直接使用 `<BestDialog>`、`useDialog`、`showDialog`,无需手动导入。
35
+
36
+ ### Vue 3 (Vite / Webpack)
37
+
38
+ ```bash
39
+ npm install best-dialog
40
+ ```
41
+
42
+ ```ts
43
+ // main.ts
44
+ import { createApp, h } from 'vue'
45
+ import { BestDialogContainer } from 'best-dialog/runtime'
46
+ import 'best-dialog/src/runtime/style.css'
47
+
48
+ const app = createApp(App)
49
+
50
+ // 挂载全局对话框容器(供 useDialog / showDialog 使用)
51
+ const el = document.createElement('div')
52
+ document.body.appendChild(el)
53
+ createApp({ render: () => h(BestDialogContainer) }).mount(el)
54
+
55
+ app.mount('#app')
56
+ ```
57
+
58
+ ---
59
+
60
+ ## 使用方式
61
+
62
+ ### 1. 模板组件 `<BestDialog>`
63
+
64
+ ```vue
65
+ <template>
66
+ <button @click="show = true">打开</button>
67
+
68
+ <!-- 基础 -->
69
+ <BestDialog v-model="show" title="提示" content="内容"
70
+ :actions="['取消', { label: '确定', primary: true }]"
71
+ @close="onClose" />
72
+
73
+ <!-- 插槽 -->
74
+ <BestDialog v-model="show2">
75
+ <template #title><span>自定义标题</span></template>
76
+ <div>自定义内容</div>
77
+ <template #actions="{ close }">
78
+ <button class="bd-dialog__btn" @click="close">关闭</button>
79
+ </template>
80
+ </BestDialog>
81
+ </template>
82
+ ```
83
+
84
+ ### 2. 组合式 `useDialog` — 链式 API
85
+
86
+ ```ts
87
+ const dialog = useDialog({
88
+ title: '确认',
89
+ content: '确定删除?',
90
+ actions: ['取消', { label: '确定', primary: true }],
91
+ })
92
+
93
+ // ── 基础 ──
94
+ const close = dialog.open() // 返回 close 方法
95
+ const close2 = dialog.open({ content: '新内容' }) // 合并新选项
96
+
97
+ // ── 链式 onClose ──
98
+ dialog.open()
99
+ .onClose((e) => {
100
+ console.log('关闭来源:', e.source) // 'button' | 'overlay' | 'close-btn' | 'esc'
101
+ console.log('按钮索引:', e.currentTarget.dataset.index)
102
+ console.log('按钮配置:', e.currentTarget.dataset.btn)
103
+ })
104
+
105
+ // ── alert ──
106
+ dialog.alert('操作成功!')
107
+
108
+ // ── confirm ──
109
+ dialog.confirm('确定删除吗?')
110
+ .onOk(() => { /* 用户点了确定 */ })
111
+ .onClose((e) => { /* 任何方式关闭 */ })
112
+
113
+ // ── prompt ──
114
+ dialog.prompt('请输入您的姓名')
115
+ .onOk((value) => { console.log('输入:', value) })
116
+ ```
117
+
118
+ ### 3. 函数式 `showDialog`
119
+
120
+ ```ts
121
+ const close = showDialog({
122
+ title: '提示',
123
+ content: '操作成功',
124
+ actions: ['知道了'],
125
+ })
126
+
127
+ setTimeout(close, 3000)
128
+ ```
129
+
130
+ ---
131
+
132
+ ## API 参考
133
+
134
+ ### Props
135
+
136
+ | Prop | 类型 | 默认值 | 说明 |
137
+ |------|------|--------|------|
138
+ | `modelValue` | `boolean` | `false` | v-model 控制显隐 |
139
+ | `title` | `string \| Component` | — | 标题 |
140
+ | `content` | `string \| Component` | — | 内容 |
141
+ | `html` | `boolean` | `false` | HTML 渲染 content |
142
+ | `url` | `string` | — | iframe 地址(优先于 content)|
143
+ | `actions` | `DialogActionItem[]` | — | 底部按钮 |
144
+ | `position` | `DialogPosition` | `'center'` | 位置 |
145
+ | `effect` | `DialogEffect` | 自动 | 动画:fade / slide / zoom |
146
+ | `overlay` | `boolean` | `true` | 显示遮罩 |
147
+ | `overlayClose` | `boolean` | `true` | 点击遮罩关闭 |
148
+ | `escClose` | `boolean` | `true` | ESC 关闭 |
149
+ | `closable` | `boolean` | `true` | 显示关闭按钮 |
150
+ | `width` | `string \| number` | `'440px'` | 宽度 |
151
+ | `fullscreen` | `boolean` | `false` | 全屏 |
152
+ | `zIndex` | `number` | — | 自定义 z-index |
153
+ | `dialogClass` | `string \| object` | — | 对话框 class |
154
+ | `dialogStyle` | `CSSProperties` | — | 对话框 style |
155
+
156
+ ### 插槽
157
+
158
+ | 插槽 | 作用域 | 说明 |
159
+ |------|--------|------|
160
+ | `default` | — | 主体内容 |
161
+ | `title` | — | 标题区域 |
162
+ | `actions` | `{ close }` | 底部按钮区域 |
163
+
164
+ ### 事件
165
+
166
+ | 事件 | 参数 | 说明 |
167
+ |------|------|------|
168
+ | `update:modelValue` | `boolean` | v-model 更新 |
169
+ | `open` | — | 打开 |
170
+ | `close` | `DialogCloseEvent` | 关闭(含事件信息)|
171
+
172
+ ### DialogCloseEvent
173
+
174
+ ```ts
175
+ interface DialogCloseEvent {
176
+ source: 'button' | 'overlay' | 'close-btn' | 'esc'
177
+ currentTarget: {
178
+ dataset: {
179
+ index?: number // 按钮索引
180
+ btn?: DialogAction // 按钮配置
181
+ }
182
+ }
183
+ }
184
+ ```
185
+
186
+ ---
187
+
188
+ ## useDialog 完整 API
189
+
190
+ ```ts
191
+ const dialog = useDialog(options?)
192
+
193
+ dialog.open(opts?) // 打开,返回 close 函数
194
+ .onClose(e => {}) // 链式:关闭回调
195
+ .onOk(value => {}) // 链式:确认回调(confirm/prompt 用)
196
+
197
+ dialog.close() // 关闭
198
+
199
+ dialog.alert(content, title?) // 快捷提示
200
+ dialog.confirm(content, title?).onOk(() => {}) // 确认框
201
+ dialog.prompt(placeholder?, title?, default?) // 输入框
202
+ .onOk(value => {})
203
+ ```
204
+
205
+ ---
206
+
207
+ ## Actions 详解
208
+
209
+ ```ts
210
+ type DialogActionItem = string | {
211
+ label?: string
212
+ onClick?: (close: () => void) => void | Promise<void> | boolean
213
+ as?: 'button' | 'a'
214
+ href?: string
215
+ target?: string
216
+ class?: string
217
+ primary?: boolean // 默认最后一个为 primary
218
+ }
219
+ ```
220
+
221
+ **onClick 返回值:**
222
+ - `undefined` / `true` → 自动关闭
223
+ - `false` → 不关闭
224
+ - `Promise<false>` → 异步后不关闭
225
+
226
+ ```ts
227
+ showDialog({
228
+ actions: [
229
+ '取消',
230
+ {
231
+ label: '提交',
232
+ primary: true,
233
+ onClick: async (close) => {
234
+ const ok = await submitForm()
235
+ if (!ok) return false // 验证失败,不关闭
236
+ },
237
+ },
238
+ ],
239
+ })
240
+ ```
241
+
242
+ ---
243
+
244
+ ## 位置
245
+
246
+ ```
247
+ ┌─────────────────────────────────────┐
248
+ │ top-left │ top │ top-right │
249
+ │─────────────┼──────────┼────────────│
250
+ │ left │ center │ right │
251
+ │─────────────┼──────────┼────────────│
252
+ │ bottom-left │ bottom │bottom-right│
253
+ └─────────────────────────────────────┘
254
+ ```
255
+
256
+ 动画自动匹配:center→zoom,其他→slide(方向自适应)。可通过 `effect` 覆盖。
257
+
258
+ ---
259
+
260
+ ## 样式定制
261
+
262
+ ```css
263
+ /* 覆盖圆角 */
264
+ .bd-dialog { border-radius: 20px; }
265
+
266
+ /* 覆盖主按钮色 */
267
+ .bd-dialog__btn--primary {
268
+ background: #7c3aed;
269
+ border-color: #7c3aed;
270
+ }
271
+ ```
272
+
273
+ ```vue
274
+ <BestDialog
275
+ :dialog-class="['my-dialog']"
276
+ :dialog-style="{ borderRadius: '20px' }"
277
+ />
278
+ ```
279
+
280
+ ## 许可证
281
+
282
+ MIT
@@ -0,0 +1,3 @@
1
+ declare const _default: NuxtModule<TOptions, TOptions, false>;
2
+
3
+ export { _default as default };
@@ -0,0 +1,3 @@
1
+ declare const _default: NuxtModule<TOptions, TOptions, false>;
2
+
3
+ export { _default as default };
@@ -0,0 +1,31 @@
1
+ import { defineNuxtModule, createResolver, addComponent, addImports, addPlugin } from '@nuxt/kit';
2
+
3
+ const module$1 = defineNuxtModule({
4
+ meta: {
5
+ name: "best-dialog",
6
+ configKey: "bestDialog",
7
+ compatibility: {
8
+ nuxt: ">=3.0.0"
9
+ }
10
+ },
11
+ defaults: {},
12
+ setup(_options, nuxt) {
13
+ const { resolve } = createResolver(import.meta.url);
14
+ addComponent({
15
+ name: "BestDialog",
16
+ filePath: resolve("./runtime/index"),
17
+ export: "BestDialog"
18
+ });
19
+ addImports([
20
+ { name: "useDialog", from: resolve("./runtime/index") },
21
+ { name: "showDialog", from: resolve("./runtime/index") }
22
+ ]);
23
+ addPlugin({
24
+ src: resolve("./runtime/plugin"),
25
+ mode: "client"
26
+ });
27
+ nuxt.options.css.push(resolve("./runtime/style.css"));
28
+ }
29
+ });
30
+
31
+ export { module$1 as default };
@@ -0,0 +1,223 @@
1
+ import * as vue from 'vue';
2
+ import { PropType, CSSProperties, VNode } from 'vue';
3
+
4
+ type DialogPosition = 'center' | 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
5
+ type DialogEffect = 'fade' | 'slide' | 'zoom';
6
+ type DialogContentType = string | object | null | undefined;
7
+ interface DialogAction {
8
+ label?: string;
9
+ onClick?: (close: () => void) => void | Promise<void> | boolean;
10
+ as?: 'button' | 'a';
11
+ href?: string;
12
+ target?: string;
13
+ class?: string;
14
+ primary?: boolean;
15
+ [key: string]: any;
16
+ }
17
+ type DialogActionItem = string | DialogAction;
18
+ interface DialogCloseEvent {
19
+ source: 'button' | 'overlay' | 'close-btn' | 'esc';
20
+ index: number;
21
+ button?: DialogAction;
22
+ }
23
+ interface DialogOptions {
24
+ title?: string | object;
25
+ content?: DialogContentType;
26
+ html?: boolean;
27
+ url?: string;
28
+ actions?: DialogActionItem[];
29
+ position?: DialogPosition;
30
+ effect?: DialogEffect;
31
+ overlay?: boolean;
32
+ overlayClose?: boolean;
33
+ escClose?: boolean;
34
+ closable?: boolean;
35
+ class?: string | string[] | Record<string, boolean>;
36
+ style?: CSSProperties | string;
37
+ width?: string | number;
38
+ fullscreen?: boolean;
39
+ zIndex?: number;
40
+ onOpen?: () => void;
41
+ onClose?: (e: DialogCloseEvent) => void;
42
+ [key: string]: any;
43
+ }
44
+ interface DialogHandle {
45
+ close: () => void;
46
+ onClose: (cb: (e: DialogCloseEvent) => void) => DialogHandle;
47
+ onOk: (cb: (value?: any) => void) => DialogHandle;
48
+ open: (opts?: DialogOptions) => DialogHandle;
49
+ alert: (content: string, title?: string) => DialogHandle;
50
+ confirm: (content: string, title?: string) => DialogHandle;
51
+ prompt: (placeholder?: string, title?: string, defaultValue?: string) => DialogHandle;
52
+ }
53
+ declare function useDialog(defaults?: DialogOptions): DialogHandle;
54
+ declare function showDialog(options: DialogOptions): (e?: DialogCloseEvent) => void;
55
+ declare const BestDialog: vue.DefineComponent<vue.ExtractPropTypes<{
56
+ modelValue: {
57
+ type: BooleanConstructor;
58
+ default: boolean;
59
+ };
60
+ title: {
61
+ type: PropType<string | object>;
62
+ default: undefined;
63
+ };
64
+ content: {
65
+ type: PropType<DialogContentType>;
66
+ default: undefined;
67
+ };
68
+ html: {
69
+ type: BooleanConstructor;
70
+ default: boolean;
71
+ };
72
+ url: {
73
+ type: StringConstructor;
74
+ default: undefined;
75
+ };
76
+ actions: {
77
+ type: PropType<DialogActionItem[]>;
78
+ default: undefined;
79
+ };
80
+ position: {
81
+ type: PropType<DialogPosition>;
82
+ default: string;
83
+ };
84
+ effect: {
85
+ type: PropType<DialogEffect>;
86
+ default: undefined;
87
+ };
88
+ overlay: {
89
+ type: BooleanConstructor;
90
+ default: boolean;
91
+ };
92
+ overlayClose: {
93
+ type: BooleanConstructor;
94
+ default: boolean;
95
+ };
96
+ escClose: {
97
+ type: BooleanConstructor;
98
+ default: boolean;
99
+ };
100
+ closable: {
101
+ type: BooleanConstructor;
102
+ default: boolean;
103
+ };
104
+ width: {
105
+ type: (StringConstructor | NumberConstructor)[];
106
+ default: string;
107
+ };
108
+ fullscreen: {
109
+ type: BooleanConstructor;
110
+ default: boolean;
111
+ };
112
+ zIndex: {
113
+ type: NumberConstructor;
114
+ default: undefined;
115
+ };
116
+ dialogClass: {
117
+ type: PropType<any>;
118
+ default: undefined;
119
+ };
120
+ dialogStyle: {
121
+ type: PropType<CSSProperties | string>;
122
+ default: undefined;
123
+ };
124
+ }>, () => VNode<vue.RendererNode, vue.RendererElement, {
125
+ [key: string]: any;
126
+ }> | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, ("update:modelValue" | "open" | "close")[], "update:modelValue" | "open" | "close", vue.PublicProps, Readonly<vue.ExtractPropTypes<{
127
+ modelValue: {
128
+ type: BooleanConstructor;
129
+ default: boolean;
130
+ };
131
+ title: {
132
+ type: PropType<string | object>;
133
+ default: undefined;
134
+ };
135
+ content: {
136
+ type: PropType<DialogContentType>;
137
+ default: undefined;
138
+ };
139
+ html: {
140
+ type: BooleanConstructor;
141
+ default: boolean;
142
+ };
143
+ url: {
144
+ type: StringConstructor;
145
+ default: undefined;
146
+ };
147
+ actions: {
148
+ type: PropType<DialogActionItem[]>;
149
+ default: undefined;
150
+ };
151
+ position: {
152
+ type: PropType<DialogPosition>;
153
+ default: string;
154
+ };
155
+ effect: {
156
+ type: PropType<DialogEffect>;
157
+ default: undefined;
158
+ };
159
+ overlay: {
160
+ type: BooleanConstructor;
161
+ default: boolean;
162
+ };
163
+ overlayClose: {
164
+ type: BooleanConstructor;
165
+ default: boolean;
166
+ };
167
+ escClose: {
168
+ type: BooleanConstructor;
169
+ default: boolean;
170
+ };
171
+ closable: {
172
+ type: BooleanConstructor;
173
+ default: boolean;
174
+ };
175
+ width: {
176
+ type: (StringConstructor | NumberConstructor)[];
177
+ default: string;
178
+ };
179
+ fullscreen: {
180
+ type: BooleanConstructor;
181
+ default: boolean;
182
+ };
183
+ zIndex: {
184
+ type: NumberConstructor;
185
+ default: undefined;
186
+ };
187
+ dialogClass: {
188
+ type: PropType<any>;
189
+ default: undefined;
190
+ };
191
+ dialogStyle: {
192
+ type: PropType<CSSProperties | string>;
193
+ default: undefined;
194
+ };
195
+ }>> & Readonly<{
196
+ "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
197
+ onOpen?: ((...args: any[]) => any) | undefined;
198
+ onClose?: ((...args: any[]) => any) | undefined;
199
+ }>, {
200
+ modelValue: boolean;
201
+ title: string | object;
202
+ content: DialogContentType;
203
+ html: boolean;
204
+ url: string;
205
+ actions: DialogActionItem[];
206
+ position: DialogPosition;
207
+ effect: DialogEffect;
208
+ overlay: boolean;
209
+ overlayClose: boolean;
210
+ escClose: boolean;
211
+ closable: boolean;
212
+ width: string | number;
213
+ fullscreen: boolean;
214
+ zIndex: number;
215
+ dialogClass: any;
216
+ dialogStyle: string | CSSProperties;
217
+ }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
218
+ declare const BestDialogContainer: vue.DefineComponent<{}, () => VNode<vue.RendererNode, vue.RendererElement, {
219
+ [key: string]: any;
220
+ }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
221
+
222
+ export { BestDialog, BestDialogContainer, showDialog, useDialog };
223
+ export type { DialogAction, DialogActionItem, DialogCloseEvent, DialogContentType, DialogEffect, DialogHandle, DialogOptions, DialogPosition };