better_ui 0.4.0 → 0.5.1
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/README.md +141 -189
- data/app/assets/stylesheets/better_ui/_base.scss +9 -0
- data/app/assets/stylesheets/better_ui/_components.scss +2 -0
- data/app/assets/stylesheets/better_ui/_utilities.scss +14 -0
- data/app/assets/stylesheets/better_ui/application.css +3 -1
- data/app/assets/stylesheets/better_ui/components/_avatar.scss +200 -0
- data/app/assets/stylesheets/better_ui/components/_badge.scss +154 -0
- data/app/assets/stylesheets/better_ui/components/_breadcrumb.scss +106 -0
- data/app/assets/stylesheets/better_ui/components/_button.scss +105 -0
- data/app/assets/stylesheets/better_ui/components/_card.scss +60 -0
- data/app/assets/stylesheets/better_ui/components/_heading.scss +81 -0
- data/app/assets/stylesheets/better_ui/components/_icon.scss +134 -0
- data/app/assets/stylesheets/better_ui/components/_index.scss +17 -0
- data/app/assets/stylesheets/better_ui/components/_link.scss +100 -0
- data/app/assets/stylesheets/better_ui/components/_panel.scss +104 -0
- data/app/assets/stylesheets/better_ui/components/_spinner.scss +129 -0
- data/app/assets/stylesheets/better_ui/components/_table.scss +156 -0
- data/app/assets/stylesheets/better_ui/components/_variables.scss +1 -0
- data/app/assets/stylesheets/better_ui.scss +4 -0
- data/app/components/better_ui/application/alert_component.html.erb +1 -1
- data/app/components/better_ui/application/alert_component.rb +95 -89
- data/app/components/better_ui/application/card_component.html.erb +24 -0
- data/app/components/better_ui/application/card_component.rb +53 -0
- data/app/components/better_ui/application/card_container_component.html.erb +8 -0
- data/app/components/better_ui/application/card_container_component.rb +14 -0
- data/app/components/better_ui/application/toast_component.rb +92 -57
- data/app/components/better_ui/general/avatar_component.html.erb +19 -0
- data/app/components/better_ui/general/avatar_component.rb +171 -0
- data/app/components/better_ui/general/badge_component.html.erb +19 -0
- data/app/components/better_ui/general/badge_component.rb +135 -0
- data/app/components/better_ui/general/breadcrumb_component.html.erb +7 -31
- data/app/components/better_ui/general/breadcrumb_component.rb +64 -66
- data/app/components/better_ui/general/button_component.html.erb +6 -6
- data/app/components/better_ui/general/button_component.rb +62 -95
- data/app/components/better_ui/general/heading_component.html.erb +1 -25
- data/app/components/better_ui/general/heading_component.rb +20 -113
- data/app/components/better_ui/general/icon_component.rb +37 -61
- data/app/components/better_ui/general/link_component.html.erb +17 -0
- data/app/components/better_ui/general/link_component.rb +132 -0
- data/app/components/better_ui/general/panel_component.rb +51 -56
- data/app/components/better_ui/general/spinner_component.html.erb +15 -0
- data/app/components/better_ui/general/spinner_component.rb +79 -0
- data/app/components/better_ui/general/table_component.html.erb +56 -20
- data/app/components/better_ui/general/table_component.rb +89 -87
- data/app/helpers/better_ui/general/components/avatar_helper.rb +17 -0
- data/app/helpers/better_ui/general/components/badge_helper.rb +17 -0
- data/app/helpers/better_ui/general/components/breadcrumb_helper.rb +17 -0
- data/app/helpers/better_ui/general/components/button_helper.rb +17 -0
- data/app/helpers/better_ui/general/components/heading_helper.rb +17 -0
- data/app/helpers/better_ui/general/components/icon_helper.rb +17 -0
- data/app/helpers/better_ui/general/components/link_helper.rb +17 -0
- data/app/helpers/better_ui/general/components/panel_helper.rb +16 -0
- data/app/helpers/better_ui/general/components/spinner_helper.rb +17 -0
- data/app/helpers/better_ui/general/components/table_helper.rb +17 -0
- data/app/helpers/better_ui/general_helper.rb +15 -0
- data/app/helpers/better_ui_helper.rb +12 -0
- data/app/views/components/better_ui/general/table/_custom_body_row.html.erb +17 -0
- data/app/views/components/better_ui/general/table/_custom_footer_rows.html.erb +17 -0
- data/app/views/components/better_ui/general/table/_custom_header_rows.html.erb +12 -0
- data/config/routes.rb +2 -13
- data/lib/better_ui/engine.rb +66 -16
- data/lib/better_ui/version.rb +1 -1
- data/lib/better_ui.rb +12 -0
- data/lib/generators/better_ui/install_generator.rb +103 -0
- data/lib/generators/better_ui/stylesheet_generator.rb +159 -0
- data/lib/generators/better_ui/templates/README +125 -0
- data/lib/generators/better_ui/templates/components/_avatar.scss +200 -0
- data/lib/generators/better_ui/templates/components/_badge.scss +154 -0
- data/lib/generators/better_ui/templates/components/_breadcrumb.scss +106 -0
- data/lib/generators/better_ui/templates/components/_button.scss +109 -0
- data/lib/generators/better_ui/templates/components/_card.scss +60 -0
- data/lib/generators/better_ui/templates/components/_heading.scss +81 -0
- data/lib/generators/better_ui/templates/components/_icon.scss +134 -0
- data/lib/generators/better_ui/templates/components/_index.scss +17 -0
- data/lib/generators/better_ui/templates/components/_link.scss +100 -0
- data/lib/generators/better_ui/templates/components/_panel.scss +104 -0
- data/lib/generators/better_ui/templates/components/_spinner.scss +129 -0
- data/lib/generators/better_ui/templates/components/_table.scss +156 -0
- data/lib/generators/better_ui/templates/components/_variables.scss +0 -0
- data/lib/generators/better_ui/templates/components_stylesheet.scss +35 -0
- data/lib/generators/better_ui/templates/index.scss +18 -0
- data/lib/generators/better_ui/templates/initializer.rb +41 -0
- metadata +120 -49
- data/app/assets/javascripts/better_ui/controllers/navbar_controller.js +0 -138
- data/app/assets/javascripts/better_ui/controllers/sidebar_controller.js +0 -211
- data/app/assets/javascripts/better_ui/controllers/toast_controller.js +0 -161
- data/app/assets/javascripts/better_ui/index.js +0 -159
- data/app/assets/javascripts/better_ui/toast_manager.js +0 -77
- data/app/components/better_ui/theme_helper.rb +0 -169
- data/app/controllers/better_ui/docs_controller.rb +0 -34
- data/app/helpers/better_ui_application_helper.rb +0 -99
@@ -1,161 +0,0 @@
|
|
1
|
-
// Toast controller per gestire il comportamento delle notifiche toast
|
2
|
-
import { Controller } from "@hotwired/stimulus"
|
3
|
-
import ToastManager from "../toast_manager"
|
4
|
-
|
5
|
-
export default class extends Controller {
|
6
|
-
static targets = ["progressBar"]
|
7
|
-
static values = {
|
8
|
-
duration: { type: Number, default: 5000 },
|
9
|
-
autoHide: { type: Boolean, default: true },
|
10
|
-
position: { type: String, default: "top-right" }
|
11
|
-
}
|
12
|
-
|
13
|
-
connect() {
|
14
|
-
// Estrai la posizione dalle classi del toast o utilizza il valore predefinito
|
15
|
-
this.position = this.getPositionFromClasses() || this.positionValue;
|
16
|
-
|
17
|
-
// Rimuovi le classi di posizione originali per evitare conflitti
|
18
|
-
this.removePositionClasses();
|
19
|
-
|
20
|
-
// Registra il toast con il manager e ottieni la funzione di pulizia
|
21
|
-
this.cleanupFunction = ToastManager.registerToast(this.element, this.position);
|
22
|
-
|
23
|
-
// Applica l'animazione di entrata
|
24
|
-
this.element.classList.add("opacity-0", "translate-y-2");
|
25
|
-
|
26
|
-
// Permetti al DOM di essere aggiornato prima di mostrare
|
27
|
-
setTimeout(() => {
|
28
|
-
this.element.classList.remove("opacity-0", "translate-y-2");
|
29
|
-
this.element.classList.add("opacity-100", "translate-y-0");
|
30
|
-
}, 10);
|
31
|
-
|
32
|
-
// Se auto-hide è attivato, imposta il timer
|
33
|
-
if (this.autoHideValue) {
|
34
|
-
this.startProgressBar();
|
35
|
-
this.hideTimeout = setTimeout(() => {
|
36
|
-
this.hide();
|
37
|
-
}, this.durationValue);
|
38
|
-
}
|
39
|
-
|
40
|
-
// Aggiungi eventi per pausa/ripresa dell'auto-hide
|
41
|
-
this.element.addEventListener("mouseenter", this.mouseEnter.bind(this));
|
42
|
-
this.element.addEventListener("mouseleave", this.mouseLeave.bind(this));
|
43
|
-
}
|
44
|
-
|
45
|
-
// Estrae la posizione dalle classi del toast
|
46
|
-
getPositionFromClasses() {
|
47
|
-
const positionClasses = [
|
48
|
-
"top-right", "top-left", "bottom-right", "bottom-left", "top-center", "bottom-center"
|
49
|
-
];
|
50
|
-
|
51
|
-
for (const position of positionClasses) {
|
52
|
-
if (this.element.classList.contains(position)) {
|
53
|
-
return position;
|
54
|
-
}
|
55
|
-
}
|
56
|
-
|
57
|
-
return null;
|
58
|
-
}
|
59
|
-
|
60
|
-
// Rimuove le classi di posizione originali
|
61
|
-
removePositionClasses() {
|
62
|
-
const positionClasses = [
|
63
|
-
"top-4", "right-4", "bottom-4", "left-4", "left-1/2", "transform", "-translate-x-1/2"
|
64
|
-
];
|
65
|
-
|
66
|
-
positionClasses.forEach(cls => {
|
67
|
-
this.element.classList.remove(cls);
|
68
|
-
});
|
69
|
-
}
|
70
|
-
|
71
|
-
startProgressBar() {
|
72
|
-
if (this.hasProgressBarTarget) {
|
73
|
-
this.progressBarTarget.style.width = "100%";
|
74
|
-
|
75
|
-
// Imposta una transizione che dura quanto il timeout
|
76
|
-
this.progressBarTarget.style.transition = `width ${this.durationValue}ms linear`;
|
77
|
-
|
78
|
-
// Forza un reflow per assicurare che la transizione inizi
|
79
|
-
void this.progressBarTarget.offsetWidth;
|
80
|
-
|
81
|
-
// Inizia la transizione
|
82
|
-
this.progressBarTarget.style.width = "0%";
|
83
|
-
}
|
84
|
-
}
|
85
|
-
|
86
|
-
disconnect() {
|
87
|
-
if (this.hideTimeout) {
|
88
|
-
clearTimeout(this.hideTimeout);
|
89
|
-
}
|
90
|
-
|
91
|
-
// Rimuovi gli event listener
|
92
|
-
this.element.removeEventListener("mouseenter", this.mouseEnter.bind(this));
|
93
|
-
this.element.removeEventListener("mouseleave", this.mouseLeave.bind(this));
|
94
|
-
|
95
|
-
// Esegui la funzione di pulizia per rimuovere il toast dal manager
|
96
|
-
if (this.cleanupFunction) {
|
97
|
-
this.cleanupFunction();
|
98
|
-
}
|
99
|
-
}
|
100
|
-
|
101
|
-
hide(event) {
|
102
|
-
if (event) {
|
103
|
-
event.preventDefault();
|
104
|
-
}
|
105
|
-
|
106
|
-
// Applica l'animazione di uscita
|
107
|
-
this.element.classList.remove("opacity-100", "translate-y-0");
|
108
|
-
this.element.classList.add("opacity-0", "translate-y-2");
|
109
|
-
|
110
|
-
// Rimuovi l'elemento dopo l'animazione
|
111
|
-
setTimeout(() => {
|
112
|
-
this.element.remove();
|
113
|
-
}, 300);
|
114
|
-
}
|
115
|
-
|
116
|
-
// Metodo per pausa e ripresa dell'autoHide quando l'utente interagisce con il toast
|
117
|
-
pauseAutoHide() {
|
118
|
-
if (this.hideTimeout) {
|
119
|
-
clearTimeout(this.hideTimeout);
|
120
|
-
|
121
|
-
if (this.hasProgressBarTarget) {
|
122
|
-
// Salva la larghezza corrente per riprendere da lì
|
123
|
-
const computedStyle = window.getComputedStyle(this.progressBarTarget);
|
124
|
-
this.pausedWidth = computedStyle.width;
|
125
|
-
this.progressBarTarget.style.transition = "none";
|
126
|
-
this.progressBarTarget.style.width = this.pausedWidth;
|
127
|
-
}
|
128
|
-
}
|
129
|
-
}
|
130
|
-
|
131
|
-
resumeAutoHide() {
|
132
|
-
if (this.autoHideValue) {
|
133
|
-
// Calcola il tempo rimanente basato sulla larghezza della barra di progresso
|
134
|
-
let remainingTime = this.durationValue;
|
135
|
-
|
136
|
-
if (this.hasProgressBarTarget && this.pausedWidth) {
|
137
|
-
const percentage = parseFloat(this.pausedWidth) / parseFloat(window.getComputedStyle(this.progressBarTarget.parentElement).width);
|
138
|
-
remainingTime = this.durationValue * percentage;
|
139
|
-
}
|
140
|
-
|
141
|
-
this.hideTimeout = setTimeout(() => {
|
142
|
-
this.hide();
|
143
|
-
}, remainingTime);
|
144
|
-
|
145
|
-
if (this.hasProgressBarTarget && this.pausedWidth) {
|
146
|
-
this.progressBarTarget.style.transition = `width ${remainingTime}ms linear`;
|
147
|
-
void this.progressBarTarget.offsetWidth;
|
148
|
-
this.progressBarTarget.style.width = "0%";
|
149
|
-
}
|
150
|
-
}
|
151
|
-
}
|
152
|
-
|
153
|
-
// Gestori eventi per pausa/ripresa dell'autoHide
|
154
|
-
mouseEnter() {
|
155
|
-
this.pauseAutoHide();
|
156
|
-
}
|
157
|
-
|
158
|
-
mouseLeave() {
|
159
|
-
this.resumeAutoHide();
|
160
|
-
}
|
161
|
-
}
|
@@ -1,159 +0,0 @@
|
|
1
|
-
// Entry point per tutti i componenti JavaScript di Better UI
|
2
|
-
import { Application } from "@hotwired/stimulus"
|
3
|
-
import ToastController from "./controllers/toast_controller"
|
4
|
-
import NavbarController from "./controllers/navbar_controller"
|
5
|
-
import SidebarController from "./controllers/sidebar_controller"
|
6
|
-
|
7
|
-
// Configura Stimulus
|
8
|
-
const application = Application.start()
|
9
|
-
|
10
|
-
// Registra i controller
|
11
|
-
application.register("toast", ToastController)
|
12
|
-
application.register("navbar", NavbarController)
|
13
|
-
application.register("sidebar", SidebarController)
|
14
|
-
|
15
|
-
// Esporta i controller e altri componenti
|
16
|
-
export { ToastController, NavbarController, SidebarController }
|
17
|
-
export { default as ToastManager } from "./toast_manager"
|
18
|
-
|
19
|
-
// Funzione di utilità per creare e mostrare un toast programmaticamente
|
20
|
-
export function showToast(options = {}) {
|
21
|
-
const {
|
22
|
-
title = null,
|
23
|
-
message = "Notifica",
|
24
|
-
variant = "info",
|
25
|
-
position = "top-right",
|
26
|
-
duration = 5000,
|
27
|
-
dismissible = true,
|
28
|
-
autoHide = true,
|
29
|
-
icon = null
|
30
|
-
} = options;
|
31
|
-
|
32
|
-
// Crea l'elemento toast
|
33
|
-
const toast = document.createElement("div");
|
34
|
-
toast.setAttribute("role", "status");
|
35
|
-
toast.setAttribute("aria-live", "polite");
|
36
|
-
toast.classList.add(
|
37
|
-
"fixed", "z-50", "rounded-lg", "p-4", "border", "shadow-lg",
|
38
|
-
"transform", "transition-transform", "duration-300",
|
39
|
-
"min-w-[20rem]", "max-w-sm", "flex", "items-start",
|
40
|
-
position
|
41
|
-
);
|
42
|
-
|
43
|
-
// Aggiungi classi specifiche per la variante
|
44
|
-
const variantClasses = {
|
45
|
-
primary: ["bg-orange-50", "border-orange-300"],
|
46
|
-
info: ["bg-blue-50", "border-blue-300"],
|
47
|
-
success: ["bg-green-50", "border-green-300"],
|
48
|
-
warning: ["bg-yellow-50", "border-yellow-300"],
|
49
|
-
danger: ["bg-red-50", "border-red-300"],
|
50
|
-
dark: ["bg-gray-800", "border-gray-700"]
|
51
|
-
};
|
52
|
-
|
53
|
-
// Applica le classi della variante
|
54
|
-
const selectedVariant = variantClasses[variant] || variantClasses.info;
|
55
|
-
toast.classList.add(...selectedVariant);
|
56
|
-
|
57
|
-
// Aggiungi attributi per Stimulus
|
58
|
-
toast.setAttribute("data-controller", "toast");
|
59
|
-
toast.setAttribute("data-toast-duration-value", duration);
|
60
|
-
toast.setAttribute("data-toast-auto-hide-value", autoHide);
|
61
|
-
toast.setAttribute("data-toast-position-value", position);
|
62
|
-
|
63
|
-
// Costruisci il contenuto HTML
|
64
|
-
let html = "";
|
65
|
-
|
66
|
-
// Icona
|
67
|
-
const defaultIcons = {
|
68
|
-
primary: "bell",
|
69
|
-
info: "info-circle",
|
70
|
-
success: "check-circle",
|
71
|
-
warning: "exclamation-triangle",
|
72
|
-
danger: "exclamation-circle",
|
73
|
-
dark: "shield-exclamation"
|
74
|
-
};
|
75
|
-
|
76
|
-
const iconName = icon || defaultIcons[variant] || defaultIcons.info;
|
77
|
-
|
78
|
-
if (iconName) {
|
79
|
-
const iconColorClasses = {
|
80
|
-
primary: "text-orange-500",
|
81
|
-
info: "text-blue-500",
|
82
|
-
success: "text-green-500",
|
83
|
-
warning: "text-yellow-500",
|
84
|
-
danger: "text-red-500",
|
85
|
-
dark: "text-gray-400"
|
86
|
-
};
|
87
|
-
|
88
|
-
html += `
|
89
|
-
<div class="flex-shrink-0 mr-3 mt-0.5 ${iconColorClasses[variant] || iconColorClasses.info}">
|
90
|
-
<i class="fas fa-${iconName}"></i>
|
91
|
-
</div>
|
92
|
-
`;
|
93
|
-
}
|
94
|
-
|
95
|
-
// Contenuto
|
96
|
-
const titleColorClasses = {
|
97
|
-
primary: "text-orange-800",
|
98
|
-
info: "text-blue-800",
|
99
|
-
success: "text-green-800",
|
100
|
-
warning: "text-yellow-800",
|
101
|
-
danger: "text-red-800",
|
102
|
-
dark: "text-white"
|
103
|
-
};
|
104
|
-
|
105
|
-
const messageColorClasses = {
|
106
|
-
primary: "text-orange-700",
|
107
|
-
info: "text-blue-700",
|
108
|
-
success: "text-green-700",
|
109
|
-
warning: "text-yellow-700",
|
110
|
-
danger: "text-red-700",
|
111
|
-
dark: "text-gray-300"
|
112
|
-
};
|
113
|
-
|
114
|
-
html += '<div class="flex-1">';
|
115
|
-
|
116
|
-
if (title) {
|
117
|
-
html += `<div class="font-medium ${titleColorClasses[variant] || titleColorClasses.info}">${title}</div>`;
|
118
|
-
}
|
119
|
-
|
120
|
-
if (message) {
|
121
|
-
html += `<div class="mt-1 ${messageColorClasses[variant] || messageColorClasses.info}">${message}</div>`;
|
122
|
-
}
|
123
|
-
|
124
|
-
if (autoHide) {
|
125
|
-
html += `
|
126
|
-
<div class="w-full bg-gray-200 h-1 mt-2 rounded overflow-hidden">
|
127
|
-
<div class="bg-current h-1 transition-all" data-toast-target="progressBar"></div>
|
128
|
-
</div>
|
129
|
-
`;
|
130
|
-
}
|
131
|
-
|
132
|
-
html += '</div>';
|
133
|
-
|
134
|
-
// Pulsante di chiusura
|
135
|
-
if (dismissible) {
|
136
|
-
const closeButtonColorClasses = {
|
137
|
-
primary: "text-orange-500 hover:bg-orange-100",
|
138
|
-
info: "text-blue-500 hover:bg-blue-100",
|
139
|
-
success: "text-green-500 hover:bg-green-100",
|
140
|
-
warning: "text-yellow-500 hover:bg-yellow-100",
|
141
|
-
danger: "text-red-500 hover:bg-red-100",
|
142
|
-
dark: "text-gray-400 hover:bg-gray-700"
|
143
|
-
};
|
144
|
-
|
145
|
-
html += `
|
146
|
-
<button type="button" class="ml-auto -mr-1.5 -mt-1.5 inline-flex h-8 w-8 rounded-lg p-1.5 focus:ring-2 focus:ring-gray-400 ${closeButtonColorClasses[variant] || closeButtonColorClasses.info}" data-action="toast#hide" aria-label="Chiudi">
|
147
|
-
<i class="fas fa-xmark"></i>
|
148
|
-
</button>
|
149
|
-
`;
|
150
|
-
}
|
151
|
-
|
152
|
-
// Inserisci il contenuto
|
153
|
-
toast.innerHTML = html;
|
154
|
-
|
155
|
-
// Aggiungi il toast al documento
|
156
|
-
document.body.appendChild(toast);
|
157
|
-
|
158
|
-
return toast;
|
159
|
-
}
|
@@ -1,77 +0,0 @@
|
|
1
|
-
// Gestore per l'impilamento dei toast in diverse posizioni
|
2
|
-
class ToastManager {
|
3
|
-
constructor() {
|
4
|
-
// Mantiene traccia dei toast attivi per posizione
|
5
|
-
this.toasts = {
|
6
|
-
'top-right': [],
|
7
|
-
'top-left': [],
|
8
|
-
'bottom-right': [],
|
9
|
-
'bottom-left': [],
|
10
|
-
'top-center': [],
|
11
|
-
'bottom-center': []
|
12
|
-
}
|
13
|
-
|
14
|
-
// Offset di base per ogni posizione (in px)
|
15
|
-
this.baseOffset = 16; // equivalente a Tailwind top-4 (16px)
|
16
|
-
|
17
|
-
// Spaziatura tra i toast (in px)
|
18
|
-
this.spacing = 12;
|
19
|
-
}
|
20
|
-
|
21
|
-
// Registra un nuovo toast e calcola la sua posizione
|
22
|
-
registerToast(toast, position) {
|
23
|
-
if (!this.toasts[position]) {
|
24
|
-
console.warn(`Posizione toast non valida: ${position}. Utilizzo 'top-right' come predefinita.`);
|
25
|
-
position = 'top-right';
|
26
|
-
}
|
27
|
-
|
28
|
-
// Aggiungi il toast all'array della posizione specifica
|
29
|
-
this.toasts[position].push(toast);
|
30
|
-
|
31
|
-
// Ricalcola le posizioni per tutti i toast in questa posizione
|
32
|
-
this.updatePositions(position);
|
33
|
-
|
34
|
-
return () => this.removeToast(toast, position);
|
35
|
-
}
|
36
|
-
|
37
|
-
// Rimuove un toast e ricalcola le posizioni
|
38
|
-
removeToast(toast, position) {
|
39
|
-
const index = this.toasts[position].indexOf(toast);
|
40
|
-
if (index !== -1) {
|
41
|
-
this.toasts[position].splice(index, 1);
|
42
|
-
this.updatePositions(position);
|
43
|
-
}
|
44
|
-
}
|
45
|
-
|
46
|
-
// Aggiorna le posizioni di tutti i toast in una data posizione
|
47
|
-
updatePositions(position) {
|
48
|
-
const isTopPosition = position.startsWith('top-');
|
49
|
-
const toastArray = this.toasts[position];
|
50
|
-
|
51
|
-
let currentOffset = this.baseOffset;
|
52
|
-
|
53
|
-
toastArray.forEach((toast, index) => {
|
54
|
-
// Applica la posizione corretta
|
55
|
-
if (isTopPosition) {
|
56
|
-
toast.style.top = `${currentOffset}px`;
|
57
|
-
} else {
|
58
|
-
toast.style.bottom = `${currentOffset}px`;
|
59
|
-
}
|
60
|
-
|
61
|
-
// Aggiorna l'offset per il prossimo toast
|
62
|
-
const height = toast.offsetHeight;
|
63
|
-
currentOffset += height + this.spacing;
|
64
|
-
});
|
65
|
-
}
|
66
|
-
|
67
|
-
// Metodo statico per accedere all'istanza singleton
|
68
|
-
static getInstance() {
|
69
|
-
if (!ToastManager.instance) {
|
70
|
-
ToastManager.instance = new ToastManager();
|
71
|
-
}
|
72
|
-
return ToastManager.instance;
|
73
|
-
}
|
74
|
-
}
|
75
|
-
|
76
|
-
// Esporta il gestore come singleton
|
77
|
-
export default ToastManager.getInstance();
|
@@ -1,169 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module BetterUi
|
4
|
-
module ThemeHelper
|
5
|
-
# Colori principali del tema
|
6
|
-
THEME_COLORS = {
|
7
|
-
# Neutrali
|
8
|
-
white: 'bg-white',
|
9
|
-
black: 'bg-black',
|
10
|
-
|
11
|
-
# Colori del tema
|
12
|
-
primary: {
|
13
|
-
light: 'bg-orange-100',
|
14
|
-
default: 'bg-orange-500',
|
15
|
-
dark: 'bg-orange-600',
|
16
|
-
text: 'text-white'
|
17
|
-
},
|
18
|
-
secondary: {
|
19
|
-
light: 'bg-gray-100',
|
20
|
-
default: 'bg-gray-200',
|
21
|
-
dark: 'bg-gray-300',
|
22
|
-
text: 'text-gray-800'
|
23
|
-
},
|
24
|
-
success: {
|
25
|
-
light: 'bg-green-100',
|
26
|
-
default: 'bg-green-500',
|
27
|
-
dark: 'bg-green-600',
|
28
|
-
text: 'text-white'
|
29
|
-
},
|
30
|
-
warning: {
|
31
|
-
light: 'bg-amber-100',
|
32
|
-
default: 'bg-amber-500',
|
33
|
-
dark: 'bg-amber-600',
|
34
|
-
text: 'text-white'
|
35
|
-
},
|
36
|
-
danger: {
|
37
|
-
light: 'bg-red-100',
|
38
|
-
default: 'bg-red-500',
|
39
|
-
dark: 'bg-red-600',
|
40
|
-
text: 'text-white'
|
41
|
-
},
|
42
|
-
info: {
|
43
|
-
light: 'bg-blue-100',
|
44
|
-
default: 'bg-blue-500',
|
45
|
-
dark: 'bg-blue-600',
|
46
|
-
text: 'text-white'
|
47
|
-
}
|
48
|
-
}
|
49
|
-
|
50
|
-
# Bordi del tema
|
51
|
-
THEME_BORDERS = {
|
52
|
-
primary: 'border-orange-200',
|
53
|
-
secondary: 'border-gray-200',
|
54
|
-
success: 'border-green-200',
|
55
|
-
warning: 'border-amber-200',
|
56
|
-
danger: 'border-red-200',
|
57
|
-
info: 'border-blue-200'
|
58
|
-
}
|
59
|
-
|
60
|
-
# Effetti hover
|
61
|
-
THEME_HOVER = {
|
62
|
-
primary: 'hover:bg-orange-600',
|
63
|
-
secondary: 'hover:bg-gray-300',
|
64
|
-
success: 'hover:bg-green-600',
|
65
|
-
warning: 'hover:bg-amber-600',
|
66
|
-
danger: 'hover:bg-red-600',
|
67
|
-
info: 'hover:bg-blue-600'
|
68
|
-
}
|
69
|
-
|
70
|
-
# Stili base comuni
|
71
|
-
COMMON_STYLES = {
|
72
|
-
rounded: 'rounded-md',
|
73
|
-
shadow: 'shadow-sm',
|
74
|
-
transitions: 'transition-all duration-200'
|
75
|
-
}
|
76
|
-
|
77
|
-
# Stili per elementi di layout
|
78
|
-
LAYOUT_STYLES = {
|
79
|
-
panel: {
|
80
|
-
base: 'rounded-lg border shadow-sm',
|
81
|
-
header: 'px-4 py-3 border-b',
|
82
|
-
body: 'p-4',
|
83
|
-
footer: 'px-4 py-3 border-t'
|
84
|
-
},
|
85
|
-
table: {
|
86
|
-
container: 'overflow-hidden rounded-md border shadow-sm',
|
87
|
-
header: 'bg-gray-50 text-left text-xs uppercase tracking-wider text-gray-500 font-medium',
|
88
|
-
row: {
|
89
|
-
base: '',
|
90
|
-
hover: 'hover:bg-gray-50',
|
91
|
-
striped: 'even:bg-white odd:bg-gray-50'
|
92
|
-
},
|
93
|
-
cell: 'px-4 py-3 text-sm text-gray-700'
|
94
|
-
}
|
95
|
-
}
|
96
|
-
|
97
|
-
# Genera classi per un componente basato su variante
|
98
|
-
def self.generate_component_classes(component_type, variant, options = {})
|
99
|
-
base_classes = []
|
100
|
-
|
101
|
-
case component_type
|
102
|
-
when :button
|
103
|
-
base_classes << 'inline-flex items-center justify-center'
|
104
|
-
base_classes << 'font-medium'
|
105
|
-
base_classes << COMMON_STYLES[:rounded]
|
106
|
-
base_classes << COMMON_STYLES[:transitions]
|
107
|
-
|
108
|
-
if options[:outline]
|
109
|
-
# Versione outline
|
110
|
-
base_classes << 'border-2'
|
111
|
-
base_classes << "border-#{variant}-500"
|
112
|
-
base_classes << "text-#{variant}-500"
|
113
|
-
base_classes << "hover:bg-#{variant}-50"
|
114
|
-
else
|
115
|
-
# Versione piena
|
116
|
-
base_classes << THEME_COLORS.dig(variant.to_sym, :default)&.sub('bg-', '') || "bg-#{variant}-500"
|
117
|
-
base_classes << THEME_COLORS.dig(variant.to_sym, :text) || 'text-white'
|
118
|
-
base_classes << THEME_HOVER[variant.to_sym] || "hover:bg-#{variant}-600"
|
119
|
-
end
|
120
|
-
|
121
|
-
# Dimensione
|
122
|
-
case options[:size]
|
123
|
-
when :sm
|
124
|
-
base_classes << 'px-2.5 py-1.5 text-xs'
|
125
|
-
when :lg
|
126
|
-
base_classes << 'px-6 py-3 text-base'
|
127
|
-
else # :md (default)
|
128
|
-
base_classes << 'px-4 py-2 text-sm'
|
129
|
-
end
|
130
|
-
|
131
|
-
# Disabilitato
|
132
|
-
if options[:disabled]
|
133
|
-
base_classes << 'opacity-50 cursor-not-allowed'
|
134
|
-
end
|
135
|
-
|
136
|
-
# Larghezza piena
|
137
|
-
if options[:full_width]
|
138
|
-
base_classes << 'w-full'
|
139
|
-
end
|
140
|
-
|
141
|
-
when :panel
|
142
|
-
base_classes << LAYOUT_STYLES[:panel][:base]
|
143
|
-
|
144
|
-
# Colore di sfondo in base alla variante
|
145
|
-
if variant.to_sym == :default
|
146
|
-
base_classes << 'bg-white'
|
147
|
-
base_classes << 'border-gray-200'
|
148
|
-
else
|
149
|
-
base_classes << THEME_COLORS.dig(variant.to_sym, :light)&.sub('bg-', '') || "bg-#{variant}-50"
|
150
|
-
base_classes << THEME_BORDERS[variant.to_sym] || "border-#{variant}-200"
|
151
|
-
end
|
152
|
-
|
153
|
-
when :table
|
154
|
-
base_classes << 'min-w-full bg-white'
|
155
|
-
|
156
|
-
# Bordi
|
157
|
-
if options[:bordered]
|
158
|
-
base_classes << 'border-collapse'
|
159
|
-
base_classes << (THEME_BORDERS[variant.to_sym] || 'border-gray-200')
|
160
|
-
end
|
161
|
-
|
162
|
-
# Classi personalizzate
|
163
|
-
base_classes << options[:classes] if options[:classes].present?
|
164
|
-
end
|
165
|
-
|
166
|
-
base_classes.compact.join(' ')
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module BetterUi
|
2
|
-
class DocsController < ApplicationController
|
3
|
-
def index
|
4
|
-
# Pagina principale della documentazione
|
5
|
-
end
|
6
|
-
|
7
|
-
def show
|
8
|
-
# Verifica se esiste una vista con il nome richiesto
|
9
|
-
page = params[:page]
|
10
|
-
valid_pages = %w[introduction installation customization components/buttons components/icons components/panel components/table]
|
11
|
-
|
12
|
-
if valid_pages.include?(page)
|
13
|
-
if page.start_with?('components/')
|
14
|
-
# Per i componenti, rendiamo esplicitamente il template nella directory corretta
|
15
|
-
component_name = page.split('/').last
|
16
|
-
render template: "better_ui/docs/components/#{component_name}"
|
17
|
-
else
|
18
|
-
render page
|
19
|
-
end
|
20
|
-
else
|
21
|
-
# Pagina non trovata
|
22
|
-
render plain: "Pagina non trovata", status: :not_found
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def component
|
27
|
-
# Gestisce le rotte tipo /better_ui/docs/components/button
|
28
|
-
component_name = params[:component]
|
29
|
-
render template: "better_ui/docs/components/#{component_name}"
|
30
|
-
rescue ActionView::MissingTemplate
|
31
|
-
render plain: "Componente non trovato", status: :not_found
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,99 +0,0 @@
|
|
1
|
-
module BetterUi
|
2
|
-
module ApplicationHelper
|
3
|
-
# Render markdown
|
4
|
-
def better_ui_markdown(text)
|
5
|
-
return '' if text.blank?
|
6
|
-
|
7
|
-
renderer = Redcarpet::Render::HTML.new(
|
8
|
-
filter_html: false,
|
9
|
-
hard_wrap: true,
|
10
|
-
link_attributes: { target: '_blank', rel: 'noopener noreferrer' }
|
11
|
-
)
|
12
|
-
|
13
|
-
markdown = Redcarpet::Markdown.new(
|
14
|
-
renderer,
|
15
|
-
autolink: true,
|
16
|
-
tables: true,
|
17
|
-
fenced_code_blocks: true,
|
18
|
-
strikethrough: true,
|
19
|
-
superscript: true,
|
20
|
-
underline: true,
|
21
|
-
highlight: true,
|
22
|
-
quote: true,
|
23
|
-
footnotes: true
|
24
|
-
)
|
25
|
-
|
26
|
-
rendered_html = markdown.render(text).html_safe
|
27
|
-
|
28
|
-
# Applica classi Tailwind agli elementi HTML generati dal markdown
|
29
|
-
rendered_html
|
30
|
-
.gsub(/<h1>/i, '<h1 class="text-3xl font-bold mb-6 pb-2 border-b border-gray-200">')
|
31
|
-
.gsub(/<h2>/i, '<h2 class="text-2xl font-semibold mt-8 mb-4 pb-2 border-b border-gray-100">')
|
32
|
-
.gsub(/<h3>/i, '<h3 class="text-xl font-medium mt-6 mb-3">')
|
33
|
-
.gsub(/<pre>/i, '<pre class="bg-gray-100 p-4 rounded-md overflow-x-auto my-4">')
|
34
|
-
.gsub(/<code>/i, '<code class="font-mono">')
|
35
|
-
.gsub(/<ul>/i, '<ul class="ml-6 mb-4">')
|
36
|
-
.gsub(/<ol>/i, '<ol class="ml-6 mb-4">')
|
37
|
-
.gsub(/<p>/i, '<p class="mb-4 leading-relaxed">')
|
38
|
-
.gsub(/<a /i, '<a class="text-blue-600 no-underline hover:underline" ')
|
39
|
-
.gsub(/<blockquote>/i, '<blockquote class="border-l-4 border-gray-200 pl-4 ml-0 text-gray-600">')
|
40
|
-
.html_safe
|
41
|
-
end
|
42
|
-
|
43
|
-
# Determina se il path corrente corrisponde a quello dato
|
44
|
-
def current_page?(path)
|
45
|
-
request.path == path
|
46
|
-
end
|
47
|
-
|
48
|
-
# Helper per i componenti General
|
49
|
-
def better_ui_general_button(label = nil, **options)
|
50
|
-
render BetterUi::General::ButtonComponent.new(label: label, **options)
|
51
|
-
end
|
52
|
-
|
53
|
-
def better_ui_general_panel(**options, &block)
|
54
|
-
render BetterUi::General::PanelComponent.new(**options), &block
|
55
|
-
end
|
56
|
-
|
57
|
-
def better_ui_general_table(**options)
|
58
|
-
render BetterUi::General::TableComponent.new(**options)
|
59
|
-
end
|
60
|
-
|
61
|
-
def better_ui_general_icon(name, **options)
|
62
|
-
render BetterUi::General::IconComponent.new(name: name, **options)
|
63
|
-
end
|
64
|
-
|
65
|
-
# Helper per i componenti Application
|
66
|
-
def better_ui_application_button(label = nil, **options)
|
67
|
-
render BetterUi::Application::ButtonComponent.new(label: label, **options)
|
68
|
-
end
|
69
|
-
|
70
|
-
def better_ui_application_panel(**options, &block)
|
71
|
-
render BetterUi::Application::PanelComponent.new(**options), &block
|
72
|
-
end
|
73
|
-
|
74
|
-
def better_ui_application_table(**options)
|
75
|
-
render BetterUi::Application::TableComponent.new(**options)
|
76
|
-
end
|
77
|
-
|
78
|
-
def better_ui_application_icon(name, **options)
|
79
|
-
render BetterUi::Application::IconComponent.new(name: name, **options)
|
80
|
-
end
|
81
|
-
|
82
|
-
# Helper per i componenti Website
|
83
|
-
def better_ui_website_button(label = nil, **options)
|
84
|
-
render BetterUi::Website::ButtonComponent.new(label: label, **options)
|
85
|
-
end
|
86
|
-
|
87
|
-
def better_ui_website_panel(**options, &block)
|
88
|
-
render BetterUi::Website::PanelComponent.new(**options), &block
|
89
|
-
end
|
90
|
-
|
91
|
-
def better_ui_website_table(**options)
|
92
|
-
render BetterUi::Website::TableComponent.new(**options)
|
93
|
-
end
|
94
|
-
|
95
|
-
def better_ui_website_icon(name, **options)
|
96
|
-
render BetterUi::Website::IconComponent.new(name: name, **options)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|