wilday_ui 0.7.0 → 0.8.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/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.");
|