component Modal { props { @event open = function @event close = function @event cancel = function @event confirm = function isOpen = false defaultOpen = false title = "Modal" description = "" icon = "" label = "Modal dialog" size = "md" placement = "center" color = "primary" variant = "default" showClose = true closeLabel = "Close modal" closeOnBackdrop = true closeOnEscape = true closeOnCancel = true closeOnConfirm = false showFooter = true cancelLabel = "Cancel" cancelIcon = "" confirmLabel = "Confirm" confirmIcon = "" confirmDisabled = false confirmLoading = false destructive = false triggerLabel = "" triggerIcon = "" scrollable = true class = "" } state visible = defaultOpen functions { function isOpen() { return open || visible } function showModal(sourceEvent) { visible = true $emit("open", { sourceEvent: sourceEvent }) } function hideModal(reason, sourceEvent) { visible = false $emit("close", { reason: reason, sourceEvent: sourceEvent }) } function cancelModal(sourceEvent) { $emit("cancel", { sourceEvent: sourceEvent }) if (closeOnCancel) { hideModal("cancel", sourceEvent) } } function confirmModal(sourceEvent) { if (confirmDisabled || confirmLoading) { return } $emit("confirm", { sourceEvent: sourceEvent }) if (closeOnConfirm) { hideModal("confirm", sourceEvent) } } function handleKeydown(sourceEvent) { if (closeOnEscape && sourceEvent.key === "Escape") { sourceEvent.preventDefault() cancelModal(sourceEvent) } } } view {
{#if triggerLabel} {/if}
} style { .wire-modal { --modal-accent: var(--wire-color-primary); --modal-soft: var(--wire-color-primary-soft); --modal-contrast: var(--wire-color-primary-contrast); position: relative; display: inline-flex; } .wire-modal[data-color="secondary"] { --modal-accent: var(--wire-color-secondary); --modal-soft: var(--wire-color-secondary-soft); --modal-contrast: var(--wire-color-secondary-contrast); } .wire-modal[data-color="info"] { --modal-accent: var(--wire-color-info); --modal-soft: var(--wire-color-info-soft); --modal-contrast: var(--wire-color-info-contrast); } .wire-modal[data-color="success"] { --modal-accent: var(--wire-color-success); --modal-soft: var(--wire-color-success-soft); --modal-contrast: var(--wire-color-success-contrast); } .wire-modal[data-color="warning"] { --modal-accent: var(--wire-color-warning); --modal-soft: var(--wire-color-warning-soft); --modal-contrast: var(--wire-color-warning-text); } .wire-modal[data-color="danger"], .wire-modal[data-destructive="true"] { --modal-accent: var(--wire-color-danger); --modal-soft: var(--wire-color-danger-soft); --modal-contrast: var(--wire-color-on-danger); } .wire-modal__trigger, .wire-modal__trigger-slot { display: inline-flex; align-items: center; gap: 0.55rem; } .wire-modal__trigger { appearance: none; min-height: 2.55rem; padding: 0.65rem 1rem; color: var(--modal-contrast); background: var(--modal-accent); border: 0; border-radius: 0.8rem; font: inherit; font-size: 0.85rem; font-weight: 650; cursor: pointer; } .wire-modal__layer { position: fixed; inset: 0; z-index: 1250; display: flex; align-items: center; justify-content: center; padding: 1rem; } .wire-modal[data-placement="top"] .wire-modal__layer { align-items: flex-start; padding-top: clamp(1rem, 8vh, 5rem); } .wire-modal__backdrop { position: absolute; inset: 0; z-index: 0; appearance: none; padding: 0; background: color-mix(in srgb, black 62%, transparent); border: 0; backdrop-filter: blur(9px); } .wire-modal__panel { position: relative; z-index: 1; display: flex; flex-direction: column; width: min(32rem, calc(100vw - 2rem)); max-height: min(88vh, 52rem); color: var(--wire-color-text); background: radial-gradient( circle at 100% 0%, color-mix(in srgb, var(--modal-accent) 9%, transparent), transparent 34% ), var(--wire-color-surface-raised); border: 1px solid color-mix(in srgb, var(--modal-accent) 18%, var(--wire-color-border)); border-radius: 1.35rem; box-shadow: 0 1px 0 color-mix(in srgb, white 5%, transparent) inset, 0 40px 110px color-mix(in srgb, black 38%, transparent); overflow: hidden; } .wire-modal[data-size="sm"] .wire-modal__panel { width: min(25rem, calc(100vw - 2rem)); } .wire-modal[data-size="lg"] .wire-modal__panel { width: min(44rem, calc(100vw - 2rem)); } .wire-modal[data-size="xl"] .wire-modal__panel { width: min(64rem, calc(100vw - 2rem)); } .wire-modal[data-size="full"] .wire-modal__panel { width: calc(100vw - 2rem); height: calc(100vh - 2rem); max-height: none; } .wire-modal[data-variant="soft"] .wire-modal__panel { background: linear-gradient(145deg, var(--modal-soft), transparent 68%), var(--wire-color-surface-raised); } .wire-modal[data-variant="outline"] .wire-modal__panel { background: var(--wire-color-surface-raised); box-shadow: 0 24px 72px color-mix(in srgb, black 24%, transparent); } .wire-modal[data-variant="solid"] .wire-modal__panel { color: var(--modal-contrast); background: var(--modal-accent); border-color: color-mix(in srgb, white 20%, transparent); } .wire-modal__header { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; padding: 1.4rem 1.4rem 1.15rem; border-bottom: 1px solid var(--wire-color-border); } .wire-modal__heading { display: flex; align-items: flex-start; gap: 0.9rem; min-width: 0; } .wire-modal__icon { flex: 0 0 auto; width: 1.3rem; height: 1.3rem; margin-top: 0.15rem; color: var(--modal-accent); } .wire-modal[data-variant="solid"] .wire-modal__icon { color: currentColor; } .wire-modal__heading-copy { display: grid; gap: 0.3rem; min-width: 0; } .wire-modal__heading-copy h2, .wire-modal__heading-copy p { margin: 0; } .wire-modal__heading-copy h2 { font-size: 1.08rem; font-weight: 650; line-height: 1.3; } .wire-modal__heading-copy p { color: var(--wire-color-text-muted); font-size: 0.8rem; line-height: 1.55; } .wire-modal[data-variant="solid"] .wire-modal__heading-copy p { color: color-mix(in srgb, currentColor 76%, transparent); } .wire-modal__close { appearance: none; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; width: 2.35rem; height: 2.35rem; color: var(--wire-color-text-muted); background: var(--wire-color-surface-soft); border: 1px solid var(--wire-color-border); border-radius: 0.75rem; cursor: pointer; } .wire-modal__close:hover, .wire-modal__close:focus-visible { color: var(--modal-accent); border-color: color-mix(in srgb, var(--modal-accent) 34%, var(--wire-color-border)); outline: none; } .wire-modal__body { flex: 1 1 auto; min-height: 0; padding: 1.4rem; } .wire-modal[data-scrollable="true"] .wire-modal__body { overflow: auto; overscroll-behavior: contain; } .wire-modal__footer { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 1rem 1.4rem 1.4rem; border-top: 1px solid var(--wire-color-border); } .wire-modal__custom-footer:empty { display: none; } .wire-modal__actions { display: flex; align-items: center; justify-content: flex-end; gap: 0.65rem; margin-left: auto; } .wire-modal__button { appearance: none; display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem; min-height: 2.55rem; padding: 0.65rem 1rem; border-radius: 0.78rem; font: inherit; font-size: 0.83rem; font-weight: 650; cursor: pointer; transition: transform 150ms ease, opacity 150ms ease, border-color 150ms ease; } .wire-modal__button:active { transform: scale(0.985); } .wire-modal__button--secondary { color: var(--wire-color-text); background: transparent; border: 1px solid var(--wire-color-border); } .wire-modal__button--primary { color: var(--modal-contrast); background: var(--modal-accent); border: 1px solid transparent; } .wire-modal__button:disabled { opacity: 0.55; cursor: not-allowed; } .wire-modal__spinner { width: 0.95rem; height: 0.95rem; border: 2px solid color-mix(in srgb, currentColor 35%, transparent); border-top-color: currentColor; border-radius: 50%; animation: wire-modal-spin 750ms linear infinite; } @keyframes wire-modal-spin { to { transform: rotate(360deg); } } @media (max-width: 639px) { .wire-modal__layer { align-items: flex-end; padding: 0; } .wire-modal__panel, .wire-modal[data-size="sm"] .wire-modal__panel, .wire-modal[data-size="lg"] .wire-modal__panel, .wire-modal[data-size="xl"] .wire-modal__panel { width: 100%; max-height: 92vh; border-radius: 1.25rem 1.25rem 0 0; } .wire-modal__footer { align-items: stretch; flex-direction: column; } .wire-modal__actions { width: 100%; } .wire-modal__button { flex: 1 1 0; } } @media (prefers-reduced-motion: reduce) { .wire-modal__button, .wire-modal__spinner { animation: none; transition: none; } } } }