Demo trực tiếp

Mọi thành phần Forge Dialog cung cấp, chạy thật trong trang này trên đúng bản build mà phần còn lại của site đang dùng. Mỗi thẻ hiển thị đoạn mã tạo ra thứ bạn vừa thấy, nên không có gì ở đây là mô phỏng.

Hộp thoại

Bốn điểm vào thay thế hàm dựng sẵn của trình duyệt, cùng hàm tổng quát open() mà tất cả đều dựng trên đó.

alert()

Một thông điệp, một nút. Trả về khi đóng.

await alert('Đây là một hộp thoại cảnh báo.', { title: 'Lưu ý' });

confirm()

Trả về true hoặc false thay vì chặn cả trang.

const ok = await confirm('Bạn có chắc muốn tiếp tục không?', {
  title: 'Xin xác nhận',
});

prompt()

Trả về chuỗi đã nhập, hoặc null khi bị bỏ qua.

const name = await prompt('Tên bạn là gì?', {
  title: 'Giới thiệu bản thân',
  defaultValue: 'Ada Lovelace',
});

Nhập liệu có kiểm tra

validate trả về true hoặc một chuỗi lỗi, và hộp thoại vẫn mở cho tới khi hợp lệ.

await prompt('Nhập một số chẵn:', {
  title: 'Nhập liệu có kiểm tra',
  validate: (value) => {
    const n = Number(value);
    if (Number.isNaN(n)) return 'Vui lòng nhập một số';
    if (n % 2 !== 0) return 'Số phải là số chẵn';
    return true;
  },
});

open()

Tự dựng phần thân và quyết định mỗi nút trả về gì.

const instance = open({
  title: 'Hộp thoại tự dựng',
  content: (container) => {
    const p = document.createElement('p');
    p.textContent = 'Hộp thoại này được dựng bằng hàm tổng quát open().';
    container.append(p);
  },
  buttons: [
    { text: 'Đóng', role: 'primary', autoFocus: true, onClick: (i) => i.close('done') },
  ],
});
const result = await instance.whenClosed();

Xếp chồng

Mở hộp thoại thứ hai đè lên cái đầu. Tiêu điểm, Escape, và khoá cuộn đều theo cái trên cùng của chồng.

const first = confirm('Hộp thoại thứ nhất. Mở thêm một cái đè lên nhé?', { title: 'Hộp thoại 1' });
const second = await confirm('Cái này xếp chồng lên hộp thoại thứ nhất.', { title: 'Hộp thoại 2' });
const firstResult = await first;

Hiệu ứng

Đặt riêng cho từng hộp thoại. Mọi hiệu ứng đều tôn trọng prefers-reduced-motion.

Không hiệu ứng

Mở và đóng tức thì.

await alert('Hộp thoại này mở và đóng tức thì.', { animation: 'none' });

Trượt lên

Trượt lên từ dưới thay vì phóng to.

await confirm('Hộp thoại này trượt lên từ phía dưới.', { animation: 'slide' });

Ngoài hộp thoại

Cùng một lõi, trình bày khác đi. Tất cả nằm sau forgedialog/presentation.

Drawer

Bảng bên hông, xử lý tiêu điểm giống hệt hộp thoại modal.

drawer({
  title: 'Cài đặt dự án',
  message: 'Drawer bên phải, dùng dialog gốc và an toàn về tiêu điểm.',
  side: 'right',
  buttons: [{ text: 'Xong', role: 'primary', closesDialog: true }],
});

Bottom sheet

Dạng cho di động. Kéo xuống để đóng.

bottomSheet({
  title: 'Thao tác nhanh',
  message: 'Kéo xuống để đóng bottom sheet này.',
  buttons: [{ text: 'Xong', role: 'primary', closesDialog: true }],
});

Toast

Thông báo lịch sự, tự biến mất. Bốn sắc thái.

toast('Đã lưu thay đổi thành công.', { tone: 'success' });

Lightbox

Ảnh cỡ đầy đủ, kèm chú thích và cùng cách đóng như hộp thoại.

