Created
September 20, 2022 10:36
-
-
Save fractaledmind/4e9df96887da21e89887e410d247547c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DetailsPopoverComponent < ApplicationComponent | |
class InvalidSide < StandardError | |
def initialize(side) | |
super("`#{side}` must be one of `top`, `bottom`, `left`, or `right`") | |
end | |
end | |
class InvalidAlign < StandardError | |
def initialize(align) | |
super("`#{align}` must be one of `start`, `center`, or `end`") | |
end | |
end | |
class VDom < DetailsPopoverComponent | |
attr_reader :struct | |
def initialize(**attributes) | |
@struct = { type: :details, props: merge_attributes(root_attributes, attributes), children: [] } | |
end | |
def trigger(icon: true, **attributes, &block) | |
@struct[:children] << { type: :summary, props: trigger_attributes, children: [ | |
{ type: :div, props: merge_attributes({class: "h-full"}, attributes), content: -> do | |
content(&block) | |
render trigger_icon(icon) if icon | |
end } | |
] } | |
end | |
def portal(**attributes, &block) | |
@struct[:children] << { type: :div, props: merge_attributes(portal_attributes, attributes), content: -> do | |
content(&block) | |
end } | |
end | |
private | |
def root_attributes | |
{ | |
class: "relative group", | |
data: { controller: "details-dropdown" } | |
} | |
end | |
def trigger_attributes | |
{ | |
class: tokens("marker:hidden cursor-pointer h-full"), | |
aria: { | |
expanded: "false", | |
haspopup: portal_attributes[:role], | |
controls: portal_attributes[:id] | |
}, | |
data: { | |
action: "click@window->details-dropdown#hide touchend@window->details-dropdown#hide", | |
details_dropdown_target: "button", | |
}, | |
} | |
end | |
def portal_attributes | |
{ | |
id: "details-popover-dialog-#{object_id}", | |
role: "dialog", | |
tabindex: "-1", | |
data: { | |
side: @side, | |
align: @align, | |
}, | |
class: tokens( | |
"absolute z-40", | |
side_top?: "bottom-full mb-1", | |
side_bottom?: "top-full mt-1", | |
vertical_start?: "left-0", | |
vertical_center?: "left-1/2 -translate-x-1/2", | |
vertical_end?: "right-0", | |
side_left?: "right-full mr-1", | |
side_right?: "left-full ml-1", | |
horizontal_start?: "top-0", | |
horizontal_center?: "top-1/2 -translate-y-1/2", | |
horizontal_end?: "bottom-0 ", | |
), | |
} | |
end | |
end | |
def initialize(side: :bottom, align: :center, **attributes) | |
@side = side | |
raise InvalidSide.new(side) unless [:top, :bottom, :left, :right].include? side | |
@align = align | |
raise InvalidAlign.new(align) unless [:start, :center, :end].include? align | |
@attributes = attributes | |
@vdom = VDom.new(**attributes) | |
end | |
def template(&) | |
yield(@vdom) | |
trigger, portal = @vdom.struct[:children] | |
trigger[:props][:aria][:haspopup] = portal.dig(:props, :role) | |
binding.irb | |
end | |
private | |
def trigger_icon(passed_icon = nil) | |
icon_name = passed_icon == true ? "chevron-#{trigger_icon_direction}" : passed_icon | |
Bootstrap::Icon.new( | |
icon_name, | |
class: "text-gray-500 group-hover:text-gray-900" | |
) | |
end | |
def trigger_icon_direction | |
case @side | |
when :top | |
:up | |
when :bottom | |
:down | |
when :left | |
:left | |
when :right | |
:right | |
end | |
end | |
def side_top? = @side == :top | |
def side_bottom? = @side == :bottom | |
def side_left? = @side == :left | |
def side_right? = @side == :right | |
def align_start? = @align == :start | |
def align_center? = @align == :center | |
def align_end? = @align == :end | |
def side_vertical? = side_top? || side_bottom? | |
def side_horizontal? = side_left? || side_right? | |
def vertical_start? = side_vertical? && align_start? | |
def vertical_center? = side_vertical? && align_center? | |
def vertical_end? = side_vertical? && align_end? | |
def horizontal_start? = side_horizontal? && align_start? | |
def horizontal_center? = side_horizontal? && align_center? | |
def horizontal_end? = side_horizontal? && align_end? | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment