Popconfirm

Component

Interactive examples and API documentation

Basic Popconfirm
The simplest use case of Popconfirm.
Code
<%= render(HakumiComponents::Popconfirm::Component.new(
  ok_text: "Yes",
  cancel_text: "No",
  id: "popconfirm-basic"
)) do |pc| %>
  <% pc.with_title { "Delete the task" } %>
  <% pc.with_description { "Are you sure to delete this task?" } %>
  <%= render(HakumiComponents::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 HakumiComponents::Flex::Component.new(vertical: true, align: :center, gap: :default) do %>
    
    <%# Top Row %>
    <%= render HakumiComponents::Flex::Component.new(gap: :small, justify: :center) do %>
      <%= render(HakumiComponents::Popconfirm::Component.new(placement: "topLeft")) do |pc| %>
        <% pc.with_title { "Title" } %>
        <%= render(HakumiComponents::Button::Component.new(class: "placement-btn")) { "TL" } %>
      <% end %>
      <%= render(HakumiComponents::Popconfirm::Component.new(placement: "top")) do |pc| %>
        <% pc.with_title { "Title" } %>
        <%= render(HakumiComponents::Button::Component.new(class: "placement-btn")) { "Top" } %>
      <% end %>
      <%= render(HakumiComponents::Popconfirm::Component.new(placement: "topRight")) do |pc| %>
        <% pc.with_title { "Title" } %>
        <%= render(HakumiComponents::Button::Component.new(class: "placement-btn")) { "TR" } %>
      <% end %>
    <% end %>

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

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

    <%# Bottom Row %>
    <%= render HakumiComponents::Flex::Component.new(gap: :small, justify: :center) do %>
      <%= render(HakumiComponents::Popconfirm::Component.new(placement: "bottomLeft")) do |pc| %>
        <% pc.with_title { "Title" } %>
        <%= render(HakumiComponents::Button::Component.new(class: "placement-btn")) { "BL" } %>
      <% end %>
      <%= render(HakumiComponents::Popconfirm::Component.new(placement: "bottom")) do |pc| %>
        <% pc.with_title { "Title" } %>
        <%= render(HakumiComponents::Button::Component.new(class: "placement-btn")) { "Bottom" } %>
      <% end %>
      <%= render(HakumiComponents::Popconfirm::Component.new(placement: "bottomRight")) do |pc| %>
        <% pc.with_title { "Title" } %>
        <%= render(HakumiComponents::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(HakumiComponents::Popconfirm::Component.new) do |pc| %>
  <% pc.with_title { "Delete the task" } %>
  <% pc.with_icon do %>
    <%= render(HakumiComponents::Icon::Component.new(name: "question-circle", style: "color: red")) %>
  <% end %>
  <%= render(HakumiComponents::Button::Component.new(type: :link)) { "Delete" } %>
<% end %>
Asynchronous Close
Asynchronously close on OK button click.
Code
<%= render(HakumiComponents::Popconfirm::Component.new(
  ok_text: "Yes",
  cancel_text: "No",
  id: "popconfirm-async"
)) do |pc| %>
  <% pc.with_title { "Delete the task" } %>
  <% pc.with_description { "Are you sure to delete this task?" } %>
  <%= render(HakumiComponents::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 HakumiComponents::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(HakumiComponents::Popconfirm::Component.new(placement: "top")) do |pc| %>
      <% pc.with_title { "I flipped to bottom because I'm on top!" } %>
      <%= render(HakumiComponents::Button::Component.new) { "Target: Top" } %>
    <% end %>
  </div>

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

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

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

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

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

    <%= render HakumiComponents::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 %>

Props

Prop Type Default Description
ok_type Symbol :primary Button type of the Confirm button.
placement Symbol :top Position relative to the target. One of: top, topLeft, topRight, bottom, bottomLeft, bottomRight, left, leftTop, leftBottom, right, rightTop, rightBottom, auto.
trigger Symbol :click Trigger mode. One of: click, hover, focus.
disabled Boolean false Disable the popconfirm.
show_cancel Boolean true Show the cancel button.
auto_adjust_overflow Boolean true Automatically adjust placement when overflowing.
**html_options Keyword args - Attributes merged into the wrapper element.

JavaScript API (element.hakumiComponent.api)

Prop Type Default Description
open() Method - Show the popconfirm manually.
close() Method - Hide the popconfirm manually.
confirm() Method - Dispatch hakumi--popconfirm:confirm and close unless the event is prevented or an async promise is pending.
cancel() Method - Dispatch hakumi--popconfirm:cancel and hide the popconfirm.
setLoading(loading) Method - Set loading state on the OK button.