mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-08 06:55:21 +02:00
Initial tooltip component
This commit is contained in:
parent
40ae3126ac
commit
d932ac84a7
5 changed files with 147 additions and 0 deletions
9
app/components/DS/tooltip.html.erb
Normal file
9
app/components/DS/tooltip.html.erb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<div data-controller="ds-tooltip" data-ds-tooltip-placement-value="<%= placement %>" data-ds-tooltip-offset-value="<%= offset %>" data-ds-tooltip-cross-axis-value="<%= cross_axis %>">
|
||||||
|
<%= helpers.icon icon_name, size: size, color: color %>
|
||||||
|
|
||||||
|
<div role="tooltip" data-ds-tooltip-target="tooltip" class="hidden absolute z-50 bg-gray-700 text-sm px-1.5 py-1 rounded-md">
|
||||||
|
<div class="fg-inverse font-normal max-w-[200px]">
|
||||||
|
<%= tooltip_content %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
17
app/components/DS/tooltip.rb
Normal file
17
app/components/DS/tooltip.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
class DS::Tooltip < ApplicationComponent
|
||||||
|
attr_reader :placement, :offset, :cross_axis, :icon_name, :size, :color
|
||||||
|
|
||||||
|
def initialize(text: nil, placement: "top", offset: 10, cross_axis: 0, icon: "info", size: "sm", color: "default")
|
||||||
|
@text = text
|
||||||
|
@placement = placement
|
||||||
|
@offset = offset
|
||||||
|
@cross_axis = cross_axis
|
||||||
|
@icon_name = icon
|
||||||
|
@size = size
|
||||||
|
@color = color
|
||||||
|
end
|
||||||
|
|
||||||
|
def tooltip_content
|
||||||
|
content? ? content : @text
|
||||||
|
end
|
||||||
|
end
|
|
@ -16,6 +16,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<span class="font-medium"><%= balance_trend.current.format %></span>
|
||||||
|
<%= render DS::Tooltip.new(text: "The end of day balance, after all transactions and adjustments") %>
|
||||||
|
</div>
|
||||||
<%= helpers.icon "chevron-down", class: "group-open:rotate-180" %>
|
<%= helpers.icon "chevron-down", class: "group-open:rotate-180" %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
85
app/javascript/controllers/ds_tooltip_controller.js
Normal file
85
app/javascript/controllers/ds_tooltip_controller.js
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import {
|
||||||
|
autoUpdate,
|
||||||
|
computePosition,
|
||||||
|
flip,
|
||||||
|
offset,
|
||||||
|
shift,
|
||||||
|
} from "@floating-ui/dom";
|
||||||
|
import { Controller } from "@hotwired/stimulus";
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
static targets = ["tooltip"];
|
||||||
|
static values = {
|
||||||
|
placement: { type: String, default: "top" },
|
||||||
|
offset: { type: Number, default: 10 },
|
||||||
|
crossAxis: { type: Number, default: 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
this._cleanup = null;
|
||||||
|
this.boundUpdate = this.update.bind(this);
|
||||||
|
this.addEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
this.removeEventListeners();
|
||||||
|
this.stopAutoUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListeners() {
|
||||||
|
this.element.addEventListener("mouseenter", this.show);
|
||||||
|
this.element.addEventListener("mouseleave", this.hide);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeEventListeners() {
|
||||||
|
this.element.removeEventListener("mouseenter", this.show);
|
||||||
|
this.element.removeEventListener("mouseleave", this.hide);
|
||||||
|
}
|
||||||
|
|
||||||
|
show = () => {
|
||||||
|
this.tooltipTarget.style.display = "block";
|
||||||
|
this.startAutoUpdate();
|
||||||
|
this.update();
|
||||||
|
};
|
||||||
|
|
||||||
|
hide = () => {
|
||||||
|
this.tooltipTarget.style.display = "none";
|
||||||
|
this.stopAutoUpdate();
|
||||||
|
};
|
||||||
|
|
||||||
|
startAutoUpdate() {
|
||||||
|
if (!this._cleanup) {
|
||||||
|
this._cleanup = autoUpdate(
|
||||||
|
this.element.firstElementChild, // Use the icon as the reference element
|
||||||
|
this.tooltipTarget,
|
||||||
|
this.boundUpdate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stopAutoUpdate() {
|
||||||
|
if (this._cleanup) {
|
||||||
|
this._cleanup();
|
||||||
|
this._cleanup = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
computePosition(this.element.firstElementChild, this.tooltipTarget, {
|
||||||
|
placement: this.placementValue,
|
||||||
|
middleware: [
|
||||||
|
offset({
|
||||||
|
mainAxis: this.offsetValue,
|
||||||
|
crossAxis: this.crossAxisValue,
|
||||||
|
}),
|
||||||
|
flip(),
|
||||||
|
shift({ padding: 5 }),
|
||||||
|
],
|
||||||
|
}).then(({ x, y }) => {
|
||||||
|
Object.assign(this.tooltipTarget.style, {
|
||||||
|
left: `${x}px`,
|
||||||
|
top: `${y}px`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
32
test/components/previews/tooltip_component_preview.rb
Normal file
32
test/components/previews/tooltip_component_preview.rb
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
class TooltipComponentPreview < ViewComponent::Preview
|
||||||
|
# @param text text
|
||||||
|
# @param placement select [top, right, bottom, left]
|
||||||
|
# @param offset number
|
||||||
|
# @param cross_axis number
|
||||||
|
# @param icon text
|
||||||
|
# @param size select [xs, sm, md, lg, xl, 2xl]
|
||||||
|
# @param color select [default, white, success, warning, destructive, current]
|
||||||
|
def default(text: "This is helpful information", placement: "top", offset: 10, cross_axis: 0, icon: "info", size: "sm", color: "default")
|
||||||
|
render DS::Tooltip.new(
|
||||||
|
text: text,
|
||||||
|
placement: placement,
|
||||||
|
offset: offset,
|
||||||
|
cross_axis: cross_axis,
|
||||||
|
icon: icon,
|
||||||
|
size: size,
|
||||||
|
color: color
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_block_content
|
||||||
|
render DS::Tooltip.new(icon: "help-circle", color: "warning") do
|
||||||
|
tag.div do
|
||||||
|
tag.p("Custom content with formatting:", class: "font-medium mb-1") +
|
||||||
|
tag.ul(class: "list-disc list-inside text-xs") do
|
||||||
|
tag.li("First item") +
|
||||||
|
tag.li("Second item")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Add a link
Reference in a new issue