ruby_ui 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/generators/ruby_ui/dependencies.yml +32 -10
- data/lib/generators/ruby_ui/install/templates/tailwind.css.erb +1 -1
- data/lib/generators/ruby_ui/javascript_utils.rb +24 -7
- data/lib/ruby_ui/avatar/avatar.rb +3 -0
- data/lib/ruby_ui/avatar/avatar_controller.js +33 -0
- data/lib/ruby_ui/avatar/avatar_fallback.rb +3 -0
- data/lib/ruby_ui/avatar/avatar_image.rb +4 -0
- data/lib/ruby_ui/base.rb +6 -0
- data/lib/ruby_ui/calendar/calendar.rb +3 -1
- data/lib/ruby_ui/calendar/calendar_controller.js +66 -7
- data/lib/ruby_ui/calendar/calendar_days.rb +20 -0
- data/lib/ruby_ui/calendar/calendar_docs.rb +9 -0
- data/lib/ruby_ui/combobox/combobox.rb +1 -7
- data/lib/ruby_ui/combobox/combobox_checkbox.rb +7 -1
- data/lib/ruby_ui/combobox/combobox_controller.js +56 -244
- data/lib/ruby_ui/combobox/combobox_item.rb +7 -5
- data/lib/ruby_ui/combobox/combobox_list_group.rb +1 -1
- data/lib/ruby_ui/combobox/combobox_popover.rb +5 -0
- data/lib/ruby_ui/combobox/combobox_radio.rb +8 -1
- data/lib/ruby_ui/combobox/combobox_toggle_all_checkbox.rb +7 -1
- data/lib/ruby_ui/combobox/combobox_trigger.rb +19 -19
- data/lib/ruby_ui/command/command_controller.js +10 -19
- data/lib/ruby_ui/command/command_dialog.rb +4 -1
- data/lib/ruby_ui/command/command_dialog_content.rb +2 -2
- data/lib/ruby_ui/command/command_dialog_controller.js +34 -0
- data/lib/ruby_ui/command/command_dialog_trigger.rb +2 -2
- data/lib/ruby_ui/date_picker/date_picker.rb +85 -0
- data/lib/ruby_ui/date_picker/date_picker_docs.rb +23 -0
- data/lib/ruby_ui/masked_input/masked_input.rb +1 -11
- data/lib/ruby_ui/masked_input/masked_input_controller.js +0 -13
- data/lib/ruby_ui/select/select_value.rb +2 -1
- data/lib/ruby_ui/sheet/sheet.rb +9 -1
- data/lib/ruby_ui/sheet/sheet_controller.js +6 -0
- data/lib/ruby_ui/theme_toggle/theme_toggle.rb +14 -2
- data/lib/ruby_ui/theme_toggle/theme_toggle_controller.js +27 -19
- data/lib/ruby_ui/theme_toggle/theme_toggle_docs.rb +12 -42
- data/lib/ruby_ui/toast/toast.rb +18 -0
- data/lib/ruby_ui/toast/toast_action.rb +27 -0
- data/lib/ruby_ui/toast/toast_cancel.rb +27 -0
- data/lib/ruby_ui/toast/toast_close.rb +40 -0
- data/lib/ruby_ui/toast/toast_controller.js +151 -0
- data/lib/ruby_ui/toast/toast_description.rb +18 -0
- data/lib/ruby_ui/toast/toast_docs.rb +12 -0
- data/lib/ruby_ui/toast/toast_icon.rb +65 -0
- data/lib/ruby_ui/toast/toast_item.rb +72 -0
- data/lib/ruby_ui/toast/toast_region.rb +124 -0
- data/lib/ruby_ui/toast/toast_title.rb +18 -0
- data/lib/ruby_ui/toast/toaster_controller.js +306 -0
- data/lib/ruby_ui/toggle/toggle.rb +101 -0
- data/lib/ruby_ui/toggle/toggle_controller.js +33 -0
- data/lib/ruby_ui/toggle_group/toggle_group.rb +119 -0
- data/lib/ruby_ui/toggle_group/toggle_group_controller.js +126 -0
- data/lib/ruby_ui/toggle_group/toggle_group_item.rb +67 -0
- data/lib/ruby_ui/tooltip/tooltip_content.rb +12 -5
- data/lib/ruby_ui/tooltip/tooltip_controller.js +58 -22
- data/lib/ruby_ui/tooltip/tooltip_docs.rb +13 -0
- data/lib/ruby_ui/tooltip/tooltip_trigger.rb +10 -3
- data/lib/ruby_ui.rb +3 -1
- metadata +30 -14
- data/lib/ruby_ui/theme_toggle/set_dark_mode.rb +0 -16
- data/lib/ruby_ui/theme_toggle/set_light_mode.rb +0 -16
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
// Connects to data-controller="ruby-ui--toggle"
|
|
4
|
+
// Sits on a wrapper element; the visible <button> and optional hidden <input>
|
|
5
|
+
// are descendants so Stimulus can target them.
|
|
6
|
+
export default class extends Controller {
|
|
7
|
+
static targets = ["button", "input"]
|
|
8
|
+
static values = {
|
|
9
|
+
pressed: Boolean,
|
|
10
|
+
value: String,
|
|
11
|
+
unpressedValue: String
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
toggle(event) {
|
|
15
|
+
if (this.buttonTarget.disabled) return
|
|
16
|
+
this.pressedValue = !this.pressedValue
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
pressedValueChanged(current, previous) {
|
|
20
|
+
if (this.hasButtonTarget) {
|
|
21
|
+
this.buttonTarget.setAttribute("aria-pressed", current ? "true" : "false")
|
|
22
|
+
this.buttonTarget.dataset.state = current ? "on" : "off"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (this.hasInputTarget) {
|
|
26
|
+
this.inputTarget.value = current ? this.valueValue : this.unpressedValueValue
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (previous !== undefined) {
|
|
30
|
+
this.dispatch("change", { detail: { pressed: current }, bubbles: true })
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class ToggleGroup < Base
|
|
5
|
+
SPACING_GAP = {0 => nil, 1 => "gap-1", 2 => "gap-2", 3 => "gap-3", 4 => "gap-4"}.freeze
|
|
6
|
+
VALID_TYPES = [:single, :multiple].freeze
|
|
7
|
+
VALID_ORIENTATIONS = [:horizontal, :vertical].freeze
|
|
8
|
+
|
|
9
|
+
def initialize(
|
|
10
|
+
type: :single,
|
|
11
|
+
name: nil,
|
|
12
|
+
value: nil,
|
|
13
|
+
variant: :default,
|
|
14
|
+
size: :default,
|
|
15
|
+
disabled: false,
|
|
16
|
+
spacing: 0,
|
|
17
|
+
orientation: :horizontal,
|
|
18
|
+
**attrs
|
|
19
|
+
)
|
|
20
|
+
@type = type.to_sym
|
|
21
|
+
raise ArgumentError, "type must be :single or :multiple" unless VALID_TYPES.include?(@type)
|
|
22
|
+
|
|
23
|
+
@orientation = orientation.to_sym
|
|
24
|
+
raise ArgumentError, "orientation must be :horizontal or :vertical" unless VALID_ORIENTATIONS.include?(@orientation)
|
|
25
|
+
|
|
26
|
+
raise ArgumentError, "spacing must be an Integer 0..4" unless spacing.is_a?(Integer) && (0..4).cover?(spacing)
|
|
27
|
+
|
|
28
|
+
@name = name
|
|
29
|
+
@value = value
|
|
30
|
+
@variant = variant.to_sym
|
|
31
|
+
@size = size.to_sym
|
|
32
|
+
@disabled = disabled
|
|
33
|
+
@spacing = spacing
|
|
34
|
+
super(**attrs)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def view_template(&block)
|
|
38
|
+
div(**attrs) do
|
|
39
|
+
yield(self)
|
|
40
|
+
render_hidden_inputs
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def item_context
|
|
45
|
+
{
|
|
46
|
+
type: @type,
|
|
47
|
+
variant: @variant,
|
|
48
|
+
size: @size,
|
|
49
|
+
disabled: @disabled,
|
|
50
|
+
selected_values: selected_values,
|
|
51
|
+
spacing: @spacing,
|
|
52
|
+
orientation: @orientation
|
|
53
|
+
}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def ToggleGroupItem(**kwargs, &block)
|
|
57
|
+
render RubyUI::ToggleGroupItem.new(group_context: item_context, **kwargs), &block
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def selected_values
|
|
63
|
+
case @type
|
|
64
|
+
when :single then @value.nil? ? [] : [@value.to_s]
|
|
65
|
+
when :multiple then Array(@value).map(&:to_s)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def render_hidden_inputs
|
|
70
|
+
return unless @name
|
|
71
|
+
|
|
72
|
+
if @type == :single
|
|
73
|
+
input(
|
|
74
|
+
type: "hidden",
|
|
75
|
+
name: @name,
|
|
76
|
+
value: selected_values.first.to_s,
|
|
77
|
+
data: {"ruby-ui--toggle-group-target": "input"}
|
|
78
|
+
)
|
|
79
|
+
else
|
|
80
|
+
selected_values.each do |v|
|
|
81
|
+
input(
|
|
82
|
+
type: "hidden",
|
|
83
|
+
name: "#{@name}[]",
|
|
84
|
+
value: v,
|
|
85
|
+
data: {"ruby-ui--toggle-group-target": "input"}
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def default_attrs
|
|
92
|
+
{
|
|
93
|
+
role: (@type == :single) ? "radiogroup" : "group",
|
|
94
|
+
data: {
|
|
95
|
+
controller: "ruby-ui--toggle-group",
|
|
96
|
+
"ruby-ui--toggle-group-type-value": @type.to_s,
|
|
97
|
+
"ruby-ui--toggle-group-name-value": @name.to_s,
|
|
98
|
+
orientation: @orientation.to_s,
|
|
99
|
+
spacing: @spacing.to_s
|
|
100
|
+
},
|
|
101
|
+
class: container_classes
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def container_classes
|
|
106
|
+
base = if @orientation == :vertical
|
|
107
|
+
"flex w-fit flex-col items-stretch rounded-md"
|
|
108
|
+
else
|
|
109
|
+
"flex w-fit items-center rounded-md"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
[
|
|
113
|
+
base,
|
|
114
|
+
SPACING_GAP[@spacing],
|
|
115
|
+
(@spacing == 0 && @variant == :outline) ? "shadow-xs" : nil
|
|
116
|
+
].compact
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
// Connects to data-controller="ruby-ui--toggle-group"
|
|
4
|
+
export default class extends Controller {
|
|
5
|
+
static targets = ["item", "input"]
|
|
6
|
+
static values = { type: String, name: String }
|
|
7
|
+
|
|
8
|
+
connect() {
|
|
9
|
+
this.reconcile()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
select(event) {
|
|
13
|
+
const item = event.currentTarget
|
|
14
|
+
if (item.disabled) return
|
|
15
|
+
|
|
16
|
+
if (this.typeValue === "single") {
|
|
17
|
+
this.itemTargets.forEach(el => this.setPressed(el, el === item))
|
|
18
|
+
} else {
|
|
19
|
+
this.setPressed(item, !this.isPressed(item))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this.rebuildInputs()
|
|
23
|
+
this.updateRovingTabindex(item)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
navigate(event) {
|
|
27
|
+
if (this.typeValue !== "single") return
|
|
28
|
+
const items = this.enabledItems()
|
|
29
|
+
if (items.length === 0) return
|
|
30
|
+
|
|
31
|
+
const isRtl = document.documentElement.dir === "rtl"
|
|
32
|
+
const currentIndex = items.indexOf(event.currentTarget)
|
|
33
|
+
let nextIndex = currentIndex
|
|
34
|
+
|
|
35
|
+
switch (event.key) {
|
|
36
|
+
case "ArrowRight":
|
|
37
|
+
case "ArrowDown":
|
|
38
|
+
nextIndex = (currentIndex + (isRtl && event.key === "ArrowRight" ? -1 : 1) + items.length) % items.length
|
|
39
|
+
break
|
|
40
|
+
case "ArrowLeft":
|
|
41
|
+
case "ArrowUp":
|
|
42
|
+
nextIndex = (currentIndex + (isRtl && event.key === "ArrowLeft" ? 1 : -1) + items.length) % items.length
|
|
43
|
+
break
|
|
44
|
+
case "Home":
|
|
45
|
+
nextIndex = 0
|
|
46
|
+
break
|
|
47
|
+
case "End":
|
|
48
|
+
nextIndex = items.length - 1
|
|
49
|
+
break
|
|
50
|
+
case " ":
|
|
51
|
+
case "Enter":
|
|
52
|
+
event.preventDefault()
|
|
53
|
+
event.currentTarget.click()
|
|
54
|
+
return
|
|
55
|
+
default:
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
event.preventDefault()
|
|
60
|
+
const next = items[nextIndex]
|
|
61
|
+
this.updateRovingTabindex(next)
|
|
62
|
+
next.focus()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
reconcile() {
|
|
66
|
+
if (this.typeValue === "single") {
|
|
67
|
+
const pressed = this.itemTargets.find(el => this.isPressed(el))
|
|
68
|
+
const first = pressed || this.enabledItems()[0]
|
|
69
|
+
this.itemTargets.forEach(el => {
|
|
70
|
+
el.setAttribute("tabindex", el === first ? "0" : "-1")
|
|
71
|
+
})
|
|
72
|
+
} else {
|
|
73
|
+
this.itemTargets.forEach(el => el.setAttribute("tabindex", "0"))
|
|
74
|
+
}
|
|
75
|
+
this.rebuildInputs()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
isPressed(item) {
|
|
79
|
+
return item.dataset.state === "on"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
setPressed(item, pressed) {
|
|
83
|
+
item.dataset.state = pressed ? "on" : "off"
|
|
84
|
+
if (this.typeValue === "single") {
|
|
85
|
+
item.setAttribute("aria-checked", pressed ? "true" : "false")
|
|
86
|
+
} else {
|
|
87
|
+
item.setAttribute("aria-pressed", pressed ? "true" : "false")
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
updateRovingTabindex(focusedItem) {
|
|
92
|
+
if (this.typeValue !== "single") return
|
|
93
|
+
this.itemTargets.forEach(el => {
|
|
94
|
+
el.setAttribute("tabindex", el === focusedItem ? "0" : "-1")
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
enabledItems() {
|
|
99
|
+
return this.itemTargets.filter(el => !el.disabled)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
rebuildInputs() {
|
|
103
|
+
if (!this.nameValue) return
|
|
104
|
+
this.inputTargets.forEach(el => el.remove())
|
|
105
|
+
|
|
106
|
+
const pressed = this.itemTargets.filter(el => this.isPressed(el))
|
|
107
|
+
|
|
108
|
+
if (this.typeValue === "single") {
|
|
109
|
+
const val = pressed[0]?.dataset.value || ""
|
|
110
|
+
this.element.appendChild(this.buildInput(this.nameValue, val))
|
|
111
|
+
} else {
|
|
112
|
+
pressed.forEach(item => {
|
|
113
|
+
this.element.appendChild(this.buildInput(`${this.nameValue}[]`, item.dataset.value))
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
buildInput(name, value) {
|
|
119
|
+
const input = document.createElement("input")
|
|
120
|
+
input.type = "hidden"
|
|
121
|
+
input.name = name
|
|
122
|
+
input.value = value
|
|
123
|
+
input.setAttribute("data-ruby-ui--toggle-group-target", "input")
|
|
124
|
+
return input
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class ToggleGroupItem < Toggle
|
|
5
|
+
JOIN_BASE = "w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10"
|
|
6
|
+
|
|
7
|
+
def initialize(value:, group_context:, variant: nil, size: nil, **attrs)
|
|
8
|
+
@item_value = value.to_s
|
|
9
|
+
@group_context = group_context
|
|
10
|
+
|
|
11
|
+
pressed = group_context[:selected_values].include?(@item_value)
|
|
12
|
+
super(
|
|
13
|
+
pressed: pressed,
|
|
14
|
+
name: nil,
|
|
15
|
+
value: @item_value,
|
|
16
|
+
variant: variant || group_context[:variant],
|
|
17
|
+
size: size || group_context[:size],
|
|
18
|
+
disabled: group_context[:disabled],
|
|
19
|
+
**attrs
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def view_template(&block)
|
|
24
|
+
button(**attrs, &block)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def default_attrs
|
|
30
|
+
attrs = {type: "button"}
|
|
31
|
+
attrs[:disabled] = true if @disabled
|
|
32
|
+
attrs[:data] = {
|
|
33
|
+
state: @pressed ? "on" : "off",
|
|
34
|
+
value: @item_value,
|
|
35
|
+
"ruby-ui--toggle-group-target": "item",
|
|
36
|
+
action: "click->ruby-ui--toggle-group#select keydown->ruby-ui--toggle-group#navigate"
|
|
37
|
+
}
|
|
38
|
+
attrs[:class] = [Toggle.classes_for(variant: @variant, size: @size), join_classes]
|
|
39
|
+
|
|
40
|
+
if @group_context[:type] == :single
|
|
41
|
+
attrs[:role] = "radio"
|
|
42
|
+
attrs[:aria] = {checked: @pressed.to_s}
|
|
43
|
+
attrs[:tabindex] = @pressed ? "0" : "-1"
|
|
44
|
+
else
|
|
45
|
+
attrs[:aria] = {pressed: @pressed.to_s}
|
|
46
|
+
attrs[:tabindex] = "0"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
attrs
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def join_classes
|
|
53
|
+
classes = [JOIN_BASE]
|
|
54
|
+
return classes unless @group_context[:spacing] == 0
|
|
55
|
+
|
|
56
|
+
classes << "rounded-none shadow-none"
|
|
57
|
+
if @group_context[:orientation] == :vertical
|
|
58
|
+
classes << "first-of-type:rounded-t-md last-of-type:rounded-b-md"
|
|
59
|
+
classes << "border-t-0 first-of-type:border-t" if @group_context[:variant] == :outline
|
|
60
|
+
else
|
|
61
|
+
classes << "first-of-type:rounded-l-md last-of-type:rounded-r-md"
|
|
62
|
+
classes << "border-l-0 first-of-type:border-l" if @group_context[:variant] == :outline
|
|
63
|
+
end
|
|
64
|
+
classes
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -8,7 +8,9 @@ module RubyUI
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def view_template(&)
|
|
11
|
-
|
|
11
|
+
template(data: {ruby_ui__tooltip_target: "content"}) do
|
|
12
|
+
div(**attrs, &)
|
|
13
|
+
end
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
private
|
|
@@ -16,10 +18,15 @@ module RubyUI
|
|
|
16
18
|
def default_attrs
|
|
17
19
|
{
|
|
18
20
|
id: @id,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
class: [
|
|
22
|
+
"invisible pointer-events-none w-fit max-w-[calc(100vw-2rem)] text-balance break-words absolute top-0 left-0 z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md",
|
|
23
|
+
"data-[placement=bottom]:slide-in-from-top-2",
|
|
24
|
+
"data-[placement=left]:slide-in-from-right-2",
|
|
25
|
+
"data-[placement=right]:slide-in-from-left-2",
|
|
26
|
+
"data-[placement=top]:slide-in-from-bottom-2",
|
|
27
|
+
"data-[state=open]:visible data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
28
|
+
"data-[state=closed]:visible data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:fill-mode-forwards"
|
|
29
|
+
]
|
|
23
30
|
}
|
|
24
31
|
end
|
|
25
32
|
end
|
|
@@ -3,36 +3,72 @@ import { computePosition, autoUpdate, offset, shift } from "@floating-ui/dom";
|
|
|
3
3
|
|
|
4
4
|
export default class extends Controller {
|
|
5
5
|
static targets = ["trigger", "content"];
|
|
6
|
-
static values = { placement: String }
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
static values = { placement: "top" };
|
|
8
|
+
|
|
9
|
+
mount() {
|
|
10
|
+
if (this.mounted) return;
|
|
11
|
+
|
|
12
|
+
const element = this.cloneTemplate();
|
|
13
|
+
element.setAttribute("data-placement", this.placementValue);
|
|
14
|
+
document.body.appendChild(element);
|
|
15
|
+
|
|
16
|
+
this.triggerTarget.setAttribute("aria-describedby", element.id);
|
|
17
|
+
element.addEventListener("animationend", (event) => this.animationEnd(event));
|
|
18
|
+
|
|
19
|
+
const onBeforeCache = () => this.unmount();
|
|
20
|
+
document.addEventListener("turbo:before-cache", onBeforeCache);
|
|
21
|
+
|
|
22
|
+
this.mounted = { element, onBeforeCache };
|
|
23
|
+
this.mounted.stopAutoUpdate = autoUpdate(this.triggerTarget, element, () => this.reposition());
|
|
11
24
|
}
|
|
12
25
|
|
|
13
|
-
|
|
14
|
-
this.
|
|
26
|
+
unmount() {
|
|
27
|
+
if (!this.mounted) return;
|
|
15
28
|
|
|
16
|
-
|
|
17
|
-
this.triggerTarget.setAttribute("aria-describedby", tooltipId);
|
|
29
|
+
document.removeEventListener("turbo:before-cache", this.mounted.onBeforeCache);
|
|
18
30
|
|
|
31
|
+
this.mounted.stopAutoUpdate?.();
|
|
32
|
+
this.mounted.element.remove();
|
|
33
|
+
this.triggerTarget.removeAttribute("aria-describedby");
|
|
34
|
+
|
|
35
|
+
this.mounted = null;
|
|
19
36
|
}
|
|
20
37
|
|
|
21
38
|
disconnect() {
|
|
22
|
-
this.
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
this.unmount();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
show() {
|
|
43
|
+
if (!this.hasContentTarget) return;
|
|
44
|
+
|
|
45
|
+
this.mount();
|
|
46
|
+
this.mounted.element.setAttribute("data-state", "open");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
hide() {
|
|
50
|
+
this.mounted?.element.setAttribute("data-state", "closed");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
animationEnd(event) {
|
|
54
|
+
if (event.animationName !== "exit") return;
|
|
55
|
+
if (this.mounted?.element.getAttribute("data-state") !== "closed") return;
|
|
56
|
+
|
|
57
|
+
this.unmount();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
cloneTemplate() {
|
|
61
|
+
return this.contentTarget.content.firstElementChild.cloneNode(true);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
reposition() {
|
|
65
|
+
if (!this.mounted) return;
|
|
66
|
+
|
|
67
|
+
const position = { placement: this.placementValue, middleware: [offset(4), shift()] };
|
|
68
|
+
|
|
69
|
+
computePosition(this.triggerTarget, this.mounted.element, position).then(({ x, y }) => {
|
|
70
|
+
this.mounted?.element.style.setProperty("left", `${x}px`);
|
|
71
|
+
this.mounted?.element.style.setProperty("top", `${y}px`);
|
|
36
72
|
});
|
|
37
73
|
}
|
|
38
74
|
}
|
|
@@ -24,6 +24,19 @@ class Views::Docs::Tooltip < Views::Base
|
|
|
24
24
|
RUBY
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
render Docs::VisualCodeExample.new(title: "Long content", context: self) do
|
|
28
|
+
<<~RUBY
|
|
29
|
+
Tooltip do
|
|
30
|
+
TooltipTrigger do
|
|
31
|
+
Button(variant: :outline) { "Hover me" }
|
|
32
|
+
end
|
|
33
|
+
TooltipContent do
|
|
34
|
+
Text { "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
RUBY
|
|
38
|
+
end
|
|
39
|
+
|
|
27
40
|
render Components::ComponentSetup::Tabs.new(component_name: component)
|
|
28
41
|
|
|
29
42
|
render Docs::ComponentsTable.new(component_files(component))
|
|
@@ -10,9 +10,16 @@ module RubyUI
|
|
|
10
10
|
|
|
11
11
|
def default_attrs
|
|
12
12
|
{
|
|
13
|
-
data: {
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
data: {
|
|
14
|
+
ruby_ui__tooltip_target: "trigger",
|
|
15
|
+
action: [
|
|
16
|
+
"mouseenter->ruby-ui--tooltip#show",
|
|
17
|
+
"mouseleave->ruby-ui--tooltip#hide",
|
|
18
|
+
"focus->ruby-ui--tooltip#show",
|
|
19
|
+
"blur->ruby-ui--tooltip#hide"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
variant: :outline
|
|
16
23
|
}
|
|
17
24
|
end
|
|
18
25
|
end
|
data/lib/ruby_ui.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- George Kettle
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: phlex
|
|
@@ -36,28 +35,28 @@ dependencies:
|
|
|
36
35
|
requirements:
|
|
37
36
|
- - "~>"
|
|
38
37
|
- !ruby/object:Gem::Version
|
|
39
|
-
version:
|
|
38
|
+
version: 5.0.0
|
|
40
39
|
type: :development
|
|
41
40
|
prerelease: false
|
|
42
41
|
version_requirements: !ruby/object:Gem::Requirement
|
|
43
42
|
requirements:
|
|
44
43
|
- - "~>"
|
|
45
44
|
- !ruby/object:Gem::Version
|
|
46
|
-
version:
|
|
45
|
+
version: 5.0.0
|
|
47
46
|
- !ruby/object:Gem::Dependency
|
|
48
47
|
name: tailwind_merge
|
|
49
48
|
requirement: !ruby/object:Gem::Requirement
|
|
50
49
|
requirements:
|
|
51
50
|
- - "~>"
|
|
52
51
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
52
|
+
version: '1.4'
|
|
54
53
|
type: :development
|
|
55
54
|
prerelease: false
|
|
56
55
|
version_requirements: !ruby/object:Gem::Requirement
|
|
57
56
|
requirements:
|
|
58
57
|
- - "~>"
|
|
59
58
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
59
|
+
version: '1.4'
|
|
61
60
|
- !ruby/object:Gem::Dependency
|
|
62
61
|
name: rake
|
|
63
62
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -92,14 +91,14 @@ dependencies:
|
|
|
92
91
|
requirements:
|
|
93
92
|
- - "~>"
|
|
94
93
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: '
|
|
94
|
+
version: '6.0'
|
|
96
95
|
type: :development
|
|
97
96
|
prerelease: false
|
|
98
97
|
version_requirements: !ruby/object:Gem::Requirement
|
|
99
98
|
requirements:
|
|
100
99
|
- - "~>"
|
|
101
100
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '
|
|
101
|
+
version: '6.0'
|
|
103
102
|
description: Ruby UI is a UI Component Library for Ruby developers. Built on top of
|
|
104
103
|
the Phlex Framework.
|
|
105
104
|
email: george.kettle@icloud.com
|
|
@@ -145,6 +144,7 @@ files:
|
|
|
145
144
|
- lib/ruby_ui/aspect_ratio/aspect_ratio.rb
|
|
146
145
|
- lib/ruby_ui/aspect_ratio/aspect_ratio_docs.rb
|
|
147
146
|
- lib/ruby_ui/avatar/avatar.rb
|
|
147
|
+
- lib/ruby_ui/avatar/avatar_controller.js
|
|
148
148
|
- lib/ruby_ui/avatar/avatar_docs.rb
|
|
149
149
|
- lib/ruby_ui/avatar/avatar_fallback.rb
|
|
150
150
|
- lib/ruby_ui/avatar/avatar_image.rb
|
|
@@ -228,6 +228,7 @@ files:
|
|
|
228
228
|
- lib/ruby_ui/command/command_controller.js
|
|
229
229
|
- lib/ruby_ui/command/command_dialog.rb
|
|
230
230
|
- lib/ruby_ui/command/command_dialog_content.rb
|
|
231
|
+
- lib/ruby_ui/command/command_dialog_controller.js
|
|
231
232
|
- lib/ruby_ui/command/command_dialog_trigger.rb
|
|
232
233
|
- lib/ruby_ui/command/command_docs.rb
|
|
233
234
|
- lib/ruby_ui/command/command_empty.rb
|
|
@@ -264,6 +265,8 @@ files:
|
|
|
264
265
|
- lib/ruby_ui/data_table/data_table_selection_summary.rb
|
|
265
266
|
- lib/ruby_ui/data_table/data_table_sort_head.rb
|
|
266
267
|
- lib/ruby_ui/data_table/data_table_toolbar.rb
|
|
268
|
+
- lib/ruby_ui/date_picker/date_picker.rb
|
|
269
|
+
- lib/ruby_ui/date_picker/date_picker_docs.rb
|
|
267
270
|
- lib/ruby_ui/dialog/dialog.rb
|
|
268
271
|
- lib/ruby_ui/dialog/dialog_content.rb
|
|
269
272
|
- lib/ruby_ui/dialog/dialog_controller.js
|
|
@@ -401,11 +404,26 @@ files:
|
|
|
401
404
|
- lib/ruby_ui/tabs/tabs_trigger.rb
|
|
402
405
|
- lib/ruby_ui/textarea/textarea.rb
|
|
403
406
|
- lib/ruby_ui/textarea/textarea_docs.rb
|
|
404
|
-
- lib/ruby_ui/theme_toggle/set_dark_mode.rb
|
|
405
|
-
- lib/ruby_ui/theme_toggle/set_light_mode.rb
|
|
406
407
|
- lib/ruby_ui/theme_toggle/theme_toggle.rb
|
|
407
408
|
- lib/ruby_ui/theme_toggle/theme_toggle_controller.js
|
|
408
409
|
- lib/ruby_ui/theme_toggle/theme_toggle_docs.rb
|
|
410
|
+
- lib/ruby_ui/toast/toast.rb
|
|
411
|
+
- lib/ruby_ui/toast/toast_action.rb
|
|
412
|
+
- lib/ruby_ui/toast/toast_cancel.rb
|
|
413
|
+
- lib/ruby_ui/toast/toast_close.rb
|
|
414
|
+
- lib/ruby_ui/toast/toast_controller.js
|
|
415
|
+
- lib/ruby_ui/toast/toast_description.rb
|
|
416
|
+
- lib/ruby_ui/toast/toast_docs.rb
|
|
417
|
+
- lib/ruby_ui/toast/toast_icon.rb
|
|
418
|
+
- lib/ruby_ui/toast/toast_item.rb
|
|
419
|
+
- lib/ruby_ui/toast/toast_region.rb
|
|
420
|
+
- lib/ruby_ui/toast/toast_title.rb
|
|
421
|
+
- lib/ruby_ui/toast/toaster_controller.js
|
|
422
|
+
- lib/ruby_ui/toggle/toggle.rb
|
|
423
|
+
- lib/ruby_ui/toggle/toggle_controller.js
|
|
424
|
+
- lib/ruby_ui/toggle_group/toggle_group.rb
|
|
425
|
+
- lib/ruby_ui/toggle_group/toggle_group_controller.js
|
|
426
|
+
- lib/ruby_ui/toggle_group/toggle_group_item.rb
|
|
409
427
|
- lib/ruby_ui/tooltip/tooltip.rb
|
|
410
428
|
- lib/ruby_ui/tooltip/tooltip_content.rb
|
|
411
429
|
- lib/ruby_ui/tooltip/tooltip_controller.js
|
|
@@ -421,7 +439,6 @@ homepage: https://rubygems.org/gems/ruby_ui
|
|
|
421
439
|
licenses:
|
|
422
440
|
- MIT
|
|
423
441
|
metadata: {}
|
|
424
|
-
post_install_message:
|
|
425
442
|
rdoc_options: []
|
|
426
443
|
require_paths:
|
|
427
444
|
- lib
|
|
@@ -436,8 +453,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
436
453
|
- !ruby/object:Gem::Version
|
|
437
454
|
version: '0'
|
|
438
455
|
requirements: []
|
|
439
|
-
rubygems_version:
|
|
440
|
-
signing_key:
|
|
456
|
+
rubygems_version: 4.0.10
|
|
441
457
|
specification_version: 4
|
|
442
458
|
summary: RubyUI is a UI Component Library for Ruby developers.
|
|
443
459
|
test_files: []
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RubyUI
|
|
4
|
-
class SetDarkMode < Base
|
|
5
|
-
def view_template(&)
|
|
6
|
-
div(**attrs, &)
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def default_attrs
|
|
10
|
-
{
|
|
11
|
-
class: "hidden dark:inline-block",
|
|
12
|
-
data: {controller: "ruby-ui--theme-toggle", action: "click->ruby-ui--theme-toggle#setLightTheme"}
|
|
13
|
-
}
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|