From db8152d3df0d9563a9d7b31dcb55d97794b1944e Mon Sep 17 00:00:00 2001 From: Christoph Schmatzler Date: Tue, 2 Jul 2024 20:23:34 +0200 Subject: [PATCH] tooltip Signed-off-by: Christoph Schmatzler --- CHANGELOG.md | 2 + lib/turboprop/headless/clipboard.ex | 2 +- lib/turboprop/headless/combobox.ex | 134 ++++++++++++++++++++++++++++ lib/turboprop/headless/pin_input.ex | 4 +- lib/turboprop/headless/tooltip.ex | 63 +++++++++++++ 5 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 lib/turboprop/headless/combobox.ex diff --git a/CHANGELOG.md b/CHANGELOG.md index 72e95a6..1a49b76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## Unreleased - New hook: `Turboprop.Hooks.Popover`. +- New hook: `Turboprop.Hooks.Tooltip`. +- New module: `Turboprop.Headless` - small wrappers around Hooks. ## 0.4.2 - 2024-06-19 diff --git a/lib/turboprop/headless/clipboard.ex b/lib/turboprop/headless/clipboard.ex index fb02518..7279880 100644 --- a/lib/turboprop/headless/clipboard.ex +++ b/lib/turboprop/headless/clipboard.ex @@ -59,7 +59,7 @@ defmodule Turboprop.Headless.Clipboard do render_as_tag_or_component(assigns, %{"data-part" => "trigger"}) end - attr :as, :any, default: "span" + attr :as, :any, default: "label" attr :rest, :global slot :inner_block diff --git a/lib/turboprop/headless/combobox.ex b/lib/turboprop/headless/combobox.ex new file mode 100644 index 0000000..5cc403f --- /dev/null +++ b/lib/turboprop/headless/combobox.ex @@ -0,0 +1,134 @@ +defmodule Turboprop.Headless.Combobox do + @moduledoc false + use Phoenix.Component + + import Turboprop.Headless + + attr :id, :string, default: nil + attr :as, :any, default: "div" + + attr :name, :string, default: nil + attr :input_behavior, :string, values: ["autohighlight", "autocomplete", "none"], default: "none" + attr :selection_behavior, :string, values: ["clear", "replace", "preserve"], default: "clear" + attr :disabled, :boolean, default: false + attr :multiple, :boolean, default: false + attr :loop_focus, :boolean, default: false + attr :allow_custom_value, :boolean, default: false + attr :read_only, :boolean, default: false + + attr :on_open_change, :string, default: nil + attr :on_input_value_change, :string, default: nil + attr :on_highlight_change, :string, default: nil + attr :on_value_change, :string, default: nil + + attr :rest, :global + slot :inner_block + + def combobox(assigns) do + {name, assigns} = Map.pop(assigns, :name) + {input_behavior, assigns} = Map.pop(assigns, :input_behavior) + {selection_behavior, assigns} = Map.pop(assigns, :selection_behavior) + {disabled, assigns} = Map.pop(assigns, :disabled) + {multiple, assigns} = Map.pop(assigns, :multiple) + {loop_focus, assigns} = Map.pop(assigns, :loop_focus) + {allow_custom_value, assigns} = Map.pop(assigns, :allow_custom_value) + {read_only, assigns} = Map.pop(assigns, :read_only) + + {on_open_change, assigns} = Map.pop(assigns, :on_open_change) + {on_input_value_change, assigns} = Map.pop(assigns, :on_input_value_change) + {on_highlight_change, assigns} = Map.pop(assigns, :on_highlight_change) + {on_value_change, assigns} = Map.pop(assigns, :on_value_change) + + render_as_tag_or_component(assigns, %{ + "id" => assigns.id || id(), + "phx-hook" => "Combobox", + "data-name" => name, + "data-input-behavior" => input_behavior, + "data-selection-behavior" => selection_behavior, + "data-disabled" => disabled, + "data-multiple" => multiple, + "data-loop-focus" => loop_focus, + "data-allow-custom-value" => allow_custom_value, + "data-read-only" => read_only, + "data-on-open-change" => on_open_change, + "data-on-input-value-change" => on_input_value_change, + "data-on-highlight-change" => on_highlight_change, + "data-on-value-change" => on_value_change + }) + end + + attr :as, :any, default: "div" + attr :rest, :global + slot :inner_block + + def root(assigns) do + render_as_tag_or_component(assigns, %{"data-part" => "root"}) + end + + attr :as, :any, default: "div" + attr :rest, :global + slot :inner_block + + def control(assigns) do + render_as_tag_or_component(assigns, %{"data-part" => "control"}) + end + + attr :as, :any, default: "input" + attr :rest, :global + slot :inner_block + + def input(assigns) do + render_as_tag_or_component(assigns, %{"data-part" => "input"}) + end + + attr :as, :any, default: "button" + attr :rest, :global + slot :inner_block + + def trigger(assigns) do + render_as_tag_or_component(assigns, %{"data-part" => "trigger"}) + end + + attr :as, :any, default: "div" + attr :rest, :global + slot :inner_block + + def positioner(assigns) do + render_as_tag_or_component(assigns, %{"data-part" => "positioner"}) + end + + attr :as, :any, default: "ul" + attr :rest, :global + slot :inner_block + + def content(assigns) do + render_as_tag_or_component(assigns, %{"data-part" => "content"}) + end + + attr :id, :string, default: nil + attr :as, :any, default: "li" + attr :value, :string, required: true + attr :label, :string, required: true + attr :rest, :global + slot :inner_block + + def item(assigns) do + {value, assigns} = Map.pop(assigns, :value) + {label, assigns} = Map.pop(assigns, :label) + + render_as_tag_or_component(assigns, %{ + "id" => assigns.id || id(), + "data-part" => "item", + "data-value" => value, + "data-label" => label + }) + end + + attr :as, :any, default: "label" + attr :rest, :global + slot :inner_block + + def label(assigns) do + render_as_tag_or_component(assigns, %{"data-part" => "label"}) + end +end diff --git a/lib/turboprop/headless/pin_input.ex b/lib/turboprop/headless/pin_input.ex index 0af0f9f..181bdce 100644 --- a/lib/turboprop/headless/pin_input.ex +++ b/lib/turboprop/headless/pin_input.ex @@ -20,7 +20,7 @@ defmodule Turboprop.Headless.PinInput do attr :rest, :global slot :inner_block - def accordion(assigns) do + def pin_input(assigns) do {type, assigns} = Map.pop(assigns, :type) {placeholder, assigns} = Map.pop(assigns, :placeholder) {dir, assigns} = Map.pop(assigns, :dir) @@ -64,7 +64,7 @@ defmodule Turboprop.Headless.PinInput do render_as_tag_or_component(assigns, %{"data-part" => "input", "data-index" => index}) end - attr :as, :any, default: "span" + attr :as, :any, default: "label" attr :rest, :global slot :inner_block diff --git a/lib/turboprop/headless/tooltip.ex b/lib/turboprop/headless/tooltip.ex index eeeb746..4e0e17f 100644 --- a/lib/turboprop/headless/tooltip.ex +++ b/lib/turboprop/headless/tooltip.ex @@ -1,4 +1,67 @@ defmodule Turboprop.Headless.Tooltip do @moduledoc false use Phoenix.Component + + import Turboprop.Headless + + attr :id, :string, default: nil + attr :as, :any, default: "div" + + attr :open_delay, :integer, default: 500 + attr :close_delay, :integer, default: 250 + + attr :close_on_escape, :boolean, default: true + attr :close_on_scroll, :boolean, default: true + attr :close_on_pointer_down, :boolean, default: true + + attr :on_open_change, :string, default: nil + + attr :rest, :global + slot :inner_block + + def tooltip(assigns) do + {open_delay, assigns} = Map.pop(assigns, :open_delay) + {close_delay, assigns} = Map.pop(assigns, :close_delay) + + {close_on_escape, assigns} = Map.pop(assigns, :close_on_escape) + {close_on_scroll, assigns} = Map.pop(assigns, :close_on_scroll) + {close_on_pointer_down, assigns} = Map.pop(assigns, :close_on_pointer_down) + + {on_open_change, assigns} = Map.pop(assigns, :on_open_change) + + render_as_tag_or_component(assigns, %{ + "id" => assigns.id || id(), + "phx-hook" => "Tooltip", + "data-open-delay" => open_delay, + "data-close-delay" => close_delay, + "data-close-on-escape" => close_on_escape, + "data-close-on-scroll" => close_on_scroll, + "data-close-on-pointer-down" => close_on_pointer_down, + "data-on-open-change" => on_open_change + }) + end + + attr :as, :any, default: "button" + attr :rest, :global + slot :inner_block + + def trigger(assigns) do + render_as_tag_or_component(assigns, %{"data-part" => "trigger"}) + end + + attr :as, :any, default: "div" + attr :rest, :global + slot :inner_block + + def positioner(assigns) do + render_as_tag_or_component(assigns, %{"data-part" => "positioner"}) + end + + attr :as, :any, default: "div" + attr :rest, :global + slot :inner_block + + def content(assigns) do + render_as_tag_or_component(assigns, %{"data-part" => "content"}) + end end