add(web): basic layout and ui

This commit is contained in:
maotovisk 2025-08-07 11:15:56 -03:00
parent fd14d22fed
commit 80df48284c
17 changed files with 9288 additions and 4 deletions

View file

@ -0,0 +1,91 @@
package modules
import (
"maot-shortner/web/templates/components/button"
"maot-shortner/web/templates/components/icon"
)
type ThemeSwitcherProps struct {
Class string
}
templ ThemeSwitcher(props ...ThemeSwitcherProps) {
{{ var p ThemeSwitcherProps }}
if len(props) > 0 {
{{ p = props[0] }}
}
<script nonce={ templ.GetNonce(ctx) }>
(function() {
// Get current theme preference (system, light, or dark)
function getThemePreference() {
return localStorage.getItem('themePreference') || 'system';
}
// Apply theme based on preference
function applyTheme() {
const preference = getThemePreference();
let isDark = false;
if (preference === 'system') {
// Use system preference
isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
} else {
// Use explicit preference
isDark = preference === 'dark';
}
document.documentElement.classList.toggle('dark', isDark);
// Dispatch event for compatibility
document.dispatchEvent(new CustomEvent('theme-changed'));
}
// Toggle between light and dark (system only on initial state)
function cycleTheme() {
const current = getThemePreference();
let next;
if (current === 'system') {
// First click from system state - determine based on current appearance
const isDarkNow = window.matchMedia('(prefers-color-scheme: dark)').matches;
next = isDarkNow ? 'light' : 'dark';
} else {
// Toggle between light and dark
next = current === 'light' ? 'dark' : 'light';
}
localStorage.setItem('themePreference', next);
applyTheme();
}
// Initialize theme
applyTheme();
// Listen for system theme changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
if (getThemePreference() === 'system') {
applyTheme();
}
});
// Use event delegation for click handling
document.addEventListener('click', (e) => {
const themeSwitcher = e.target.closest('[data-theme-switcher]');
if (themeSwitcher) {
e.preventDefault();
cycleTheme();
}
});
})();
</script>
@button.Button(button.Props{
Size: button.SizeIcon,
Variant: button.VariantGhost,
Class: p.Class,
Attributes: templ.Attributes{
"data-theme-switcher": "true",
},
}) {
@icon.Eclipse(icon.Props{Size: 20})
}
}