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.
@@ -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
+ }
@@ -10,6 +10,7 @@
10
10
  *= require ./features/gradients
11
11
  *= require ./features/clipboard
12
12
  *= require ./features/confirmation
13
+ *= require ./features/tooltip
13
14
  *= require ../../tokens/colors
14
15
  */
15
16
 
@@ -2,6 +2,5 @@ module WildayUi
2
2
  module ApplicationHelper
3
3
  include WildayUi::Components::ButtonHelper
4
4
  include WildayUi::JavascriptHelper
5
- include WildayUi::ThemeHelper
6
5
  end
7
6
  end
@@ -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.");