wilday_ui 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/builds/wilday_ui/index.js +237 -0
- data/app/assets/builds/wilday_ui/index.js.map +3 -3
- data/app/assets/stylesheets/wilday_ui/components/button/features/tooltip.css +258 -0
- data/app/assets/stylesheets/wilday_ui/components/button/index.css +1 -0
- data/app/helpers/wilday_ui/application_helper.rb +0 -1
- data/app/helpers/wilday_ui/components/button_helper.rb +135 -2
- data/app/javascript/wilday_ui/controllers/index.js +2 -0
- data/app/javascript/wilday_ui/controllers/tooltip_controller.js +318 -0
- data/lib/wilday_ui/version.rb +1 -1
- metadata +4 -3
- data/app/helpers/wilday_ui/theme_helper.rb +0 -19
@@ -0,0 +1,258 @@
|
|
1
|
+
.w-tooltip {
|
2
|
+
position: absolute;
|
3
|
+
z-index: 1000;
|
4
|
+
padding: 0.5rem 1rem;
|
5
|
+
font-size: 0.875rem;
|
6
|
+
line-height: 1.25rem;
|
7
|
+
border-radius: 0.375rem;
|
8
|
+
pointer-events: none;
|
9
|
+
opacity: 0;
|
10
|
+
transform: scale(0.95);
|
11
|
+
transition: opacity 150ms ease-in-out, transform 150ms ease-in-out;
|
12
|
+
max-width: 20rem; /* 320px */
|
13
|
+
word-wrap: break-word;
|
14
|
+
white-space: normal;
|
15
|
+
}
|
16
|
+
|
17
|
+
/* Position and Alignment transforms */
|
18
|
+
/* Bottom position */
|
19
|
+
.w-tooltip[data-position="bottom"][data-align="start"] {
|
20
|
+
transform-origin: top left;
|
21
|
+
transform: scale(0.95); /* Reset transform */
|
22
|
+
}
|
23
|
+
|
24
|
+
.w-tooltip[data-position="bottom"][data-align="center"] {
|
25
|
+
transform-origin: top center;
|
26
|
+
transform: translateX(-50%) scale(0.95);
|
27
|
+
}
|
28
|
+
|
29
|
+
.w-tooltip[data-position="bottom"][data-align="end"] {
|
30
|
+
transform-origin: top right;
|
31
|
+
transform: translateX(-100%) scale(0.95);
|
32
|
+
}
|
33
|
+
|
34
|
+
/* Top position */
|
35
|
+
.w-tooltip[data-position="top"][data-align="start"] {
|
36
|
+
transform-origin: bottom left;
|
37
|
+
transform: scale(0.95); /* Reset transform */
|
38
|
+
}
|
39
|
+
|
40
|
+
.w-tooltip[data-position="top"][data-align="center"] {
|
41
|
+
transform-origin: bottom center;
|
42
|
+
transform: translateX(-50%) scale(0.95);
|
43
|
+
}
|
44
|
+
|
45
|
+
.w-tooltip[data-position="top"][data-align="end"] {
|
46
|
+
transform-origin: bottom right;
|
47
|
+
transform: translateX(-100%) scale(0.95);
|
48
|
+
}
|
49
|
+
|
50
|
+
/* Left position */
|
51
|
+
.w-tooltip[data-position="left"][data-align="start"] {
|
52
|
+
transform-origin: right top;
|
53
|
+
transform: scale(0.95); /* Reset transform */
|
54
|
+
}
|
55
|
+
|
56
|
+
.w-tooltip[data-position="left"][data-align="center"] {
|
57
|
+
transform-origin: right center;
|
58
|
+
transform: translateY(-50%) scale(0.95);
|
59
|
+
}
|
60
|
+
|
61
|
+
.w-tooltip[data-position="left"][data-align="end"] {
|
62
|
+
transform-origin: right bottom;
|
63
|
+
transform: translateY(-100%) scale(0.95);
|
64
|
+
}
|
65
|
+
|
66
|
+
/* Right position */
|
67
|
+
.w-tooltip[data-position="right"][data-align="start"] {
|
68
|
+
transform-origin: left top;
|
69
|
+
transform: scale(0.95); /* Reset transform */
|
70
|
+
}
|
71
|
+
|
72
|
+
.w-tooltip[data-position="right"][data-align="center"] {
|
73
|
+
transform-origin: left center;
|
74
|
+
transform: translateY(-50%) scale(0.95);
|
75
|
+
}
|
76
|
+
|
77
|
+
.w-tooltip[data-position="right"][data-align="end"] {
|
78
|
+
transform-origin: left bottom;
|
79
|
+
transform: translateY(-100%) scale(0.95);
|
80
|
+
}
|
81
|
+
|
82
|
+
/* Visible state transforms */
|
83
|
+
.w-tooltip-visible[data-position="bottom"][data-align="start"],
|
84
|
+
.w-tooltip-visible[data-position="top"][data-align="start"],
|
85
|
+
.w-tooltip-visible[data-position="left"][data-align="start"],
|
86
|
+
.w-tooltip-visible[data-position="right"][data-align="start"] {
|
87
|
+
transform: scale(1);
|
88
|
+
}
|
89
|
+
|
90
|
+
.w-tooltip-visible[data-position="bottom"][data-align="center"],
|
91
|
+
.w-tooltip-visible[data-position="top"][data-align="center"] {
|
92
|
+
transform: translateX(-50%) scale(1);
|
93
|
+
}
|
94
|
+
|
95
|
+
.w-tooltip-visible[data-position="bottom"][data-align="end"],
|
96
|
+
.w-tooltip-visible[data-position="top"][data-align="end"] {
|
97
|
+
transform: translateX(-100%) scale(1);
|
98
|
+
}
|
99
|
+
|
100
|
+
.w-tooltip-visible[data-position="left"][data-align="center"],
|
101
|
+
.w-tooltip-visible[data-position="right"][data-align="center"] {
|
102
|
+
transform: translateY(-50%) scale(1);
|
103
|
+
}
|
104
|
+
|
105
|
+
.w-tooltip-visible[data-position="left"][data-align="end"],
|
106
|
+
.w-tooltip-visible[data-position="right"][data-align="end"] {
|
107
|
+
transform: translateY(-100%) scale(1);
|
108
|
+
}
|
109
|
+
|
110
|
+
.w-tooltip-visible {
|
111
|
+
opacity: 1;
|
112
|
+
transform: scale(1);
|
113
|
+
}
|
114
|
+
|
115
|
+
/* Themes */
|
116
|
+
.w-tooltip-light {
|
117
|
+
background-color: white;
|
118
|
+
color: #1f2937;
|
119
|
+
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
120
|
+
border: 1px solid #e5e7eb;
|
121
|
+
}
|
122
|
+
|
123
|
+
.w-tooltip-dark {
|
124
|
+
background-color: #1f2937;
|
125
|
+
color: white;
|
126
|
+
}
|
127
|
+
|
128
|
+
.w-tooltip-transparent {
|
129
|
+
background-color: rgba(0, 0, 0, 0.75);
|
130
|
+
color: white;
|
131
|
+
}
|
132
|
+
|
133
|
+
/* Add size variants */
|
134
|
+
.w-tooltip-size-sm {
|
135
|
+
max-width: 12rem; /* 192px */
|
136
|
+
}
|
137
|
+
|
138
|
+
.w-tooltip-size-md {
|
139
|
+
max-width: 20rem; /* 320px - default */
|
140
|
+
}
|
141
|
+
|
142
|
+
.w-tooltip-size-lg {
|
143
|
+
max-width: 24rem; /* 384px */
|
144
|
+
}
|
145
|
+
|
146
|
+
/* Arrow base styles */
|
147
|
+
.w-tooltip-arrow {
|
148
|
+
--arrow-size: 8px;
|
149
|
+
padding: calc(
|
150
|
+
var(--arrow-size) + 4px
|
151
|
+
) !important; /* Add padding to accommodate arrow */
|
152
|
+
}
|
153
|
+
|
154
|
+
.w-tooltip-arrow::before {
|
155
|
+
content: "";
|
156
|
+
position: absolute;
|
157
|
+
width: var(--arrow-size);
|
158
|
+
height: var(--arrow-size);
|
159
|
+
background: inherit;
|
160
|
+
border: 1px solid;
|
161
|
+
border-color: inherit;
|
162
|
+
transform: rotate(45deg);
|
163
|
+
z-index: -1;
|
164
|
+
}
|
165
|
+
|
166
|
+
/* Arrow positions */
|
167
|
+
.w-tooltip-arrow[data-position="top"]::before {
|
168
|
+
bottom: calc(var(--arrow-size) / -2);
|
169
|
+
border-top: 0;
|
170
|
+
border-left: 0;
|
171
|
+
}
|
172
|
+
|
173
|
+
.w-tooltip-arrow[data-position="bottom"]::before {
|
174
|
+
top: calc(var(--arrow-size) / -2);
|
175
|
+
border-bottom: 0;
|
176
|
+
border-right: 0;
|
177
|
+
}
|
178
|
+
|
179
|
+
.w-tooltip-arrow[data-position="left"]::before {
|
180
|
+
right: calc(var(--arrow-size) / -2);
|
181
|
+
border-left: 0;
|
182
|
+
border-bottom: 0;
|
183
|
+
}
|
184
|
+
|
185
|
+
.w-tooltip-arrow[data-position="right"]::before {
|
186
|
+
left: calc(var(--arrow-size) / -2);
|
187
|
+
border-right: 0;
|
188
|
+
border-top: 0;
|
189
|
+
}
|
190
|
+
|
191
|
+
/* Arrow alignment */
|
192
|
+
.w-tooltip-arrow[data-position="top"][data-align="start"]::before,
|
193
|
+
.w-tooltip-arrow[data-position="bottom"][data-align="start"]::before {
|
194
|
+
left: var(--arrow-size);
|
195
|
+
}
|
196
|
+
|
197
|
+
.w-tooltip-arrow[data-position="top"][data-align="center"]::before,
|
198
|
+
.w-tooltip-arrow[data-position="bottom"][data-align="center"]::before {
|
199
|
+
left: 50%;
|
200
|
+
transform: translateX(-50%) rotate(45deg);
|
201
|
+
}
|
202
|
+
|
203
|
+
.w-tooltip-arrow[data-position="top"][data-align="end"]::before,
|
204
|
+
.w-tooltip-arrow[data-position="bottom"][data-align="end"]::before {
|
205
|
+
right: var(--arrow-size);
|
206
|
+
left: auto;
|
207
|
+
}
|
208
|
+
|
209
|
+
.w-tooltip-arrow[data-position="left"][data-align="start"]::before,
|
210
|
+
.w-tooltip-arrow[data-position="right"][data-align="start"]::before {
|
211
|
+
top: var(--arrow-size);
|
212
|
+
}
|
213
|
+
|
214
|
+
.w-tooltip-arrow[data-position="left"][data-align="center"]::before,
|
215
|
+
.w-tooltip-arrow[data-position="right"][data-align="center"]::before {
|
216
|
+
top: 50%;
|
217
|
+
transform: translateY(-50%) rotate(45deg);
|
218
|
+
}
|
219
|
+
|
220
|
+
.w-tooltip-arrow[data-position="left"][data-align="end"]::before,
|
221
|
+
.w-tooltip-arrow[data-position="right"][data-align="end"]::before {
|
222
|
+
bottom: var(--arrow-size);
|
223
|
+
top: auto;
|
224
|
+
}
|
225
|
+
|
226
|
+
/* Theme-specific arrow styles */
|
227
|
+
.w-tooltip-arrow.w-tooltip-light::before {
|
228
|
+
background-color: white;
|
229
|
+
border-color: #e5e7eb;
|
230
|
+
}
|
231
|
+
|
232
|
+
.w-tooltip-arrow.w-tooltip-dark::before {
|
233
|
+
background-color: #1f2937;
|
234
|
+
border-color: #1f2937;
|
235
|
+
}
|
236
|
+
|
237
|
+
.w-tooltip-arrow.w-tooltip-transparent::before {
|
238
|
+
background-color: rgba(0, 0, 0, 0.75);
|
239
|
+
border-color: transparent;
|
240
|
+
}
|
241
|
+
|
242
|
+
/* Add custom theme support */
|
243
|
+
.w-tooltip-custom {
|
244
|
+
background-color: var(--tooltip-bg-color);
|
245
|
+
color: var(--tooltip-text-color);
|
246
|
+
}
|
247
|
+
|
248
|
+
.w-tooltip-arrow.w-tooltip-custom::before {
|
249
|
+
background-color: var(--tooltip-bg-color);
|
250
|
+
border-color: var(--tooltip-bg-color);
|
251
|
+
}
|
252
|
+
|
253
|
+
/* Optional: Add different max-widths for different screen sizes */
|
254
|
+
@media (max-width: 640px) {
|
255
|
+
.w-tooltip {
|
256
|
+
max-width: 16rem; /* 256px */
|
257
|
+
}
|
258
|
+
}
|
@@ -21,6 +21,14 @@ module WildayUi
|
|
21
21
|
wrapper_required: true,
|
22
22
|
stimulus_controller: "confirmation",
|
23
23
|
default_stimulus_action: "click->confirmation#showDialog"
|
24
|
+
},
|
25
|
+
tooltip: {
|
26
|
+
wrapper_required: true,
|
27
|
+
stimulus_controller: "tooltip",
|
28
|
+
default_stimulus_action: {
|
29
|
+
hover: "mouseenter->tooltip#show mouseleave->tooltip#hide focusin->tooltip#show focusout->tooltip#hide",
|
30
|
+
click: "click->tooltip#toggle"
|
31
|
+
}
|
24
32
|
}
|
25
33
|
# Add more features here as needed
|
26
34
|
# tooltip: {
|
@@ -54,6 +62,7 @@ module WildayUi
|
|
54
62
|
theme: nil,
|
55
63
|
copy_to_clipboard: nil,
|
56
64
|
confirm: nil,
|
65
|
+
tooltip: nil,
|
57
66
|
**options
|
58
67
|
)
|
59
68
|
content_for(:head) { stylesheet_link_tag "wilday_ui/components/button/index", media: "all" }
|
@@ -80,7 +89,7 @@ module WildayUi
|
|
80
89
|
gradient_class = get_gradient_class(gradient)
|
81
90
|
|
82
91
|
# Setup features that require Stimulus controllers
|
83
|
-
active_features = determine_active_features(loading, dropdown, loading_text, copy_to_clipboard, confirm, use_default_controller)
|
92
|
+
active_features = determine_active_features(loading, dropdown, loading_text, copy_to_clipboard, confirm, tooltip, use_default_controller)
|
84
93
|
|
85
94
|
setup_features(active_features, options, use_default_controller, loading_text)
|
86
95
|
|
@@ -114,6 +123,15 @@ module WildayUi
|
|
114
123
|
)
|
115
124
|
end
|
116
125
|
|
126
|
+
if tooltip
|
127
|
+
setup_tooltip_options(
|
128
|
+
options,
|
129
|
+
additional_classes,
|
130
|
+
tooltip,
|
131
|
+
wrapper_data
|
132
|
+
)
|
133
|
+
end
|
134
|
+
|
117
135
|
# Setup wrapper options if any feature requires it
|
118
136
|
wrapper_options = setup_wrapper_options(
|
119
137
|
active_features,
|
@@ -297,12 +315,13 @@ module WildayUi
|
|
297
315
|
styles.map { |k, v| "#{k}: #{v}" }.join(";")
|
298
316
|
end
|
299
317
|
|
300
|
-
def determine_active_features(loading, dropdown, loading_text = nil, copy_to_clipboard = nil, confirm = nil, use_default_controller = true)
|
318
|
+
def determine_active_features(loading, dropdown, loading_text = nil, copy_to_clipboard = nil, confirm = nil, tooltip = nil, use_default_controller = true)
|
301
319
|
features = {}
|
302
320
|
features[:loading] = true if (loading || loading_text.present?) && use_default_controller
|
303
321
|
features[:dropdown] = true if dropdown && use_default_controller
|
304
322
|
features[:copy_to_clipboard] = true if copy_to_clipboard.present? && use_default_controller
|
305
323
|
features[:confirm] = true if confirm.present? && use_default_controller
|
324
|
+
features[:tooltip] = true if tooltip.present? && use_default_controller
|
306
325
|
features
|
307
326
|
end
|
308
327
|
|
@@ -507,6 +526,120 @@ module WildayUi
|
|
507
526
|
end
|
508
527
|
end
|
509
528
|
|
529
|
+
def setup_tooltip_options(options, additional_classes, tooltip, wrapper_data)
|
530
|
+
tooltip_config = normalize_tooltip_options(tooltip)
|
531
|
+
|
532
|
+
# Check if dropdown is present
|
533
|
+
has_dropdown = wrapper_data[:controller]&.include?("dropdown")
|
534
|
+
has_clipboard = wrapper_data[:controller]&.include?("clipboard")
|
535
|
+
|
536
|
+
# Get the appropriate action based on trigger type
|
537
|
+
# Force hover behavior if dropdown is present
|
538
|
+
trigger_type = (has_dropdown || has_clipboard) ? :hover : tooltip_config[:trigger].to_sym
|
539
|
+
tooltip_action = BUTTON_FEATURES[:tooltip][:default_stimulus_action][trigger_type]
|
540
|
+
|
541
|
+
# Merge controllers
|
542
|
+
existing_controller = wrapper_data[:controller]
|
543
|
+
wrapper_data[:controller] = [
|
544
|
+
existing_controller,
|
545
|
+
"tooltip"
|
546
|
+
].compact.join(" ")
|
547
|
+
|
548
|
+
# Merge actions
|
549
|
+
existing_action = wrapper_data[:action]
|
550
|
+
if has_dropdown
|
551
|
+
# Keep the dropdown toggle action and add tooltip hover actions
|
552
|
+
wrapper_data[:action] = [
|
553
|
+
"click->dropdown#toggle", # Ensure dropdown action comes first
|
554
|
+
tooltip_action
|
555
|
+
].compact.join(" ")
|
556
|
+
elsif has_clipboard
|
557
|
+
# Keep the clipboard copy action and add tooltip hover actions
|
558
|
+
wrapper_data[:action] = [
|
559
|
+
"click->clipboard#copy click->button#toggleLoading",
|
560
|
+
tooltip_action
|
561
|
+
].compact.join(" ")
|
562
|
+
else
|
563
|
+
wrapper_data[:action] = [
|
564
|
+
existing_action,
|
565
|
+
tooltip_action
|
566
|
+
].compact.join(" ")
|
567
|
+
end
|
568
|
+
|
569
|
+
# Handle theme data
|
570
|
+
theme = tooltip_config[:theme]
|
571
|
+
theme_name = theme.is_a?(Hash) ? theme[:name] : theme
|
572
|
+
|
573
|
+
wrapper_data.merge!(
|
574
|
+
tooltip_content_value: tooltip_config[:content],
|
575
|
+
tooltip_position_value: tooltip_config[:position],
|
576
|
+
tooltip_align_value: tooltip_config[:align],
|
577
|
+
tooltip_trigger_value: trigger_type,
|
578
|
+
tooltip_show_delay_value: tooltip_config[:delay][:show],
|
579
|
+
tooltip_hide_delay_value: tooltip_config[:delay][:hide],
|
580
|
+
tooltip_offset_value: tooltip_config[:offset],
|
581
|
+
tooltip_theme_value: theme_name,
|
582
|
+
tooltip_size_value: tooltip_config[:size],
|
583
|
+
tooltip_arrow_value: tooltip_config[:arrow]
|
584
|
+
)
|
585
|
+
|
586
|
+
# Add custom theme styles if present
|
587
|
+
if theme.is_a?(Hash) && theme[:custom]
|
588
|
+
custom_styles = []
|
589
|
+
custom_styles << "--tooltip-text-color: #{theme[:custom][:color]}" if theme[:custom][:color]
|
590
|
+
custom_styles << "--tooltip-bg-color: #{theme[:custom][:background]}" if theme[:custom][:background]
|
591
|
+
wrapper_data[:tooltip_custom_style_value] = custom_styles.join(";")
|
592
|
+
end
|
593
|
+
|
594
|
+
options[:data][:tooltip_target] = "trigger"
|
595
|
+
options[:aria] ||= {}
|
596
|
+
options[:aria][:describedby] = "tooltip-#{SecureRandom.hex(4)}"
|
597
|
+
end
|
598
|
+
|
599
|
+
def normalize_tooltip_options(options)
|
600
|
+
if options.is_a?(String)
|
601
|
+
{
|
602
|
+
content: options,
|
603
|
+
position: "top",
|
604
|
+
align: "center",
|
605
|
+
trigger: "hover",
|
606
|
+
delay: { show: 0, hide: 0 },
|
607
|
+
offset: 8,
|
608
|
+
theme: "light",
|
609
|
+
size: "md",
|
610
|
+
arrow: false
|
611
|
+
}
|
612
|
+
else
|
613
|
+
theme = options[:theme]
|
614
|
+
theme_data = if theme.is_a?(Hash) && theme[:custom]
|
615
|
+
{
|
616
|
+
name: "custom",
|
617
|
+
custom: {
|
618
|
+
color: theme.dig(:custom, :color),
|
619
|
+
background: theme.dig(:custom, :background)
|
620
|
+
}
|
621
|
+
}
|
622
|
+
else
|
623
|
+
{ name: theme || "light" }
|
624
|
+
end
|
625
|
+
|
626
|
+
{
|
627
|
+
content: options[:content],
|
628
|
+
position: options[:position] || "top",
|
629
|
+
align: options[:align] || "center",
|
630
|
+
trigger: options[:trigger] || "hover",
|
631
|
+
delay: {
|
632
|
+
show: options.dig(:delay, :show) || 0,
|
633
|
+
hide: options.dig(:delay, :hide) || 0
|
634
|
+
},
|
635
|
+
offset: options[:offset] || 8,
|
636
|
+
theme: theme_data,
|
637
|
+
size: options[:size] || "md",
|
638
|
+
arrow: options[:arrow] || false
|
639
|
+
}
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
510
643
|
def render_button(content, variant_class, size_class, radius_class, gradient_class, icon, icon_position, icon_only,
|
511
644
|
loading, loading_text, additional_classes, disabled, options, href, underline,
|
512
645
|
dropdown, dropdown_items, dropdown_icon, wrapper_options)
|
@@ -3,6 +3,7 @@ import ButtonController from "./button_controller";
|
|
3
3
|
import DropdownController from "./dropdown_controller";
|
4
4
|
import ClipboardController from "./clipboard_controller";
|
5
5
|
import ConfirmationController from "./confirmation_controller";
|
6
|
+
import TooltipController from "./tooltip_controller";
|
6
7
|
// Initialize Stimulus
|
7
8
|
const application = Application.start();
|
8
9
|
window.Stimulus = application;
|
@@ -12,6 +13,7 @@ application.register("button", ButtonController);
|
|
12
13
|
application.register("dropdown", DropdownController);
|
13
14
|
application.register("clipboard", ClipboardController);
|
14
15
|
application.register("confirmation", ConfirmationController);
|
16
|
+
application.register("tooltip", TooltipController);
|
15
17
|
// Debug check to ensure Stimulus is loaded
|
16
18
|
// if (window.Stimulus) {
|
17
19
|
// console.log("✅ Stimulus is loaded and initialized.");
|