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
@@ -11,6 +11,24 @@ module WildayUi
|
|
11
11
|
wrapper_required: false,
|
12
12
|
stimulus_controller: "button",
|
13
13
|
default_stimulus_action: "click->button#toggleLoading"
|
14
|
+
},
|
15
|
+
copy_to_clipboard: {
|
16
|
+
wrapper_required: true,
|
17
|
+
stimulus_controller: "clipboard button",
|
18
|
+
default_stimulus_action: "click->clipboard#copy click->button#toggleLoading"
|
19
|
+
},
|
20
|
+
confirm: {
|
21
|
+
wrapper_required: true,
|
22
|
+
stimulus_controller: "confirmation",
|
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
|
+
}
|
14
32
|
}
|
15
33
|
# Add more features here as needed
|
16
34
|
# tooltip: {
|
@@ -42,6 +60,9 @@ module WildayUi
|
|
42
60
|
dropdown_items: nil,
|
43
61
|
dropdown_icon: nil,
|
44
62
|
theme: nil,
|
63
|
+
copy_to_clipboard: nil,
|
64
|
+
confirm: nil,
|
65
|
+
tooltip: nil,
|
45
66
|
**options
|
46
67
|
)
|
47
68
|
content_for(:head) { stylesheet_link_tag "wilday_ui/components/button/index", media: "all" }
|
@@ -68,7 +89,7 @@ module WildayUi
|
|
68
89
|
gradient_class = get_gradient_class(gradient)
|
69
90
|
|
70
91
|
# Setup features that require Stimulus controllers
|
71
|
-
active_features = determine_active_features(loading, dropdown, loading_text, use_default_controller)
|
92
|
+
active_features = determine_active_features(loading, dropdown, loading_text, copy_to_clipboard, confirm, tooltip, use_default_controller)
|
72
93
|
|
73
94
|
setup_features(active_features, options, use_default_controller, loading_text)
|
74
95
|
|
@@ -84,6 +105,33 @@ module WildayUi
|
|
84
105
|
)
|
85
106
|
end
|
86
107
|
|
108
|
+
if copy_to_clipboard
|
109
|
+
setup_clipboard_options(
|
110
|
+
options,
|
111
|
+
additional_classes,
|
112
|
+
copy_to_clipboard,
|
113
|
+
wrapper_data
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
if confirm
|
118
|
+
setup_confirmation_options(
|
119
|
+
options,
|
120
|
+
additional_classes,
|
121
|
+
confirm,
|
122
|
+
wrapper_data
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
if tooltip
|
127
|
+
setup_tooltip_options(
|
128
|
+
options,
|
129
|
+
additional_classes,
|
130
|
+
tooltip,
|
131
|
+
wrapper_data
|
132
|
+
)
|
133
|
+
end
|
134
|
+
|
87
135
|
# Setup wrapper options if any feature requires it
|
88
136
|
wrapper_options = setup_wrapper_options(
|
89
137
|
active_features,
|
@@ -267,10 +315,13 @@ module WildayUi
|
|
267
315
|
styles.map { |k, v| "#{k}: #{v}" }.join(";")
|
268
316
|
end
|
269
317
|
|
270
|
-
def determine_active_features(loading, dropdown, loading_text = 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)
|
271
319
|
features = {}
|
272
320
|
features[:loading] = true if (loading || loading_text.present?) && use_default_controller
|
273
321
|
features[:dropdown] = true if dropdown && use_default_controller
|
322
|
+
features[:copy_to_clipboard] = true if copy_to_clipboard.present? && use_default_controller
|
323
|
+
features[:confirm] = true if confirm.present? && use_default_controller
|
324
|
+
features[:tooltip] = true if tooltip.present? && use_default_controller
|
274
325
|
features
|
275
326
|
end
|
276
327
|
|
@@ -389,6 +440,206 @@ module WildayUi
|
|
389
440
|
"#{base}#{index}"
|
390
441
|
end
|
391
442
|
|
443
|
+
def setup_clipboard_options(options, additional_classes, copy_to_clipboard, wrapper_data)
|
444
|
+
return unless copy_to_clipboard.present?
|
445
|
+
|
446
|
+
clipboard_config = normalize_clipboard_options(copy_to_clipboard)
|
447
|
+
|
448
|
+
wrapper_data.merge!(
|
449
|
+
controller: "clipboard button",
|
450
|
+
clipboard_text_value: clipboard_config[:text],
|
451
|
+
clipboard_feedback_text_value: clipboard_config[:feedback_text],
|
452
|
+
clipboard_feedback_position_value: clipboard_config[:position],
|
453
|
+
clipboard_feedback_duration_value: clipboard_config[:duration]
|
454
|
+
)
|
455
|
+
|
456
|
+
options[:data][:clipboard_target] = "button"
|
457
|
+
options[:data][:button_target] = "button"
|
458
|
+
end
|
459
|
+
|
460
|
+
def normalize_clipboard_options(options)
|
461
|
+
if options.is_a?(Hash)
|
462
|
+
{
|
463
|
+
text: options[:text],
|
464
|
+
feedback_text: options[:feedback_text] || "Copied!",
|
465
|
+
position: options[:position] || "top",
|
466
|
+
duration: options[:duration] || 2000
|
467
|
+
}
|
468
|
+
else
|
469
|
+
{
|
470
|
+
text: options.to_s,
|
471
|
+
feedback_text: "Copied!",
|
472
|
+
position: "top",
|
473
|
+
duration: 2000
|
474
|
+
}
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
def setup_confirmation_options(options, additional_classes, confirm, wrapper_data)
|
479
|
+
return unless confirm.present?
|
480
|
+
|
481
|
+
confirm_config = normalize_confirmation_options(confirm)
|
482
|
+
|
483
|
+
# Use the same theme processing as regular buttons
|
484
|
+
confirm_theme_styles = process_theme(:solid, { name: confirm_config[:variant] })
|
485
|
+
cancel_theme_styles = process_theme(:subtle, { name: :secondary })
|
486
|
+
|
487
|
+
wrapper_data.merge!(
|
488
|
+
controller: "confirmation",
|
489
|
+
confirmation_title_value: confirm_config[:title],
|
490
|
+
confirmation_message_value: confirm_config[:message],
|
491
|
+
confirmation_icon_color_value: confirm_config[:variant],
|
492
|
+
confirmation_confirm_text_value: confirm_config[:confirm_text],
|
493
|
+
confirmation_cancel_text_value: confirm_config[:cancel_text],
|
494
|
+
confirmation_confirm_styles_value: confirm_theme_styles,
|
495
|
+
confirmation_cancel_styles_value: cancel_theme_styles
|
496
|
+
)
|
497
|
+
|
498
|
+
# Only add loading state if enabled
|
499
|
+
if confirm_config[:loading]
|
500
|
+
wrapper_data.merge!(
|
501
|
+
confirmation_loading_value: "true",
|
502
|
+
confirmation_loading_text_value: confirm_config[:loading_text]
|
503
|
+
)
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
def normalize_confirmation_options(options)
|
508
|
+
if options.is_a?(String)
|
509
|
+
{
|
510
|
+
title: "Confirm Action",
|
511
|
+
message: options,
|
512
|
+
variant: :info,
|
513
|
+
confirm_text: "Confirm",
|
514
|
+
cancel_text: "Cancel"
|
515
|
+
}
|
516
|
+
else
|
517
|
+
{
|
518
|
+
title: options[:title] || "Confirm Action",
|
519
|
+
message: options[:message],
|
520
|
+
variant: options[:variant] || :info,
|
521
|
+
confirm_text: options[:confirm_text] || "Confirm",
|
522
|
+
cancel_text: options[:cancel_text] || "Cancel",
|
523
|
+
loading: options[:loading] || false,
|
524
|
+
loading_text: options[:loading_text] || "Processing..."
|
525
|
+
}
|
526
|
+
end
|
527
|
+
end
|
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
|
+
|
392
643
|
def render_button(content, variant_class, size_class, radius_class, gradient_class, icon, icon_position, icon_only,
|
393
644
|
loading, loading_text, additional_classes, disabled, options, href, underline,
|
394
645
|
dropdown, dropdown_items, dropdown_icon, wrapper_options)
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["button", "feedback"];
|
5
|
+
static values = {
|
6
|
+
text: String,
|
7
|
+
feedbackText: { type: String, default: "Copied!" },
|
8
|
+
feedbackPosition: { type: String, default: "top" },
|
9
|
+
feedbackDuration: { type: Number, default: 2000 },
|
10
|
+
};
|
11
|
+
|
12
|
+
connect() {
|
13
|
+
// Optional: Initialize any necessary setup
|
14
|
+
}
|
15
|
+
|
16
|
+
async copy(event) {
|
17
|
+
event.preventDefault();
|
18
|
+
|
19
|
+
try {
|
20
|
+
await navigator.clipboard.writeText(this.textValue);
|
21
|
+
this.showFeedback();
|
22
|
+
} catch (err) {
|
23
|
+
console.error("Failed to copy text:", err);
|
24
|
+
// Fallback for older browsers
|
25
|
+
this.fallbackCopy();
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
fallbackCopy() {
|
30
|
+
const textArea = document.createElement("textarea");
|
31
|
+
textArea.value = this.textValue;
|
32
|
+
textArea.style.position = "fixed";
|
33
|
+
textArea.style.left = "-9999px";
|
34
|
+
document.body.appendChild(textArea);
|
35
|
+
textArea.select();
|
36
|
+
|
37
|
+
try {
|
38
|
+
document.execCommand("copy");
|
39
|
+
this.showFeedback();
|
40
|
+
} catch (err) {
|
41
|
+
console.error("Fallback: Oops, unable to copy", err);
|
42
|
+
}
|
43
|
+
|
44
|
+
document.body.removeChild(textArea);
|
45
|
+
}
|
46
|
+
|
47
|
+
showFeedback() {
|
48
|
+
const feedback = this.hasFeedbackTarget
|
49
|
+
? this.feedbackTarget
|
50
|
+
: this.createFeedbackElement();
|
51
|
+
feedback.textContent = this.feedbackTextValue;
|
52
|
+
|
53
|
+
// Remove any existing position classes
|
54
|
+
feedback.className = "w-button-feedback";
|
55
|
+
|
56
|
+
// Add position-specific classes
|
57
|
+
const positionClasses = this.feedbackPositionValue.split("-");
|
58
|
+
positionClasses.forEach((pos) => {
|
59
|
+
feedback.classList.add(`w-button-feedback-${pos}`);
|
60
|
+
});
|
61
|
+
|
62
|
+
feedback.classList.add("w-button-feedback-show");
|
63
|
+
|
64
|
+
setTimeout(() => {
|
65
|
+
feedback.classList.remove("w-button-feedback-show");
|
66
|
+
}, this.feedbackDurationValue);
|
67
|
+
}
|
68
|
+
|
69
|
+
createFeedbackElement() {
|
70
|
+
const feedback = document.createElement("div");
|
71
|
+
feedback.classList.add("w-button-feedback");
|
72
|
+
feedback.setAttribute("data-clipboard-target", "feedback");
|
73
|
+
this.element.appendChild(feedback);
|
74
|
+
return feedback;
|
75
|
+
}
|
76
|
+
}
|
@@ -0,0 +1,216 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["dialog", "confirmButton", "cancelButton"];
|
5
|
+
static values = {
|
6
|
+
title: String,
|
7
|
+
message: String,
|
8
|
+
iconColor: String,
|
9
|
+
confirmText: String,
|
10
|
+
cancelText: String,
|
11
|
+
confirmStyles: String,
|
12
|
+
cancelStyles: String,
|
13
|
+
};
|
14
|
+
|
15
|
+
// Store the original event to be used later
|
16
|
+
originalEvent = null;
|
17
|
+
|
18
|
+
connect() {
|
19
|
+
// Create and append dialog if it doesn't exist
|
20
|
+
if (!this.hasDialogTarget) {
|
21
|
+
this.element.insertAdjacentHTML("beforeend", this.dialogHTML);
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
disconnect() {
|
26
|
+
this.originalEvent = null;
|
27
|
+
this.isConfirmed = false;
|
28
|
+
}
|
29
|
+
|
30
|
+
showDialog(event) {
|
31
|
+
// Don't show dialog if already confirmed
|
32
|
+
if (this.isConfirmed) {
|
33
|
+
this.isConfirmed = false;
|
34
|
+
return;
|
35
|
+
}
|
36
|
+
|
37
|
+
// console.log("Show dialog triggered", event);
|
38
|
+
event.preventDefault();
|
39
|
+
// Store the original event and element
|
40
|
+
this.originalEvent = {
|
41
|
+
type: event.type,
|
42
|
+
element: event.currentTarget,
|
43
|
+
ctrlKey: event.ctrlKey,
|
44
|
+
metaKey: event.metaKey,
|
45
|
+
};
|
46
|
+
// console.log("Original event stored:", this.originalEvent);
|
47
|
+
this.dialogTarget.showModal();
|
48
|
+
this.focusConfirmButton();
|
49
|
+
}
|
50
|
+
|
51
|
+
confirm(event) {
|
52
|
+
// console.log("Confirm clicked");
|
53
|
+
event.preventDefault();
|
54
|
+
this.dialogTarget.close();
|
55
|
+
|
56
|
+
if (this.originalEvent?.element) {
|
57
|
+
const element = this.originalEvent.element;
|
58
|
+
// console.log("Processing element:", element);
|
59
|
+
|
60
|
+
// Let Turbo handle its own elements
|
61
|
+
if (
|
62
|
+
this.hasTurbo &&
|
63
|
+
!element.hasAttribute("data-turbo") &&
|
64
|
+
!element.hasAttribute("data-turbo-method")
|
65
|
+
) {
|
66
|
+
this.resumeOriginalEvent();
|
67
|
+
return;
|
68
|
+
}
|
69
|
+
|
70
|
+
// Dispatch standard DOM custom event
|
71
|
+
const confirmEvent = new CustomEvent("confirm", {
|
72
|
+
bubbles: true,
|
73
|
+
cancelable: true,
|
74
|
+
detail: {
|
75
|
+
element: element,
|
76
|
+
originalEvent: this.originalEvent,
|
77
|
+
},
|
78
|
+
});
|
79
|
+
|
80
|
+
const wasHandled = !element.dispatchEvent(confirmEvent);
|
81
|
+
if (wasHandled) return;
|
82
|
+
|
83
|
+
// If not handled by custom event, resume original event
|
84
|
+
this.resumeOriginalEvent();
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
resumeOriginalEvent() {
|
89
|
+
if (!this.originalEvent) return;
|
90
|
+
|
91
|
+
const element = this.originalEvent.element;
|
92
|
+
// console.log("Resuming original event for:", element);
|
93
|
+
|
94
|
+
// Set flag before triggering the event
|
95
|
+
this.isConfirmed = true;
|
96
|
+
|
97
|
+
// Handle form submissions
|
98
|
+
if (element.closest("form")) {
|
99
|
+
const form = element.closest("form");
|
100
|
+
// console.log("Submitting form:", form);
|
101
|
+
const submitEvent = new Event("submit", {
|
102
|
+
bubbles: true,
|
103
|
+
cancelable: true,
|
104
|
+
});
|
105
|
+
form.dispatchEvent(submitEvent); // This will trigger the event listener
|
106
|
+
this.originalEvent = null;
|
107
|
+
return;
|
108
|
+
}
|
109
|
+
|
110
|
+
// Handle links
|
111
|
+
if (element.tagName === "A" || element.hasAttribute("href")) {
|
112
|
+
// console.log("Processing link click");
|
113
|
+
const click = new MouseEvent("click", {
|
114
|
+
bubbles: true,
|
115
|
+
cancelable: true,
|
116
|
+
ctrlKey: this.originalEvent.ctrlKey,
|
117
|
+
metaKey: this.originalEvent.metaKey,
|
118
|
+
});
|
119
|
+
element.dispatchEvent(click);
|
120
|
+
this.originalEvent = null;
|
121
|
+
return;
|
122
|
+
}
|
123
|
+
|
124
|
+
// Handle regular button click
|
125
|
+
if (
|
126
|
+
!element.closest("form") &&
|
127
|
+
!(element.tagName === "A" || element.hasAttribute("href"))
|
128
|
+
) {
|
129
|
+
// console.log("Processing button click");
|
130
|
+
element.click();
|
131
|
+
this.originalEvent = null;
|
132
|
+
return;
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
cancel(event) {
|
137
|
+
event.preventDefault();
|
138
|
+
this.closeDialog();
|
139
|
+
}
|
140
|
+
|
141
|
+
closeDialog() {
|
142
|
+
this.dialogTarget.close();
|
143
|
+
this.originalEvent = null;
|
144
|
+
}
|
145
|
+
|
146
|
+
handleKeydown(event) {
|
147
|
+
if (event.key === "Escape") {
|
148
|
+
this.cancel(event);
|
149
|
+
}
|
150
|
+
}
|
151
|
+
|
152
|
+
handleClickOutside(event) {
|
153
|
+
if (event.target === this.dialogTarget) {
|
154
|
+
this.cancel(event);
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
focusConfirmButton() {
|
159
|
+
this.confirmButtonTarget.focus();
|
160
|
+
}
|
161
|
+
|
162
|
+
get hasTurbo() {
|
163
|
+
return typeof Turbo !== "undefined";
|
164
|
+
}
|
165
|
+
|
166
|
+
get dialogHTML() {
|
167
|
+
return `
|
168
|
+
<dialog class="w-button-confirmation-dialog"
|
169
|
+
data-confirmation-target="dialog"
|
170
|
+
data-action="click->confirmation#handleClickOutside keydown->confirmation#handleKeydown">
|
171
|
+
<div class="w-button-confirmation-dialog-content">
|
172
|
+
<div class="w-button-confirmation-dialog-icon ${this.iconColorValue}">
|
173
|
+
${this.iconHTML}
|
174
|
+
</div>
|
175
|
+
|
176
|
+
<h3 class="w-button-confirmation-dialog-title">
|
177
|
+
${this.titleValue}
|
178
|
+
</h3>
|
179
|
+
|
180
|
+
<div class="w-button-confirmation-dialog-message">
|
181
|
+
${this.messageValue}
|
182
|
+
</div>
|
183
|
+
|
184
|
+
<div class="w-button-confirmation-dialog-actions">
|
185
|
+
<button data-confirmation-target="cancelButton"
|
186
|
+
data-action="click->confirmation#cancel"
|
187
|
+
class="w-button w-button-subtle w-button-medium w-button-rounded"
|
188
|
+
style="${this.cancelStylesValue}">
|
189
|
+
${this.cancelTextValue}
|
190
|
+
</button>
|
191
|
+
|
192
|
+
<button data-confirmation-target="confirmButton"
|
193
|
+
data-action="click->confirmation#confirm"
|
194
|
+
class="w-button w-button-solid w-button-medium w-button-rounded"
|
195
|
+
style="${this.confirmStylesValue}">
|
196
|
+
${this.confirmTextValue}
|
197
|
+
</button>
|
198
|
+
</div>
|
199
|
+
</div>
|
200
|
+
</dialog>
|
201
|
+
`;
|
202
|
+
}
|
203
|
+
|
204
|
+
get iconHTML() {
|
205
|
+
const icons = {
|
206
|
+
info: '<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>',
|
207
|
+
success:
|
208
|
+
'<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>',
|
209
|
+
warning:
|
210
|
+
'<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>',
|
211
|
+
danger:
|
212
|
+
'<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>',
|
213
|
+
};
|
214
|
+
return icons[this.iconColorValue] || icons.info;
|
215
|
+
}
|
216
|
+
}
|
@@ -1,7 +1,9 @@
|
|
1
1
|
import { Application } from "@hotwired/stimulus";
|
2
2
|
import ButtonController from "./button_controller";
|
3
3
|
import DropdownController from "./dropdown_controller";
|
4
|
-
|
4
|
+
import ClipboardController from "./clipboard_controller";
|
5
|
+
import ConfirmationController from "./confirmation_controller";
|
6
|
+
import TooltipController from "./tooltip_controller";
|
5
7
|
// Initialize Stimulus
|
6
8
|
const application = Application.start();
|
7
9
|
window.Stimulus = application;
|
@@ -9,6 +11,9 @@ window.Stimulus = application;
|
|
9
11
|
// Register the button controller
|
10
12
|
application.register("button", ButtonController);
|
11
13
|
application.register("dropdown", DropdownController);
|
14
|
+
application.register("clipboard", ClipboardController);
|
15
|
+
application.register("confirmation", ConfirmationController);
|
16
|
+
application.register("tooltip", TooltipController);
|
12
17
|
// Debug check to ensure Stimulus is loaded
|
13
18
|
// if (window.Stimulus) {
|
14
19
|
// console.log("✅ Stimulus is loaded and initialized.");
|