#对话框
对话框在需要明确确认或短暂补充信息时将焦点限制在一个任务中,并在完成后返回原流程。
npm install @a3s-lab/ui@0.4.1<button type="button" onclick="document.getElementById('demo-dialog-edit-profile').showModal()" class="btn" data-variant="outline">打开对话框</button>
<dialog id="demo-dialog-edit-profile" class="dialog" aria-labelledby="demo-dialog-edit-profile-title" aria-describedby="demo-dialog-edit-profile-description" onclick="if (event.target === this) this.close()">
<div class="sm:max-w-sm">
<header>
<h2 id="demo-dialog-edit-profile-title">编辑个人资料</h2>
<p id="demo-dialog-edit-profile-description">修改个人资料,完成后保存更改。</p>
</header>
<section>
<form class="grid gap-4">
<div class="grid gap-3">
<label class="label" for="demo-dialog-edit-profile-name">姓名</label>
<input class="input" type="text" value="Pedro Duarte" id="demo-dialog-edit-profile-name" autofocus />
</div>
<div class="grid gap-3">
<label class="label" for="demo-dialog-edit-profile-username">用户名</label>
<input class="input" type="text" value="@peduarte" id="demo-dialog-edit-profile-username" />
</div>
</form>
</section>
<footer>
<button class="btn" data-variant="outline" onclick="this.closest('dialog').close()">取消</button>
<button class="btn" onclick="this.closest('dialog').close()">保存更改</button>
</footer>
<button type="button" class="btn" data-variant="ghost" data-size="icon-sm" aria-label="Close dialog" onclick="this.closest('dialog').close()"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18" /><path d="m6 6 12 12" /></svg></button>
</div>
</dialog>import "@a3s-lab/ui/a3s.css";
import "@a3s-lab/ui/all";#用法
引入 CSS
导入 Tailwind 和一个完整的 A3S UI 样式包。
@import "tailwindcss";
@import "@a3s-lab/ui/a3s.css";或者仅导入基本CSS、对话框组件CSS和一个样式包。
@import "tailwindcss";
@import "@a3s-lab/ui/base.css";
@import "@a3s-lab/ui/components/dialog.css";
@import "@a3s-lab/ui/styles/a3s.css";使用CDN或批量程序导入?请参见安装页。
添加对话框 HTML
A3S UI uses the native <dialog> element and showModal(). This differs from shadcn/ui's portalled Base UI implementation, but preserves native modality, focus handling, and inert page content without component JavaScript.
<button type="button" onclick="document.getElementById('demo-dialog-edit-profile').showModal()" class="btn" data-variant="outline">打开对话框</button>
<dialog id="demo-dialog-edit-profile" class="dialog" aria-labelledby="demo-dialog-edit-profile-title" aria-describedby="demo-dialog-edit-profile-description" onclick="if (event.target === this) this.close()">
<div class="sm:max-w-sm">
<header>
<h2 id="demo-dialog-edit-profile-title">编辑个人资料</h2>
<p id="demo-dialog-edit-profile-description">修改个人资料,完成后保存更改。</p>
</header>
<section>
<form class="grid gap-4">
<div class="grid gap-3">
<label class="label" for="demo-dialog-edit-profile-name">姓名</label>
<input class="input" type="text" value="Pedro Duarte" id="demo-dialog-edit-profile-name" autofocus />
</div>
<div class="grid gap-3">
<label class="label" for="demo-dialog-edit-profile-username">用户名</label>
<input class="input" type="text" value="@peduarte" id="demo-dialog-edit-profile-username" />
</div>
</form>
</section>
<footer>
<button class="btn" data-variant="outline" onclick="this.closest('dialog').close()">取消</button>
<button class="btn" onclick="this.closest('dialog').close()">保存更改</button>
</footer>
<button type="button" class="btn" data-variant="ghost" data-size="icon-sm" aria-label="Close dialog" onclick="this.closest('dialog').close()"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18" /><path d="m6 6 12 12" /></svg></button>
</div>
</dialog>#HTML 结构
<button type="button" onclick="dialog.showModal()">Optional- Trigger button. A3S UI intentionally uses the native
HTMLDialogElement.showModal()method. <dialog class="dialog" id="{ DIALOG_ID }">Native modal dialog. Add
aria-labelledby="{ TITLE_ID }"andaria-describedby="{ DESCRIPTION_ID }"when title and description are present. Add width utilities to the inner<div>when a dialog needs a custom size. The macro also adds backdrop-click close handling.<div>Dialog content surface.
<header>OptionalDialog header.
<h2 id="{ TITLE_ID }">- Dialog title. Reference it from
aria-labelledby. <p id="{ DESCRIPTION_ID }">Optional- Dialog description. Reference it from
aria-describedby.
<section>Optional- Dialog body/content area. Add overflow utilities when the body should scroll.
<footer>Optional- Action area. It stacks actions on small screens and aligns them to the end on larger screens.
<button type="button" onclick="this.closest('dialog').close()">Optional- Close button. You can also wrap a button in
<form method="dialog">.
#JavaScript API
Dialog 保留原生 HTMLDialogElement 契约。React 的 useDialog 与 Vue 的同名 composable 可调用 showModal() 和 close(returnValue);cancel 与 close 仍是原生 DOM 事件。
#示例
#自定义关闭按钮
<button type="button" onclick="document.getElementById('dialog-custom-close').showModal()" class="btn" data-variant="outline">Share</button>
<dialog id="dialog-custom-close" class="dialog" aria-labelledby="dialog-custom-close-title" aria-describedby="dialog-custom-close-description" onclick="if (event.target === this) this.close()">
<div class="sm:max-w-md">
<header>
<h2 id="dialog-custom-close-title">Share link</h2>
<p id="dialog-custom-close-description">Anyone who has this link will be able to view this.</p>
</header>
<section class="flex items-center gap-2">
<div class="grid flex-1 gap-2">
<label class="label sr-only" for="dialog-custom-close-link">Link</label>
<input class="input" id="dialog-custom-close-link" value="https://a3s-lab.github.io/UI/installation.html" readonly />
</div>
</section>
<footer class="sm:justify-start">
<button class="btn" onclick="this.closest('dialog').close()">Close</button>
</footer>
</div>
</dialog>#无关闭按钮
<button type="button" onclick="document.getElementById('dialog-no-close').showModal()" class="btn" data-variant="outline">No Close Button</button>
<dialog id="dialog-no-close" class="dialog" aria-labelledby="dialog-no-close-title" aria-describedby="dialog-no-close-description" onclick="if (event.target === this) this.close()">
<div>
<header>
<h2 id="dialog-no-close-title">No Close Button</h2>
<p id="dialog-no-close-description">This dialog doesn't have a close button in the top-right corner.</p>
</header>
</div>
</dialog>#粘页脚
<button type="button" onclick="document.getElementById('dialog-sticky-footer').showModal()" class="btn" data-variant="outline">Sticky Footer</button>
<dialog id="dialog-sticky-footer" class="dialog" aria-labelledby="dialog-sticky-footer-title" aria-describedby="dialog-sticky-footer-description" onclick="if (event.target === this) this.close()">
<div>
<header>
<h2 id="dialog-sticky-footer-title">Sticky Footer</h2>
<p id="dialog-sticky-footer-description">This dialog has a sticky footer that stays visible while the content scrolls.</p>
</header>
<section class="-mx-4 max-h-[50vh] overflow-y-auto px-4 scrollbar">
<p class="mb-4 leading-normal">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
<p class="mb-4 leading-normal">Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p class="mb-4 leading-normal">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p class="mb-4 leading-normal">Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
<p class="mb-4 leading-normal">Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
<p class="mb-4 leading-normal">Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p class="mb-4 leading-normal">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p class="mb-4 leading-normal">Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</section>
<footer>
<button class="btn" data-variant="outline" onclick="this.closest('dialog').close()">Close</button>
</footer>
<button type="button" class="btn" data-variant="ghost" data-size="icon-sm" aria-label="Close dialog" onclick="this.closest('dialog').close()"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18" /><path d="m6 6 12 12" /></svg></button>
</div>
</dialog>#可滚动内容
<button type="button" onclick="document.getElementById('dialog-scrollable').showModal()" class="btn" data-variant="outline">Scrollable Content</button>
<dialog id="dialog-scrollable" class="dialog" aria-labelledby="dialog-scrollable-title" aria-describedby="dialog-scrollable-description" onclick="if (event.target === this) this.close()">
<div>
<header>
<h2 id="dialog-scrollable-title">Scrollable Content</h2>
<p id="dialog-scrollable-description">This is a dialog with scrollable content.</p>
</header>
<section class="-mx-4 max-h-[50vh] overflow-y-auto px-4 scrollbar">
<p class="mb-4 leading-normal">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p class="mb-4 leading-normal">Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
<p class="mb-4 leading-normal">Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
<p class="mb-4 leading-normal">Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p class="mb-4 leading-normal">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p class="mb-4 leading-normal">Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
<p class="mb-4 leading-normal">Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
<p class="mb-4 leading-normal">Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p class="mb-4 leading-normal">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p class="mb-4 leading-normal">Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</section>
<button type="button" class="btn" data-variant="ghost" data-size="icon-sm" aria-label="Close dialog" onclick="this.closest('dialog').close()"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18" /><path d="m6 6 12 12" /></svg></button>
</div>
</dialog>#RTL
对话框定位和关闭按钮放置使用逻辑属性。在对话框或父元素上设置 dir="rtl"。
<div dir="rtl">
<button type="button" onclick="document.getElementById('dialog-rtl').showModal()" class="btn" data-variant="outline">فتح الحوار</button>
<dialog id="dialog-rtl" class="dialog" aria-labelledby="dialog-rtl-title" aria-describedby="dialog-rtl-description" onclick="if (event.target === this) this.close()">
<div class="sm:max-w-sm">
<header>
<h2 id="dialog-rtl-title">تعديل الملف الشخصي</h2>
<p id="dialog-rtl-description">قم بإجراء تغييرات على ملفك الشخصي هنا. انقر فوق حفظ عند الانتهاء.</p>
</header>
<section>
<form class="grid gap-4">
<div class="grid gap-3">
<label class="label" for="dialog-rtl-name">الاسم</label>
<input class="input" id="dialog-rtl-name" name="name" value="Pedro Duarte" />
</div>
<div class="grid gap-3">
<label class="label" for="dialog-rtl-username">اسم المستخدم</label>
<input class="input" id="dialog-rtl-username" name="username" value="@peduarte" />
</div>
</form>
</section>
<footer>
<button class="btn" data-variant="outline" onclick="this.closest('dialog').close()">إلغاء</button>
<button class="btn" onclick="this.closest('dialog').close()">حفظ التغييرات</button>
</footer>
<button type="button" class="btn" data-variant="ghost" data-size="icon-sm" aria-label="Close dialog" onclick="this.closest('dialog').close()"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18" /><path d="m6 6 12 12" /></svg></button>
</div>
</dialog>
</div>