lightbox('/og-image.png', {
  alt: 'Ảnh chia sẻ mạng xã hội của Forge Dialog',
  caption: 'Kết xuất từ site/icon.svg lúc build.',
});

Trạng thái tải

Trạng thái chặn do bạn điều khiển. update() đổi thông điệp, close() kết thúc nó.

const task = loading('Đang tải lên…');
task.update('Đang xử lý…');
await task.close();

Bảng lệnh

Danh sách lệnh lọc được, điều hướng bằng bàn phím. Gõ để thu hẹp.

commandPalette([
  { id: 'theme-dark', label: 'Chuyển sang chủ đề tối', keywords: ['appearance'],
    run: () => setTheme('dark') },
  { id: 'theme-light', label: 'Chuyển sang chủ đề sáng', keywords: ['appearance'],
    run: () => setTheme('light') },
]);

Trung tâm thông báo

Mọi toast đã hiện trong trang này, lưu lại thành lịch sử. Bật vài toast trước rồi hãy mở nó.

notificationCenter();
getNotificationHistory(); // [{ id, message, tone, createdAt }]
clearNotificationHistory();

Biểu mẫu và wizard

Nằm sau forgedialog/workflows. Trong TypeScript, tên các trường được suy ra vào kiểu kết quả, nên giá trị trả về đã có kiểu mà không cần ép.

form()

Biểu mẫu có kiểm tra, dựng từ danh sách trường. Trả về các giá trị, hoặc null.

const values = await form(
  [
    { name: 'email', type: 'email', label: 'Email', required: true },
    { name: 'plan', type: 'select', label: 'Gói', options: [
      { value: 'free', label: 'Miễn phí' },
      { value: 'pro', label: 'Chuyên nghiệp' },
    ] },
    { name: 'notes', type: 'textarea', label: 'Ghi chú', rows: 3 },
  ],
  { title: 'Tạo tài khoản' },
);

wizard()

Nhiều bước, kiểm tra theo từng bước và dùng chung dữ liệu.

const flow = wizard({
  initialData: { name: '' },
  steps: [
    {
      id: 'profile',
      title: 'Hồ sơ của bạn',
      render: (el, ctx) => {
        const input = document.createElement('input');
        input.className = 'fd-input';
        input.placeholder = 'Tên';
        input.addEventListener('input', () => ctx.set({ name: input.value }));
        el.append(input);
      },
      validate: (ctx) => (ctx.data.name ? true : 'Vui lòng nhập tên của bạn.'),
    },
    { id: 'review', title: 'Xem lại',
      render: (el, ctx) => { el.textContent = `Sẵn sàng chào đón ${ctx.data.name}.`; } },
  ],
});
const data = await flow.result;

formWizard()

Cùng danh sách trường như form(), trải ra nhiều bước.

const values = await formWizard([
  { id: 'account', title: 'Tài khoản', fields: [
    { name: 'email', type: 'email', label: 'Email', required: true },
  ] },
  { id: 'profile', title: 'Hồ sơ', fields: [
    { name: 'name', type: 'text', label: 'Họ và tên', required: true },
  ] },
]);

Chủ đề

setTheme() điều khiển thư viện và trang này cùng lúc; setThemePreset() đổi toàn bộ diện mạo.

Chế độ sáng tối

Sáng, tối, hoặc theo hệ điều hành.

setTheme('dark'); // 'light' | 'dark' | 'system'

Bộ chủ đề dựng sẵn

Chọn một bộ, rồi mở bất kỳ demo nào ở trên để thấy nó được áp dụng.

setThemePreset('glass');

Giao diện và kéo thả

Màu sắc, bo góc, hướng đổ bóng, hover, và giới hạn kéo của từng hộp thoại có bảng điều khiển riêng ở trang chủ.

open({
  title: 'Hộp thoại đã tạo kiểu',
  appearance: { radius: 20, shadow: { angle: 180, distance: 20, blur: 60 } },
  draggable: { bounds: 'viewport' },
});

Mỗi demo trả về gì

Mọi demo ở trên in kết quả xuống đây, kèm các hook vòng đời plugin mà nó kích hoạt — afterOpenafterClose từ một plugin đăng ký trên trang này.