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.
@@ -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.");