Popconfirm

Component

Interactive examples and API documentation

Basic Popconfirm
The simplest use case of Popconfirm.
Code
<%= render Hakumi::Popconfirm::Component.new(
  title: "Delete the task",
  description: "Are you sure to delete this task?",
  ok_text: "Yes",
  cancel_text: "No",
  id: "popconfirm-basic"
) do %>
  <%= render(Hakumi::Button::Component.new(type: :link)) { "Delete" } %>
<% end %>

<script>
  (() => {
    const el = document.getElementById("popconfirm-basic")
    if (!el) return

    const wire = () => {
      if (!window.HakumiComponents?.renderComponent) return false

      el.addEventListener("hakumi-popconfirm:confirm", async () => {
        const { instance: n } = await window.HakumiComponents.renderComponent("notification")
        n.success("Confirmed", { description: "The task has been deleted." })
      })

      el.addEventListener("hakumi-popconfirm:cancel", () => {
        console.log("Cancelled")
      })

      return true
    }

    if (wire()) return

    const onReady = () => {
      if (wire()) {
        document.removeEventListener("turbo:load", onReady)
        window.removeEventListener("load", onReady)
      }
    }

    document.addEventListener("turbo:load", onReady)
    window.addEventListener("load", onReady)
  })()
</script>
Placement
There are 12 placement options available.
Code
<div class="popconfirm-placement-demo">
  <%= render Hakumi::Flex::Component.new(vertical: true, align: :center, gap: :middle) do %>
    
    <%# Top Row %>
    <%= render Hakumi::Flex::Component.new(gap: :small, justify: :center) do %>
      <%= render Hakumi::Popconfirm::Component.new(title: "Title", placement: "topLeft") do %>
        <%= render(Hakumi::Button::Component.new(class: "placement-btn")) { "TL" } %>
      <% end %>
      <%= render Hakumi::Popconfirm::Component.new(title: "Title", placement: "top") do %>
        <%= render(Hakumi::Button::Component.new(class: "placement-btn")) { "Top" } %>
      <% end %>
      <%= render Hakumi::Popconfirm::Component.new(title: "Title", placement: "topRight") do %>
        <%= render(Hakumi::Button::Component.new(class: "placement-btn")) { "TR" } %>
      <% end %>
    <% end %>

    <%# Middle Container %>
    <%= render Hakumi::Flex::Component.new(justify: :center, gap: 280) do %>
       <%# Left Column %>
       <%= render Hakumi::Flex::Component.new(vertical: true, gap: :small) do %>
          <%= render Hakumi::Popconfirm::Component.new(title: "Title", placement: "leftTop") do %>
            <%= render(Hakumi::Button::Component.new(class: "placement-btn")) { "LT" } %>
          <% end %>
          <%= render Hakumi::Popconfirm::Component.new(title: "Title", placement: "left") do %>
            <%= render(Hakumi::Button::Component.new(class: "placement-btn")) { "Left" } %>
          <% end %>
          <%= render Hakumi::Popconfirm::Component.new(title: "Title", placement: "leftBottom") do %>
            <%= render(Hakumi::Button::Component.new(class: "placement-btn")) { "LB" } %>
          <% end %>
       <% end %>

       <%# Right Column %>
       <%= render Hakumi::Flex::Component.new(vertical: true, gap: :small) do %>
          <%= render Hakumi::Popconfirm::Component.new(title: "Title", placement: "rightTop") do %>
            <%= render(Hakumi::Button::Component.new(class: "placement-btn")) { "RT" } %>
          <% end %>
          <%= render Hakumi::Popconfirm::Component.new(title: "Title", placement: "right") do %>
            <%= render(Hakumi::Button::Component.new(class: "placement-btn")) { "Right" } %>
          <% end %>
          <%= render Hakumi::Popconfirm::Component.new(title: "Title", placement: "rightBottom") do %>
            <%= render(Hakumi::Button::Component.new(class: "placement-btn")) { "RB" } %>
          <% end %>
       <% end %>
    <% end %>

    <%# Bottom Row %>
    <%= render Hakumi::Flex::Component.new(gap: :small, justify: :center) do %>
      <%= render Hakumi::Popconfirm::Component.new(title: "Title", placement: "bottomLeft") do %>
        <%= render(Hakumi::Button::Component.new(class: "placement-btn")) { "BL" } %>
      <% end %>
      <%= render Hakumi::Popconfirm::Component.new(title: "Title", placement: "bottom") do %>
        <%= render(Hakumi::Button::Component.new(class: "placement-btn")) { "Bottom" } %>
      <% end %>
      <%= render Hakumi::Popconfirm::Component.new(title: "Title", placement: "bottomRight") do %>
        <%= render(Hakumi::Button::Component.new(class: "placement-btn")) { "BR" } %>
      <% end %>
    <% end %>

  <% end %>
</div>

<style>
  .popconfirm-placement-demo {
    padding: 100px 0;
    width: 100%;
    display: flex;
    justify-content: center;
  }
  .placement-btn {
    width: 80px;
    justify-content: center;
  }
