Modal (Kotak Dialog Konfirmasi)
Modal (.mjl-modal) menghentikan sementara interaksi halaman di belakangnya untuk meminta keputusan warga — seperti konfirmasi penghapusan draf permohonan atau pembatalan antrean.
1. Pratinjau Tampilan Modal
Seluruh isian data formulir KTP Elektronik dan foto dokumen yang sudah Anda unggah akan dihapus secara permanen dari server. Tindakan ini tidak dapat dibatalkan.
Penggunaan di Berbagai Teknologi
Salin contoh sesuai teknologi proyek Anda. Semua versi memakai class dari @majalengka/css. Komponen siap pakai tersedia di @majalengka/vue; untuk React, Angular, Laravel, dan PHP, contoh di bawah sudah berupa pembungkus kecil yang bisa langsung disalin ke proyek.
<button type="button" class="mjl-btn mjl-btn--danger mjl-btn--md" onclick="document.getElementById('konfirmasi').showModal()">
Batalkan Pengajuan
</button>
<dialog id="konfirmasi" class="mjl-modal" aria-labelledby="konfirmasi-judul">
<div class="mjl-modal__header">
<h2 id="konfirmasi-judul" class="mjl-modal__title">Batalkan pengajuan?</h2>
<button type="button" class="mjl-modal__close" aria-label="Tutup" onclick="this.closest('dialog').close()">✕</button>
</div>
<div class="mjl-modal__body">Data yang sudah diisi akan terhapus.</div>
<form method="dialog" class="mjl-modal__footer">
<button value="batal" class="mjl-btn mjl-btn--outline mjl-btn--md">Kembali</button>
<button value="ya" class="mjl-btn mjl-btn--danger mjl-btn--md">Ya, batalkan</button>
</form>
</dialog>
<script setup lang="ts">
import { ref } from 'vue';
import { MjlButton, MjlConfirmDialog } from '@majalengka/vue';
const buka = ref(false);
function batalkan() {
// hapus draf pengajuan
}
</script>
<template>
<MjlButton variant="danger" @click="buka = true">Batalkan Pengajuan</MjlButton>
<MjlConfirmDialog v-model="buka" title="Batalkan pengajuan?" confirm-label="Ya, batalkan" danger @confirm="batalkan">
Data yang sudah diisi akan terhapus.
</MjlConfirmDialog>
</template>
import { useEffect, useId, useRef, type ReactNode } from 'react';
export function MjlModal({ open, title, onClose, children, footer }: { open: boolean; title: string; onClose: () => void; children: ReactNode; footer?: ReactNode }) {
const ref = useRef<HTMLDialogElement>(null);
const titleId = useId();
useEffect(() => {
const d = ref.current;
if (open && !d?.open) d?.showModal();
if (!open && d?.open) d.close();
}, [open]);
return (
<dialog ref={ref} className="mjl-modal" aria-labelledby={titleId} onClose={onClose}>
<div className="mjl-modal__header">
<h2 id={titleId} className="mjl-modal__title">{title}</h2>
<button type="button" className="mjl-modal__close" aria-label="Tutup" onClick={onClose}>✕</button>
</div>
<div className="mjl-modal__body">{children}</div>
{footer && <div className="mjl-modal__footer">{footer}</div>}
</dialog>
);
}
import { Component, ElementRef, effect, input, model, viewChild } from '@angular/core';
let nextId = 0;
@Component({
selector: 'mjl-modal',
standalone: true,
template: `
<dialog #dialog class="mjl-modal" [attr.aria-labelledby]="titleId" (close)="open.set(false)">
<div class="mjl-modal__header">
<h2 [id]="titleId" class="mjl-modal__title">{{ title() }}</h2>
<button type="button" class="mjl-modal__close" aria-label="Tutup" (click)="open.set(false)">✕</button>
</div>
<div class="mjl-modal__body"><ng-content /></div>
<div class="mjl-modal__footer"><ng-content select="[footer]" /></div>
</dialog>
`,
})
export class MjlModal {
open = model(false);
title = input.required<string>();
titleId = `mjl-modal-title-${nextId++}`;
dialog = viewChild.required<ElementRef<HTMLDialogElement>>('dialog');
constructor() {
effect(() => {
const d = this.dialog().nativeElement;
if (this.open() && !d.open) d.showModal();
if (!this.open() && d.open) d.close();
});
}
}
// Pemakaian: <mjl-modal [(open)]="konfirmasi" title="Batalkan pengajuan?">...</mjl-modal>
<form id="form-batal" method="POST" action="{{ route('permohonan.destroy', $permohonan) }}">
@csrf @method('DELETE')
</form>
<button type="button" class="mjl-btn mjl-btn--danger mjl-btn--md" onclick="document.getElementById('konfirmasi').showModal()">
Batalkan Pengajuan
</button>
<dialog id="konfirmasi" class="mjl-modal" aria-labelledby="konfirmasi-judul">
<div class="mjl-modal__header">
<h2 id="konfirmasi-judul" class="mjl-modal__title">Batalkan pengajuan {{ $permohonan->nomor }}?</h2>
</div>
<div class="mjl-modal__body">Data yang sudah diisi akan terhapus.</div>
<div class="mjl-modal__footer">
<button type="button" class="mjl-btn mjl-btn--outline mjl-btn--md" onclick="this.closest('dialog').close()">Kembali</button>
<button type="submit" form="form-batal" class="mjl-btn mjl-btn--danger mjl-btn--md">Ya, batalkan</button>
</div>
</dialog>
<form id="form-batal" method="post" action="batal.php">
<input type="hidden" name="id" value="<?= (int) $permohonan['id'] ?>">
</form>
<button type="button" class="mjl-btn mjl-btn--danger mjl-btn--md" onclick="document.getElementById('konfirmasi').showModal()">
Batalkan Pengajuan
</button>
<dialog id="konfirmasi" class="mjl-modal" aria-labelledby="konfirmasi-judul">
<div class="mjl-modal__header">
<h2 id="konfirmasi-judul" class="mjl-modal__title">Batalkan pengajuan?</h2>
</div>
<div class="mjl-modal__body">Data yang sudah diisi akan terhapus.</div>
<div class="mjl-modal__footer">
<button type="button" class="mjl-btn mjl-btn--outline mjl-btn--md" onclick="this.closest('dialog').close()">Kembali</button>
<button type="submit" form="form-batal" class="mjl-btn mjl-btn--danger mjl-btn--md">Ya, batalkan</button>
</div>
</dialog>
Semua versi memakai elemen <dialog> + showModal(): fokus otomatis terkunci di dalam modal, tombol Esc menutupnya, dan konten di belakangnya tidak bisa diakses.