ruby_ui 1.4.0 → 1.6.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 +12 -2
- data/lib/generators/ruby_ui/javascript_utils.rb +0 -13
- data/lib/ruby_ui/accordion/accordion_controller.js +16 -9
- data/lib/ruby_ui/bubble/bubble.rb +37 -0
- data/lib/ruby_ui/bubble/bubble_content.rb +23 -0
- data/lib/ruby_ui/bubble/bubble_docs.rb +167 -0
- data/lib/ruby_ui/bubble/bubble_group.rb +18 -0
- data/lib/ruby_ui/bubble/bubble_reactions.rb +38 -0
- data/lib/ruby_ui/combobox/combobox.rb +3 -1
- data/lib/ruby_ui/combobox/combobox_checkbox.rb +8 -1
- data/lib/ruby_ui/combobox/combobox_controller.js +3 -2
- data/lib/ruby_ui/combobox/combobox_trigger.rb +1 -4
- data/lib/ruby_ui/context_menu/context_menu_content.rb +5 -6
- data/lib/ruby_ui/context_menu/context_menu_controller.js +87 -75
- data/lib/ruby_ui/data_table/data_table_column_toggle.rb +8 -4
- data/lib/ruby_ui/data_table/data_table_column_visibility_controller.js +12 -2
- data/lib/ruby_ui/dialog/dialog_content.rb +1 -1
- data/lib/ruby_ui/dropdown_menu/dropdown_menu.rb +0 -1
- data/lib/ruby_ui/dropdown_menu/dropdown_menu_controller.js +5 -1
- data/lib/ruby_ui/dropdown_menu/dropdown_menu_docs.rb +1 -1
- data/lib/ruby_ui/empty/empty.rb +18 -0
- data/lib/ruby_ui/empty/empty_content.rb +18 -0
- data/lib/ruby_ui/empty/empty_description.rb +18 -0
- data/lib/ruby_ui/empty/empty_docs.rb +69 -0
- data/lib/ruby_ui/empty/empty_header.rb +18 -0
- data/lib/ruby_ui/empty/empty_media.rb +31 -0
- data/lib/ruby_ui/empty/empty_title.rb +18 -0
- data/lib/ruby_ui/hover_card/hover_card_content.rb +5 -6
- data/lib/ruby_ui/hover_card/hover_card_controller.js +113 -76
- data/lib/ruby_ui/input_otp/input_otp.rb +42 -0
- data/lib/ruby_ui/input_otp/input_otp_controller.js +128 -0
- data/lib/ruby_ui/input_otp/input_otp_docs.rb +213 -0
- data/lib/ruby_ui/input_otp/input_otp_group.rb +15 -0
- data/lib/ruby_ui/input_otp/input_otp_separator.rb +39 -0
- data/lib/ruby_ui/input_otp/input_otp_slot.rb +33 -0
- data/lib/ruby_ui/message/message.rb +23 -0
- data/lib/ruby_ui/message/message_avatar.rb +18 -0
- data/lib/ruby_ui/message/message_content.rb +18 -0
- data/lib/ruby_ui/message/message_docs.rb +173 -0
- data/lib/ruby_ui/message/message_footer.rb +18 -0
- data/lib/ruby_ui/message/message_group.rb +18 -0
- data/lib/ruby_ui/message/message_header.rb +18 -0
- data/lib/ruby_ui/message_scroller/message_scroller.rb +18 -0
- data/lib/ruby_ui/message_scroller/message_scroller_button.rb +56 -0
- data/lib/ruby_ui/message_scroller/message_scroller_content.rb +23 -0
- data/lib/ruby_ui/message_scroller/message_scroller_controller.js +335 -0
- data/lib/ruby_ui/message_scroller/message_scroller_docs.rb +208 -0
- data/lib/ruby_ui/message_scroller/message_scroller_item.rb +28 -0
- data/lib/ruby_ui/message_scroller/message_scroller_provider.rb +34 -0
- data/lib/ruby_ui/message_scroller/message_scroller_viewport.rb +22 -0
- data/lib/ruby_ui/native_select/native_select_icon.rb +1 -1
- data/lib/ruby_ui/popover/popover_content.rb +2 -1
- data/lib/ruby_ui/popover/popover_controller.js +95 -32
- data/lib/ruby_ui/toast/toaster_controller.js +24 -8
- data/lib/ruby_ui.rb +1 -1
- metadata +34 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2d65c0b25a3f006ce2a97107b84779fb313f55fe0937048f033a8a4477ee5bda
|
|
4
|
+
data.tar.gz: fccde45b64530b69c9c1b3330f2203da5d4921a2b89a48dbc25ae2d27f8d36fd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ed144e39bf1a8cdf62f5550a9fdf1426cbc609979dbc7049c33c9c0e6fac737adffad32bbe5f21dd21c6de8b974ffe5c61a9ebd24011001411edac378629b416
|
|
7
|
+
data.tar.gz: 158aa2d122d94feadfb41ebd67e6e2078cf3b8ef9675fb94f23d228e0d27fcfb3b206e46a6f8bf47c3f8ede71b8c68e32c18db7ff9a8e3188f5a000138142d4e
|
|
@@ -40,7 +40,7 @@ command:
|
|
|
40
40
|
|
|
41
41
|
context_menu:
|
|
42
42
|
js_packages:
|
|
43
|
-
- "
|
|
43
|
+
- "@floating-ui/dom"
|
|
44
44
|
|
|
45
45
|
date_picker:
|
|
46
46
|
components:
|
|
@@ -64,7 +64,7 @@ dropdown_menu:
|
|
|
64
64
|
|
|
65
65
|
hover_card:
|
|
66
66
|
js_packages:
|
|
67
|
-
- "
|
|
67
|
+
- "@floating-ui/dom"
|
|
68
68
|
|
|
69
69
|
masked_input:
|
|
70
70
|
components:
|
|
@@ -73,6 +73,16 @@ masked_input:
|
|
|
73
73
|
js_packages:
|
|
74
74
|
- "maska"
|
|
75
75
|
|
|
76
|
+
message:
|
|
77
|
+
components:
|
|
78
|
+
- "Avatar"
|
|
79
|
+
- "Bubble"
|
|
80
|
+
|
|
81
|
+
message_scroller:
|
|
82
|
+
components:
|
|
83
|
+
- "Message"
|
|
84
|
+
- "Bubble"
|
|
85
|
+
|
|
76
86
|
pagination:
|
|
77
87
|
components:
|
|
78
88
|
- "Button"
|
|
@@ -25,8 +25,6 @@ module RubyUI
|
|
|
25
25
|
pin_motion
|
|
26
26
|
when "tw-animate-css"
|
|
27
27
|
pin_tw_animate_css
|
|
28
|
-
when "tippy.js"
|
|
29
|
-
pin_tippy_js
|
|
30
28
|
else
|
|
31
29
|
run "bin/importmap pin #{package}"
|
|
32
30
|
end
|
|
@@ -65,17 +63,6 @@ module RubyUI
|
|
|
65
63
|
RUBY
|
|
66
64
|
end
|
|
67
65
|
|
|
68
|
-
def pin_tippy_js
|
|
69
|
-
say <<~TEXT
|
|
70
|
-
WARNING: Installing tippy.js from CDN because `bin/importmap pin tippy.js` doesn't download the correct file.
|
|
71
|
-
TEXT
|
|
72
|
-
|
|
73
|
-
inject_into_file rails_root.join("config/importmap.rb"), <<~RUBY
|
|
74
|
-
pin "tippy.js", to: "https://cdn.jsdelivr.net/npm/tippy.js@6.3.7/+esm"
|
|
75
|
-
pin "@popperjs/core", to: "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/+esm"\n
|
|
76
|
-
RUBY
|
|
77
|
-
end
|
|
78
|
-
|
|
79
66
|
def rails_root = Rails.root
|
|
80
67
|
end
|
|
81
68
|
end
|
|
@@ -79,7 +79,11 @@ export default class extends Controller {
|
|
|
79
79
|
duration: this.animationDurationValue,
|
|
80
80
|
easing: this.animationEasingValue,
|
|
81
81
|
},
|
|
82
|
-
)
|
|
82
|
+
)
|
|
83
|
+
.finished.then(() => {
|
|
84
|
+
if (content.dataset.state === "open") content.style.height = "auto";
|
|
85
|
+
})
|
|
86
|
+
.catch(() => {});
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
// Hide the accordion content with animation
|
|
@@ -87,20 +91,23 @@ export default class extends Controller {
|
|
|
87
91
|
const content = this.contentTarget;
|
|
88
92
|
content.dataset.state = "closed";
|
|
89
93
|
|
|
94
|
+
const currentHeight = content.getBoundingClientRect().height;
|
|
90
95
|
animate(
|
|
91
96
|
content,
|
|
92
|
-
{ height:
|
|
97
|
+
{ height: [`${currentHeight}px`, "0px"] },
|
|
93
98
|
{
|
|
94
99
|
duration: this.animationDurationValue,
|
|
95
100
|
easing: this.animationEasingValue,
|
|
96
101
|
},
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
content.
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
)
|
|
103
|
+
.finished.then(() => {
|
|
104
|
+
// After animation completes, truly hide the element so it is removed
|
|
105
|
+
// from layout and form focus — prevents trapped validation errors
|
|
106
|
+
if (content.dataset.state === "closed") {
|
|
107
|
+
content.setAttribute("hidden", "");
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
.catch(() => {});
|
|
104
111
|
}
|
|
105
112
|
|
|
106
113
|
// Rotate the accordion icon 180deg using animate function
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class Bubble < Base
|
|
5
|
+
VARIANTS = {
|
|
6
|
+
default: "*:data-[slot=bubble-content]:bg-primary *:data-[slot=bubble-content]:text-primary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-primary/80",
|
|
7
|
+
secondary: "*:data-[slot=bubble-content]:bg-secondary *:data-[slot=bubble-content]:text-secondary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)]",
|
|
8
|
+
muted: "*:data-[slot=bubble-content]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--muted),var(--foreground)_5%)]",
|
|
9
|
+
tinted: "*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.93_calc(c*0.4)_h)] dark:*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.3_calc(c*0.4)_h)] *:data-[slot=bubble-content]:text-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.88_calc(c*0.5)_h)] dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.35_calc(c*0.5)_h)]",
|
|
10
|
+
outline: "*:data-[slot=bubble-content]:bg-background *:data-[slot=bubble-content]:border-border [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-input/30",
|
|
11
|
+
ghost: "*:data-[slot=bubble-content]:rounded-none *:data-[slot=bubble-content]:bg-transparent *:data-[slot=bubble-content]:p-0 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted/50 border-none",
|
|
12
|
+
destructive: "*:data-[slot=bubble-content]:bg-destructive/10 dark:*:data-[slot=bubble-content]:bg-destructive/20 *:data-[slot=bubble-content]:text-destructive [&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/20 dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/30"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def initialize(variant: :default, align: :start, **attrs)
|
|
16
|
+
@variant = variant
|
|
17
|
+
@align = align
|
|
18
|
+
super(**attrs)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def view_template(&)
|
|
22
|
+
div(**attrs, &)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def default_attrs
|
|
28
|
+
{
|
|
29
|
+
data: {slot: "bubble", variant: @variant, align: @align},
|
|
30
|
+
class: [
|
|
31
|
+
"group/bubble relative flex w-fit min-w-0 flex-col gap-1 max-w-[80%] data-[align=end]:self-end data-[variant=ghost]:max-w-full",
|
|
32
|
+
VARIANTS[@variant]
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class BubbleContent < Base
|
|
5
|
+
def initialize(as: :div, **attrs)
|
|
6
|
+
@as = as
|
|
7
|
+
super(**attrs)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def view_template(&)
|
|
11
|
+
send(@as, **attrs, &)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def default_attrs
|
|
17
|
+
{
|
|
18
|
+
data: {slot: "bubble-content"},
|
|
19
|
+
class: "w-fit max-w-full min-w-0 overflow-hidden wrap-break-word rounded-3xl border border-transparent px-3 py-2.5 text-sm leading-relaxed group-data-[align=end]/bubble:self-end [&:is(button,a)]:text-left [&:is(button,a)]:outline-none [&:is(button,a)]:transition-colors [&:is(button,a):focus-visible]:border-ring [&:is(button,a):focus-visible]:ring-3 [&:is(button,a):focus-visible]:ring-ring/30"
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Views::Docs::Bubble < Views::Base
|
|
4
|
+
def view_template
|
|
5
|
+
component = "Bubble"
|
|
6
|
+
|
|
7
|
+
div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
|
|
8
|
+
render Docs::Header.new(title: "Bubble", description: "A chat bubble surface for displaying conversational content, with variants, alignment, grouping, and reactions.")
|
|
9
|
+
|
|
10
|
+
Heading(level: 2) { "Usage" }
|
|
11
|
+
|
|
12
|
+
render Docs::VisualCodeExample.new(title: "Default", context: self) do
|
|
13
|
+
<<~RUBY
|
|
14
|
+
Bubble(align: :end) do
|
|
15
|
+
BubbleContent { "Hey there! what's up?" }
|
|
16
|
+
end
|
|
17
|
+
RUBY
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
render Docs::VisualCodeExample.new(title: "Conversation", context: self) do
|
|
21
|
+
<<~RUBY
|
|
22
|
+
div(class: "flex flex-col gap-8") do
|
|
23
|
+
Bubble(align: :end) do
|
|
24
|
+
BubbleContent { "Hey there! what's up?" }
|
|
25
|
+
end
|
|
26
|
+
BubbleGroup do
|
|
27
|
+
Bubble(variant: :muted) do
|
|
28
|
+
BubbleContent { "Hey! Want to see chat bubbles?" }
|
|
29
|
+
end
|
|
30
|
+
Bubble(variant: :muted) do
|
|
31
|
+
BubbleContent { "I can group messages, switch sides, and keep the whole thread easy to scan." }
|
|
32
|
+
BubbleReactions(role: "img", aria_label: "Reaction: thumbs up") do
|
|
33
|
+
span { "👍" }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
Bubble(align: :end) do
|
|
38
|
+
BubbleContent { "Sure. Hit me with your best demo." }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
RUBY
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
Heading(level: 2) { "Variants" }
|
|
45
|
+
|
|
46
|
+
render Docs::VisualCodeExample.new(title: "Variants", context: self) do
|
|
47
|
+
<<~RUBY
|
|
48
|
+
div(class: "flex flex-col gap-4 w-full") do
|
|
49
|
+
Bubble(variant: :default) { BubbleContent { "Default" } }
|
|
50
|
+
Bubble(variant: :secondary) { BubbleContent { "Secondary" } }
|
|
51
|
+
Bubble(variant: :muted) { BubbleContent { "Muted" } }
|
|
52
|
+
Bubble(variant: :tinted) { BubbleContent { "Tinted" } }
|
|
53
|
+
Bubble(variant: :outline) { BubbleContent { "Outline" } }
|
|
54
|
+
Bubble(variant: :ghost) { BubbleContent { "Ghost — unframed, full width for assistant text or markdown." } }
|
|
55
|
+
Bubble(variant: :destructive) { BubbleContent { "Destructive — something went wrong." } }
|
|
56
|
+
end
|
|
57
|
+
RUBY
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
Heading(level: 2) { "Alignment" }
|
|
61
|
+
|
|
62
|
+
render Docs::VisualCodeExample.new(title: "Start and end", context: self) do
|
|
63
|
+
<<~RUBY
|
|
64
|
+
div(class: "flex flex-col gap-4 w-full") do
|
|
65
|
+
Bubble(align: :start, variant: :muted) do
|
|
66
|
+
BubbleContent { "Aligned to the start (receiver)." }
|
|
67
|
+
end
|
|
68
|
+
Bubble(align: :end) do
|
|
69
|
+
BubbleContent { "Aligned to the end (sender)." }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
RUBY
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
Heading(level: 2) { "Reactions" }
|
|
76
|
+
|
|
77
|
+
render Docs::VisualCodeExample.new(title: "Reactions", context: self) do
|
|
78
|
+
<<~RUBY
|
|
79
|
+
div(class: "flex flex-col gap-10 w-full py-6") do
|
|
80
|
+
Bubble(variant: :muted) do
|
|
81
|
+
BubbleContent { "Reactions anchor to the bubble edge." }
|
|
82
|
+
BubbleReactions(role: "img", aria_label: "Reactions: thumbs up, fire, eyes, and 2 more") do
|
|
83
|
+
span { "👍" }
|
|
84
|
+
span { "🔥" }
|
|
85
|
+
span { "👀" }
|
|
86
|
+
span { "+2" }
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
Bubble(align: :end) do
|
|
90
|
+
BubbleContent { "Place them on top and to the start too." }
|
|
91
|
+
BubbleReactions(side: :top, align: :start, role: "img", aria_label: "Reaction: heart") do
|
|
92
|
+
span { "❤️" }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
RUBY
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
Heading(level: 2) { "Group" }
|
|
100
|
+
|
|
101
|
+
render Docs::VisualCodeExample.new(title: "Bubble group", context: self) do
|
|
102
|
+
<<~RUBY
|
|
103
|
+
BubbleGroup do
|
|
104
|
+
Bubble(variant: :muted) { BubbleContent { "First message in the group." } }
|
|
105
|
+
Bubble(variant: :muted) { BubbleContent { "Second one, tighter spacing." } }
|
|
106
|
+
Bubble(variant: :muted) { BubbleContent { "Third, all stacked together." } }
|
|
107
|
+
end
|
|
108
|
+
RUBY
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
Heading(level: 2) { "Link or button bubble" }
|
|
112
|
+
|
|
113
|
+
render Docs::VisualCodeExample.new(title: "Interactive content", context: self) do
|
|
114
|
+
<<~RUBY
|
|
115
|
+
div(class: "flex flex-col gap-4 w-full") do
|
|
116
|
+
Bubble(align: :end) do
|
|
117
|
+
BubbleContent(as: :a, href: "#") { "Tap to open the link →" }
|
|
118
|
+
end
|
|
119
|
+
Bubble(variant: :outline) do
|
|
120
|
+
BubbleContent(as: :button, type: "button") { "Retry sending" }
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
RUBY
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
Heading(level: 2) { "With Tooltip" }
|
|
127
|
+
|
|
128
|
+
render Docs::VisualCodeExample.new(title: "Reveal metadata on hover", context: self) do
|
|
129
|
+
<<~RUBY
|
|
130
|
+
Tooltip do
|
|
131
|
+
TooltipTrigger(class: "w-fit") do
|
|
132
|
+
Bubble(variant: :muted, class: "max-w-none") do
|
|
133
|
+
BubbleContent { "Read 9:41 AM" }
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
TooltipContent do
|
|
137
|
+
Text { "Delivered and read" }
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
RUBY
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
Heading(level: 2) { "With Popover" }
|
|
144
|
+
|
|
145
|
+
render Docs::VisualCodeExample.new(title: "Surface details on demand", context: self) do
|
|
146
|
+
<<~RUBY
|
|
147
|
+
Popover do
|
|
148
|
+
PopoverTrigger do
|
|
149
|
+
Bubble(variant: :destructive, class: "max-w-none") do
|
|
150
|
+
BubbleContent { "Message failed to send" }
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
PopoverContent(class: "w-64") do
|
|
154
|
+
Text(weight: :semibold) { "Delivery error" }
|
|
155
|
+
Text(size: :sm, class: "text-muted-foreground") { "The recipient's inbox is full. Try again later." }
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
RUBY
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
render Components::ComponentSetup::Tabs.new(component_name: component)
|
|
162
|
+
|
|
163
|
+
# components
|
|
164
|
+
render Docs::ComponentsTable.new(component_files(component))
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class BubbleGroup < Base
|
|
5
|
+
def view_template(&)
|
|
6
|
+
div(**attrs, &)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def default_attrs
|
|
12
|
+
{
|
|
13
|
+
data: {slot: "bubble-group"},
|
|
14
|
+
class: "flex min-w-0 flex-col gap-2"
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyUI
|
|
4
|
+
class BubbleReactions < Base
|
|
5
|
+
SIDES = {
|
|
6
|
+
top: "top-0 -translate-y-3/4",
|
|
7
|
+
bottom: "bottom-0 translate-y-3/4"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
ALIGNS = {
|
|
11
|
+
start: "left-3",
|
|
12
|
+
end: "right-3"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def initialize(side: :bottom, align: :end, **attrs)
|
|
16
|
+
@side = side
|
|
17
|
+
@align = align
|
|
18
|
+
super(**attrs)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def view_template(&)
|
|
22
|
+
div(**attrs, &)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def default_attrs
|
|
28
|
+
{
|
|
29
|
+
data: {slot: "bubble-reactions", side: @side, align: @align},
|
|
30
|
+
class: [
|
|
31
|
+
"absolute z-10 flex w-fit items-center justify-center rounded-full ring-3 ring-card bg-muted shrink-0 gap-1 px-1.5 py-0.5 has-[button]:p-0 text-sm",
|
|
32
|
+
SIDES[@side],
|
|
33
|
+
ALIGNS[@align]
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module RubyUI
|
|
4
4
|
class Combobox < Base
|
|
5
|
-
def initialize(term: nil, **)
|
|
5
|
+
def initialize(term: nil, placement: "bottom-start", **)
|
|
6
6
|
@term = term
|
|
7
|
+
@placement = placement
|
|
7
8
|
super(**)
|
|
8
9
|
end
|
|
9
10
|
|
|
@@ -19,6 +20,7 @@ module RubyUI
|
|
|
19
20
|
data: {
|
|
20
21
|
controller: "ruby-ui--combobox",
|
|
21
22
|
ruby_ui__combobox_term_value: @term,
|
|
23
|
+
ruby_ui__combobox_placement_value: @placement,
|
|
22
24
|
action: "turbo:morph@window->ruby-ui--combobox#updateTriggerContent"
|
|
23
25
|
}
|
|
24
26
|
}
|
|
@@ -19,7 +19,14 @@ module RubyUI
|
|
|
19
19
|
],
|
|
20
20
|
data: {
|
|
21
21
|
ruby_ui__combobox_target: "input",
|
|
22
|
-
|
|
22
|
+
ruby_ui__checkbox_group_target: "checkbox",
|
|
23
|
+
ruby_ui__form_field_target: "input",
|
|
24
|
+
action: %w[
|
|
25
|
+
ruby-ui--combobox#inputChanged
|
|
26
|
+
change->ruby-ui--checkbox-group#onChange
|
|
27
|
+
change->ruby-ui--form-field#onInput
|
|
28
|
+
invalid->ruby-ui--form-field#onInvalid
|
|
29
|
+
]
|
|
23
30
|
}
|
|
24
31
|
}
|
|
25
32
|
end
|
|
@@ -5,7 +5,8 @@ import { computePosition, autoUpdate, offset, flip } from "@floating-ui/dom";
|
|
|
5
5
|
export default class extends Controller {
|
|
6
6
|
static values = {
|
|
7
7
|
term: String,
|
|
8
|
-
minPopoverWidth: { type: Number, default: 240 }
|
|
8
|
+
minPopoverWidth: { type: Number, default: 240 },
|
|
9
|
+
placement: { type: String, default: "bottom-start" }
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
static targets = [
|
|
@@ -175,7 +176,7 @@ export default class extends Controller {
|
|
|
175
176
|
updatePopoverPosition() {
|
|
176
177
|
this.cleanup = autoUpdate(this.triggerTarget, this.popoverTarget, () => {
|
|
177
178
|
computePosition(this.triggerTarget, this.popoverTarget, {
|
|
178
|
-
placement:
|
|
179
|
+
placement: this.placementValue,
|
|
179
180
|
middleware: [offset(4), flip()],
|
|
180
181
|
}).then(({ x, y }) => {
|
|
181
182
|
Object.assign(this.popoverTarget.style, {
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module RubyUI
|
|
4
4
|
class ContextMenuContent < Base
|
|
5
|
-
def view_template(&
|
|
6
|
-
|
|
7
|
-
div(**attrs, &block)
|
|
8
|
-
end
|
|
5
|
+
def view_template(&)
|
|
6
|
+
div(**attrs, &)
|
|
9
7
|
end
|
|
10
8
|
|
|
11
9
|
private
|
|
@@ -14,9 +12,10 @@ module RubyUI
|
|
|
14
12
|
{
|
|
15
13
|
role: "menu",
|
|
16
14
|
aria_orientation: "vertical",
|
|
17
|
-
data_state: "
|
|
15
|
+
data_state: "closed",
|
|
16
|
+
data: {ruby_ui__context_menu_target: "content"},
|
|
18
17
|
class:
|
|
19
|
-
"z-50 min-w-[8rem] outline-none pointer-events-auto overflow-hidden rounded-md border bg-background p-1 text-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
18
|
+
"hidden absolute z-50 min-w-[8rem] outline-none pointer-events-auto overflow-hidden rounded-md border bg-background p-1 text-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
20
19
|
tabindex: "-1",
|
|
21
20
|
data_orientation: "vertical"
|
|
22
21
|
}
|