wilday_ui 0.6.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 +460 -0
- data/app/assets/builds/wilday_ui/index.js.map +3 -3
- data/app/assets/stylesheets/wilday_ui/components/button/features/clipboard.css +108 -0
- data/app/assets/stylesheets/wilday_ui/components/button/features/confirmation.css +136 -0
- data/app/assets/stylesheets/wilday_ui/components/button/features/tooltip.css +258 -0
- data/app/assets/stylesheets/wilday_ui/components/button/features/variants.css +5 -0
- data/app/assets/stylesheets/wilday_ui/components/button/index.css +4 -0
- data/app/assets/stylesheets/wilday_ui/tokens/colors.css +109 -0
- data/app/helpers/wilday_ui/components/button_helper.rb +253 -2
- data/app/javascript/wilday_ui/controllers/clipboard_controller.js +76 -0
- data/app/javascript/wilday_ui/controllers/confirmation_controller.js +216 -0
- data/app/javascript/wilday_ui/controllers/index.js +6 -1
- data/app/javascript/wilday_ui/controllers/tooltip_controller.js +318 -0
- data/lib/wilday_ui/version.rb +1 -1
- metadata +9 -2
@@ -0,0 +1,318 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["trigger"];
|
5
|
+
static values = {
|
6
|
+
content: String,
|
7
|
+
position: { type: String, default: "top" },
|
8
|
+
align: { type: String, default: "center" },
|
9
|
+
trigger: { type: String, default: "hover" },
|
10
|
+
showDelay: { type: Number, default: 0 },
|
11
|
+
hideDelay: { type: Number, default: 0 },
|
12
|
+
offset: { type: Number, default: 8 },
|
13
|
+
theme: { type: String, default: "light" },
|
14
|
+
size: { type: String, default: "md" },
|
15
|
+
arrow: { type: Boolean, default: false },
|
16
|
+
customStyle: String,
|
17
|
+
};
|
18
|
+
|
19
|
+
connect() {
|
20
|
+
this.tooltipElement = null;
|
21
|
+
this.showTimeoutId = null;
|
22
|
+
this.hideTimeoutId = null;
|
23
|
+
this.setupTooltip();
|
24
|
+
|
25
|
+
// Add scroll handler
|
26
|
+
this.scrollHandler = () => {
|
27
|
+
if (this.tooltipElement.style.display !== "none") {
|
28
|
+
// Check if trigger is in viewport
|
29
|
+
const triggerRect = this.triggerTarget.getBoundingClientRect();
|
30
|
+
const isInViewport =
|
31
|
+
triggerRect.top >= 0 &&
|
32
|
+
triggerRect.left >= 0 &&
|
33
|
+
triggerRect.bottom <=
|
34
|
+
(window.innerHeight || document.documentElement.clientHeight) &&
|
35
|
+
triggerRect.right <=
|
36
|
+
(window.innerWidth || document.documentElement.clientWidth);
|
37
|
+
|
38
|
+
if (isInViewport) {
|
39
|
+
// Update position if trigger is visible
|
40
|
+
this.position();
|
41
|
+
} else {
|
42
|
+
// Hide tooltip if trigger is not visible
|
43
|
+
this.hide();
|
44
|
+
}
|
45
|
+
}
|
46
|
+
};
|
47
|
+
window.addEventListener("scroll", this.scrollHandler);
|
48
|
+
}
|
49
|
+
|
50
|
+
disconnect() {
|
51
|
+
if (this.clickOutsideHandler) {
|
52
|
+
document.removeEventListener("click", this.clickOutsideHandler);
|
53
|
+
}
|
54
|
+
window.removeEventListener("scroll", this.scrollHandler);
|
55
|
+
this.removeTooltip();
|
56
|
+
}
|
57
|
+
|
58
|
+
setupTooltip() {
|
59
|
+
this.tooltipElement = this.createTooltipElement();
|
60
|
+
document.body.appendChild(this.tooltipElement);
|
61
|
+
|
62
|
+
if (this.triggerValue === "hover") {
|
63
|
+
this.triggerTarget.addEventListener("mouseenter", () => this.show());
|
64
|
+
this.triggerTarget.addEventListener("mouseleave", () => this.hide());
|
65
|
+
this.triggerTarget.addEventListener("focusin", () => this.show());
|
66
|
+
this.triggerTarget.addEventListener("focusout", () => this.hide());
|
67
|
+
} else if (this.triggerValue === "click") {
|
68
|
+
// Check if dropdown or clipboard is present
|
69
|
+
const hasDropdown =
|
70
|
+
this.element.hasAttribute("data-controller") &&
|
71
|
+
this.element.getAttribute("data-controller").includes("dropdown");
|
72
|
+
const hasClipboard =
|
73
|
+
this.element.hasAttribute("data-controller") &&
|
74
|
+
this.element.getAttribute("data-controller").includes("clipboard");
|
75
|
+
|
76
|
+
if (hasDropdown || hasClipboard) {
|
77
|
+
// Force hover behavior for dropdown/clipboard buttons
|
78
|
+
this.triggerTarget.addEventListener("mouseenter", () => this.show());
|
79
|
+
this.triggerTarget.addEventListener("mouseleave", () => this.hide());
|
80
|
+
} else {
|
81
|
+
// Normal click behavior
|
82
|
+
this.triggerTarget.addEventListener("click", (e) => {
|
83
|
+
e.stopPropagation();
|
84
|
+
this.toggle();
|
85
|
+
});
|
86
|
+
|
87
|
+
this.clickOutsideHandler = (e) => {
|
88
|
+
if (
|
89
|
+
!this.triggerTarget.contains(e.target) &&
|
90
|
+
!this.tooltipElement.contains(e.target)
|
91
|
+
) {
|
92
|
+
this.hide();
|
93
|
+
}
|
94
|
+
};
|
95
|
+
document.addEventListener("click", this.clickOutsideHandler);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
createTooltipElement() {
|
101
|
+
const tooltip = document.createElement("div");
|
102
|
+
tooltip.id = this.triggerTarget.getAttribute("aria-describedby");
|
103
|
+
tooltip.className = `w-tooltip w-tooltip-${this.themeValue} w-tooltip-size-${this.sizeValue}`;
|
104
|
+
if (this.arrowValue) tooltip.classList.add("w-tooltip-arrow"); // Add arrow class if enabled
|
105
|
+
tooltip.setAttribute("role", "tooltip");
|
106
|
+
tooltip.setAttribute("data-position", this.positionValue);
|
107
|
+
tooltip.setAttribute("data-align", this.alignValue);
|
108
|
+
tooltip.innerHTML = this.contentValue;
|
109
|
+
|
110
|
+
// Apply custom styles if present
|
111
|
+
if (this.hasCustomStyleValue && this.customStyleValue) {
|
112
|
+
tooltip.style.cssText += this.customStyleValue;
|
113
|
+
}
|
114
|
+
|
115
|
+
tooltip.style.display = "none";
|
116
|
+
return tooltip;
|
117
|
+
}
|
118
|
+
|
119
|
+
show() {
|
120
|
+
clearTimeout(this.hideTimeoutId);
|
121
|
+
this.showTimeoutId = setTimeout(() => {
|
122
|
+
// Reset any existing transforms and make tooltip visible but hidden
|
123
|
+
this.tooltipElement.style.transform = "none";
|
124
|
+
this.tooltipElement.style.visibility = "hidden";
|
125
|
+
this.tooltipElement.style.display = "block";
|
126
|
+
|
127
|
+
// Force a reflow to ensure dimensions are calculated correctly
|
128
|
+
this.tooltipElement.offsetHeight;
|
129
|
+
|
130
|
+
// Position the tooltip
|
131
|
+
this.position();
|
132
|
+
|
133
|
+
// Make visible with transition
|
134
|
+
this.tooltipElement.style.visibility = "visible";
|
135
|
+
requestAnimationFrame(() => {
|
136
|
+
this.tooltipElement.classList.add("w-tooltip-visible");
|
137
|
+
});
|
138
|
+
}, this.showDelayValue);
|
139
|
+
}
|
140
|
+
|
141
|
+
hide() {
|
142
|
+
clearTimeout(this.showTimeoutId);
|
143
|
+
this.hideTimeoutId = setTimeout(() => {
|
144
|
+
this.tooltipElement.classList.remove("w-tooltip-visible");
|
145
|
+
setTimeout(() => {
|
146
|
+
this.tooltipElement.style.display = "none";
|
147
|
+
}, 150); // Match transition duration
|
148
|
+
}, this.hideDelayValue);
|
149
|
+
}
|
150
|
+
|
151
|
+
toggle() {
|
152
|
+
if (this.tooltipElement.style.display === "none") {
|
153
|
+
this.show();
|
154
|
+
} else {
|
155
|
+
this.hide();
|
156
|
+
}
|
157
|
+
}
|
158
|
+
|
159
|
+
position() {
|
160
|
+
const triggerRect = this.triggerTarget.getBoundingClientRect();
|
161
|
+
const tooltipRect = this.tooltipElement.getBoundingClientRect();
|
162
|
+
const viewportHeight = window.innerHeight;
|
163
|
+
const viewportWidth = window.innerWidth;
|
164
|
+
const arrowOffset = this.arrowValue ? 2 : 0;
|
165
|
+
|
166
|
+
// Calculate scroll position
|
167
|
+
const scrollX = window.pageXOffset || document.documentElement.scrollLeft;
|
168
|
+
const scrollY = window.pageYOffset || document.documentElement.scrollTop;
|
169
|
+
|
170
|
+
// Debug initial values
|
171
|
+
console.log("=== Initial Values ===");
|
172
|
+
console.log("Viewport Height:", viewportHeight);
|
173
|
+
console.log("Viewport Width:", viewportWidth);
|
174
|
+
console.log("Trigger Top:", triggerRect.top);
|
175
|
+
console.log("Trigger Bottom:", triggerRect.bottom);
|
176
|
+
console.log("Trigger Left:", triggerRect.left);
|
177
|
+
console.log("Trigger Right:", triggerRect.right);
|
178
|
+
console.log("Tooltip Height:", tooltipRect.height);
|
179
|
+
console.log("Tooltip Width:", tooltipRect.width);
|
180
|
+
|
181
|
+
let position = this.positionValue;
|
182
|
+
let top, left;
|
183
|
+
|
184
|
+
// Calculate available space in viewport
|
185
|
+
const spaceBelow = viewportHeight - triggerRect.bottom;
|
186
|
+
const spaceAbove = triggerRect.top;
|
187
|
+
const spaceRight = viewportWidth - triggerRect.right;
|
188
|
+
const spaceLeft = triggerRect.left;
|
189
|
+
const requiredSpaceVertical =
|
190
|
+
tooltipRect.height + this.offsetValue + arrowOffset;
|
191
|
+
const requiredSpaceHorizontal =
|
192
|
+
tooltipRect.width + this.offsetValue + arrowOffset;
|
193
|
+
|
194
|
+
// Debug space
|
195
|
+
console.log("\n=== Space Calculation ===");
|
196
|
+
console.log("Space Above:", spaceAbove);
|
197
|
+
console.log("Space Below:", spaceBelow);
|
198
|
+
console.log("Space Left:", spaceLeft);
|
199
|
+
console.log("Space Right:", spaceRight);
|
200
|
+
console.log("Required Space Vertical:", requiredSpaceVertical);
|
201
|
+
console.log("Required Space Horizontal:", requiredSpaceHorizontal);
|
202
|
+
|
203
|
+
// Determine position based on available viewport space
|
204
|
+
if (position === "right" && spaceRight >= requiredSpaceHorizontal) {
|
205
|
+
position = "right";
|
206
|
+
console.log("Using right - sufficient space");
|
207
|
+
} else if (position === "left" && spaceLeft >= requiredSpaceHorizontal) {
|
208
|
+
position = "left";
|
209
|
+
console.log("Using left - sufficient space");
|
210
|
+
} else if (position === "top" && spaceAbove >= requiredSpaceVertical) {
|
211
|
+
position = "top";
|
212
|
+
console.log("Using top - sufficient space");
|
213
|
+
} else if (position === "bottom" && spaceBelow >= requiredSpaceVertical) {
|
214
|
+
position = "bottom";
|
215
|
+
console.log("Using bottom - sufficient space");
|
216
|
+
} else if (spaceBelow >= requiredSpaceVertical) {
|
217
|
+
position = "bottom";
|
218
|
+
console.log("Fallback to bottom - sufficient space");
|
219
|
+
} else {
|
220
|
+
position = "top";
|
221
|
+
console.log("Fallback to top - insufficient space below");
|
222
|
+
}
|
223
|
+
|
224
|
+
// Calculate viewport-relative position
|
225
|
+
switch (position) {
|
226
|
+
case "top":
|
227
|
+
// Position above trigger
|
228
|
+
top =
|
229
|
+
triggerRect.top - tooltipRect.height - this.offsetValue - arrowOffset;
|
230
|
+
if (this.alignValue === "end") {
|
231
|
+
left = triggerRect.right - tooltipRect.width;
|
232
|
+
} else if (this.alignValue === "center") {
|
233
|
+
left =
|
234
|
+
triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;
|
235
|
+
} else {
|
236
|
+
// start
|
237
|
+
left = triggerRect.left;
|
238
|
+
}
|
239
|
+
break;
|
240
|
+
case "bottom":
|
241
|
+
// Position below trigger
|
242
|
+
top = triggerRect.bottom + this.offsetValue + arrowOffset;
|
243
|
+
if (this.alignValue === "end") {
|
244
|
+
left = triggerRect.right - tooltipRect.width;
|
245
|
+
} else if (this.alignValue === "center") {
|
246
|
+
left =
|
247
|
+
triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;
|
248
|
+
} else {
|
249
|
+
// start
|
250
|
+
left = triggerRect.left;
|
251
|
+
}
|
252
|
+
break;
|
253
|
+
case "left":
|
254
|
+
// Position to the left of trigger
|
255
|
+
left =
|
256
|
+
triggerRect.left - tooltipRect.width - this.offsetValue - arrowOffset;
|
257
|
+
if (this.alignValue === "end") {
|
258
|
+
top = triggerRect.bottom - tooltipRect.height;
|
259
|
+
} else if (this.alignValue === "center") {
|
260
|
+
top =
|
261
|
+
triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2;
|
262
|
+
} else {
|
263
|
+
// start
|
264
|
+
top = triggerRect.top;
|
265
|
+
}
|
266
|
+
break;
|
267
|
+
case "right":
|
268
|
+
// Position to the right of trigger
|
269
|
+
left = triggerRect.right + this.offsetValue + arrowOffset;
|
270
|
+
if (this.alignValue === "end") {
|
271
|
+
top = triggerRect.bottom - tooltipRect.height;
|
272
|
+
} else if (this.alignValue === "center") {
|
273
|
+
top =
|
274
|
+
triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2;
|
275
|
+
} else {
|
276
|
+
// start
|
277
|
+
top = triggerRect.top;
|
278
|
+
}
|
279
|
+
break;
|
280
|
+
}
|
281
|
+
|
282
|
+
// Ensure tooltip stays within viewport
|
283
|
+
if (top < 0) {
|
284
|
+
top = this.offsetValue; // Keep some padding from top
|
285
|
+
} else if (top + tooltipRect.height > viewportHeight) {
|
286
|
+
top = viewportHeight - tooltipRect.height - this.offsetValue;
|
287
|
+
}
|
288
|
+
|
289
|
+
if (left < 0) {
|
290
|
+
left = this.offsetValue; // Keep some padding from left
|
291
|
+
} else if (left + tooltipRect.width > viewportWidth) {
|
292
|
+
left = viewportWidth - tooltipRect.width - this.offsetValue;
|
293
|
+
}
|
294
|
+
|
295
|
+
// Debug final position
|
296
|
+
console.log("\n=== Final Position ===");
|
297
|
+
console.log("Position:", position);
|
298
|
+
console.log("Top:", top);
|
299
|
+
console.log("Left:", left);
|
300
|
+
console.log("Alignment:", this.alignValue);
|
301
|
+
|
302
|
+
// Add scroll position to final coordinates
|
303
|
+
top += scrollY;
|
304
|
+
left += scrollX;
|
305
|
+
|
306
|
+
// Update tooltip position and data attribute
|
307
|
+
this.tooltipElement.setAttribute("data-position", position);
|
308
|
+
this.tooltipElement.style.top = `${top}px`;
|
309
|
+
this.tooltipElement.style.left = `${left}px`;
|
310
|
+
}
|
311
|
+
|
312
|
+
removeTooltip() {
|
313
|
+
if (this.tooltipElement) {
|
314
|
+
this.tooltipElement.remove();
|
315
|
+
this.tooltipElement = null;
|
316
|
+
}
|
317
|
+
}
|
318
|
+
}
|
data/lib/wilday_ui/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wilday_ui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- davidwinalda
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
11
|
+
date: 2024-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -45,6 +45,8 @@ files:
|
|
45
45
|
- app/assets/stylesheets/wilday_ui/application.css
|
46
46
|
- app/assets/stylesheets/wilday_ui/button.css
|
47
47
|
- app/assets/stylesheets/wilday_ui/components/button/base.css
|
48
|
+
- app/assets/stylesheets/wilday_ui/components/button/features/clipboard.css
|
49
|
+
- app/assets/stylesheets/wilday_ui/components/button/features/confirmation.css
|
48
50
|
- app/assets/stylesheets/wilday_ui/components/button/features/dropdown.css
|
49
51
|
- app/assets/stylesheets/wilday_ui/components/button/features/gradients.css
|
50
52
|
- app/assets/stylesheets/wilday_ui/components/button/features/icons.css
|
@@ -52,16 +54,21 @@ files:
|
|
52
54
|
- app/assets/stylesheets/wilday_ui/components/button/features/old_variants.css
|
53
55
|
- app/assets/stylesheets/wilday_ui/components/button/features/radius.css
|
54
56
|
- app/assets/stylesheets/wilday_ui/components/button/features/sizes.css
|
57
|
+
- app/assets/stylesheets/wilday_ui/components/button/features/tooltip.css
|
55
58
|
- app/assets/stylesheets/wilday_ui/components/button/features/variants.css
|
56
59
|
- app/assets/stylesheets/wilday_ui/components/button/index.css
|
60
|
+
- app/assets/stylesheets/wilday_ui/tokens/colors.css
|
57
61
|
- app/controllers/wilday_ui/application_controller.rb
|
58
62
|
- app/helpers/wilday_ui/application_helper.rb
|
59
63
|
- app/helpers/wilday_ui/components/button_helper.rb
|
60
64
|
- app/helpers/wilday_ui/javascript_helper.rb
|
61
65
|
- app/javascript/wilday_ui/components/button.js
|
62
66
|
- app/javascript/wilday_ui/controllers/button_controller.js
|
67
|
+
- app/javascript/wilday_ui/controllers/clipboard_controller.js
|
68
|
+
- app/javascript/wilday_ui/controllers/confirmation_controller.js
|
63
69
|
- app/javascript/wilday_ui/controllers/dropdown_controller.js
|
64
70
|
- app/javascript/wilday_ui/controllers/index.js
|
71
|
+
- app/javascript/wilday_ui/controllers/tooltip_controller.js
|
65
72
|
- app/javascript/wilday_ui/index.js
|
66
73
|
- app/jobs/wilday_ui/application_job.rb
|
67
74
|
- app/mailers/wilday_ui/application_mailer.rb
|