</style>
Customize Icon
You can customize the icon in the popconfirm.
Code
<%= render Hakumi::Popconfirm::Component.new(
  title: "Delete the task",
  icon: render(Hakumi::Icon::Component.new(name: "question-circle", style: "color: red"))
) do %>
  <%= render(Hakumi::Button::Component.new(type: :link)) { "Delete" } %>
<% end %>
Asynchronous Close
Asynchronously close on OK button click.
Code
<%= render Hakumi::Popconfirm::Component.new(
  title: "Delete the task",
  description: "Are you sure to delete this task?",
  ok_text: "Yes",
  cancel_text: "No",
  id: "popconfirm-async"
) do %>
  <%= render(Hakumi::Button::Component.new(type: :primary)) { "Open Popconfirm with async logic" } %>
<% end %>

<script>
  (function() {
    const el = document.getElementById('popconfirm-async');
    el.addEventListener('hakumi-popconfirm:confirm', async (e) => {
      const { instance: n } = await window.HakumiComponents.renderComponent('notification');
      
      n.info('Deleting...', { duration: 2 });
      
      // We just provide the promise, the component handles the rest (loading, close, etc)
      e.detail.promise = new Promise(resolve => setTimeout(resolve, 2000))
        .then(() => {
          n.success('Success', { description: 'Task deleted.' });
        });
    });
  })();
</script>
Auto Shift
Automatically adjust placement when overflowing viewport.
Code
<%= render Hakumi::Flex::Component.new(
  vertical: true, 
  align: :center, 
  justify: :center, 
  style: "height: 100vh; width: 100vw; position: relative; overflow: hidden;"
) do %>
  
  <%# Top Edge - Forcing collision %>
  <div style="position: absolute; top: 0;">
    <%= render Hakumi::Popconfirm::Component.new(title: "I flipped to bottom because I'm on top!", placement: "top") do %>
      <%= render(Hakumi::Button::Component.new) { "Target: Top" } %>
    <% end %>
  </div>

  <%# Bottom Edge - Forcing collision %>
  <div style="position: absolute; bottom: 0;">
    <%= render Hakumi::Popconfirm::Component.new(title: "I flipped to top because I'm on bottom!", placement: "bottom") do %>
      <%= render(Hakumi::Button::Component.new) { "Target: Bottom" } %>
    <% end %>
  </div>

  <%# Left Edge - Forcing collision %>
  <div style="position: absolute; left: 0;">
    <%= render Hakumi::Popconfirm::Component.new(title: "I flipped to right!", placement: "left") do %>
      <%= render(Hakumi::Button::Component.new) { "Target: Left" } %>
    <% end %>
  </div>

  <%# Right Edge - Forcing collision %>
  <div style="position: absolute; right: 0;">
    <%= render Hakumi::Popconfirm::Component.new(title: "I flipped to left!", placement: "right") do %>
      <%= render(Hakumi::Button::Component.new) { "Target: Right" } %>
    <% end %>
  </div>

  <%# Center Section demonstrating pure 'auto' placement %>
  <%= render Hakumi::Flex::Component.new(vertical: true, align: :center, gap: :middle) do %>
    
    <%= render Hakumi::Space::Component.new(direction: :vertical, align: :center, size: :small) do %>
      <%= render Hakumi::Typography::Title::Component.new(level: 4, style: "margin-bottom: 0;") do %>
        Auto Placement & Collision
      <% end %>
      <%= render Hakumi::Typography::Text::Component.new(type: :secondary) do %>
        The center button uses <code>placement: "auto"</code>
      <% end %>
    <% end %>

    <%= render Hakumi::Popconfirm::Component.new(
      title: "I chose the side with most space!", 
      description: "In this case, probably top or bottom.",
      placement: "auto"
    ) do %>
      <%= render(Hakumi::Button::Component.new(type: :primary, size: :large)) { "Auto Placement Button" } %>
    <% end %>

    <%= render Hakumi::Typography::Text::Component.new(type: :secondary, style: "font-style: italic; margin-top: 20px;") do %>
      Click edge buttons to see collisions, or center button for auto-logic.
    <% end %>
  <% end %>

<% end %>

Popconfirm Ruby Props

Prop Type Default Description
title String - Title of the confirmation box.
description String - Description of the confirmation box.
ok_text String "OK" Text of the Confirm button.
cancel_text String "Cancel" Text of the Cancel button.
ok_type Symbol :primary Button type of the Confirm button.
placement String "top" The position of the popconfirm relative to the target.
trigger String "click" Popconfirm trigger mode (hover, click, focus).
disabled Boolean false Whether to disable popconfirm.
show_cancel Boolean true Whether to show cancel button.
icon String - Customize the icon.
auto_adjust_overflow Boolean true Whether to automatically adjust placement when overflowing.

JavaScript API (element.hakumiComponent.api)

Prop Type Default Description
open() Function - Show the popconfirm manually.
close() Function - Hide the popconfirm manually.
confirm() Function - Trigger the confirm action.
cancel() Function - Trigger the cancel action.